diff options
1008 files changed, 18959 insertions, 8102 deletions
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java index bc8fc53cc448..2de6f369796d 100644 --- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java +++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java @@ -147,28 +147,4 @@ public class TypefaceSerializationPerfTest { out.clear(); } } - - @ManualBenchmarkState.ManualBenchmarkTest( - warmupDurationNs = WARMUP_DURATION_NS, - targetTestDurationNs = TARGET_TEST_DURATION_NS) - @Test - public void testSetSystemFontMap() throws Exception { - SharedMemory memory = null; - ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState(); - - long elapsedTime = 0; - while (state.keepRunning(elapsedTime)) { - // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit - // (max_map_count). - Typeface.destroySystemFontMap(); - Typeface.loadPreinstalledSystemFontMap(); - if (memory != null) { - memory.close(); - } - memory = Typeface.serializeFontMap(Typeface.getSystemFontMap()); - long startTime = System.nanoTime(); - Typeface.setSystemFontMap(memory); - elapsedTime = System.nanoTime() - startTime; - } - } } diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index 9a0053f8add6..c37ff9761757 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -483,6 +483,22 @@ java_library { } java_library { + name: "android_test_frameworks_core_stubs_current.from-source", + static_libs: [ + "all-updatable-modules-system-stubs", + "android-non-updatable.stubs.test", + "private-stub-annotations-jar", + ], + defaults: [ + "android.jar_defaults", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/test-core", + }, +} + +java_library { name: "android_module_lib_stubs_current.from-source", defaults: [ "android.jar_defaults", @@ -572,6 +588,170 @@ java_genrule { }, } +// +// Java API defaults and libraries for single tree build +// + +java_defaults { + name: "stub-annotation-defaults", + libs: [ + "stub-annotations", + ], + static_libs: [ + // stub annotations do not contribute to the API surfaces but are statically + // linked in the stubs for API surfaces (see frameworks/base/StubLibraries.bp). + // This is because annotation processors insist on loading the classes for any + // annotations found, thus should exist inside android.jar. + "private-stub-annotations-jar", + ], +} + +// Listing of API domains contribution and dependencies per API surfaces +java_defaults { + name: "android_test_stubs_current_contributions", + api_surface: "test", + api_contributions: [ + "test-api-stubs-docs-non-updatable.api.contribution", + "framework-virtualization.stubs.source.test.api.contribution", + ], +} + +java_defaults { + name: "android_test_frameworks_core_stubs_current_contributions", + api_surface: "test", + api_contributions: [ + "test-api-stubs-docs-non-updatable.api.contribution", + ], +} + +java_defaults { + name: "android_module_lib_stubs_current_contributions", + api_surface: "module-lib", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "module-lib-api-stubs-docs-non-updatable.api.contribution", + "art.module.public.api.stubs.source.api.contribution", + "art.module.public.api.stubs.source.system.api.contribution", + "art.module.public.api.stubs.source.module_lib.api.contribution", + "i18n.module.public.api.stubs.source.api.contribution", + "i18n.module.public.api.stubs.source.system.api.contribution", + "i18n.module.public.api.stubs.source.module_lib.api.contribution", + ], +} + +// Java API library definitions per API surface +java_api_library { + name: "android_stubs_current.from-text", + api_surface: "public", + defaults: [ + // This module is dynamically created at frameworks/base/api/api.go + // instead of being written out, in order to minimize edits in the codebase + // when there is a change in the list of modules. + // that contributes to an api surface. + "android_stubs_current_contributions", + "stub-annotation-defaults", + ], + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + ], + visibility: ["//visibility:public"], +} + +java_api_library { + name: "android_system_stubs_current.from-text", + api_surface: "system", + defaults: [ + "android_stubs_current_contributions", + "android_system_stubs_current_contributions", + "stub-annotation-defaults", + ], + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + ], + visibility: ["//visibility:public"], +} + +java_api_library { + name: "android_test_stubs_current.from-text", + api_surface: "test", + defaults: [ + "android_stubs_current_contributions", + "android_system_stubs_current_contributions", + "android_test_stubs_current_contributions", + "stub-annotation-defaults", + ], + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + ], + visibility: ["//visibility:public"], +} + +java_api_library { + name: "android_test_frameworks_core_stubs_current.from-text", + api_surface: "test", + defaults: [ + "android_stubs_current_contributions", + "android_system_stubs_current_contributions", + "android_test_frameworks_core_stubs_current_contributions", + "stub-annotation-defaults", + ], + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + ], +} + +java_api_library { + name: "android_module_lib_stubs_current_full.from-text", + api_surface: "module-lib", + defaults: [ + "android_stubs_current_contributions", + "android_system_stubs_current_contributions", + "android_module_lib_stubs_current_contributions_full", + ], + libs: [ + "stub-annotations", + ], + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "module-lib-api-stubs-docs-non-updatable.api.contribution", + ], + visibility: ["//visibility:public"], +} + +java_api_library { + name: "android_module_lib_stubs_current.from-text", + api_surface: "module-lib", + defaults: [ + "android_module_lib_stubs_current_contributions", + ], + libs: [ + "android_module_lib_stubs_current_full.from-text", + "stub-annotations", + ], + visibility: ["//visibility:public"], +} + +java_api_library { + name: "android_system_server_stubs_current.from-text", + api_surface: "system-server", + api_contributions: [ + "services-non-updatable-stubs.api.contribution", + ], + libs: [ + "android_module_lib_stubs_current.from-text", + "stub-annotations", + ], + static_libs: [ + "android_module_lib_stubs_current.from-text", + ], + visibility: ["//visibility:public"], +} + //////////////////////////////////////////////////////////////////////// // api-versions.xml generation, for public and system. This API database // also contains the android.test.* APIs. diff --git a/api/api.go b/api/api.go index c568a45de5d0..d5c6145ba062 100644 --- a/api/api.go +++ b/api/api.go @@ -110,6 +110,7 @@ type defaultsProps struct { Api_surface *string Api_contributions []string Defaults_visibility []string + Previous_api *string } type Bazel_module struct { @@ -145,7 +146,7 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { metalavaCmd := "$(location metalava)" // Silence reflection warnings. See b/168689341 metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " - metalavaCmd += " --quiet --no-banner --format=v2 " + metalavaCmd += " --quiet merge-signatures --format=v2 " filename := txt.TxtFilename if txt.Scope != "public" { @@ -155,7 +156,7 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) props.Tools = []string{"metalava"} props.Out = []string{filename} - props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)") + props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)") props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...) props.Dists = []android.Dist{ { @@ -359,6 +360,7 @@ func createApiContributionDefaults(ctx android.LoadHookContext, modules []string props.Api_contributions = transformArray( modules, "", fmt.Sprintf(".stubs.source%s.api.contribution", apiSuffix)) props.Defaults_visibility = []string{"//visibility:public"} + props.Previous_api = proptools.StringPtr(":android.api.public.latest") ctx.CreateModule(java.DefaultsFactory, &props) } } @@ -368,6 +370,7 @@ func createFullApiLibraries(ctx android.LoadHookContext) { "android_stubs_current", "android_system_stubs_current", "android_test_stubs_current", + "android_test_frameworks_core_stubs_current", "android_module_lib_stubs_current", "android_system_server_stubs_current", } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 10ce3b8d8161..774ba7460c6b 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1565,6 +1565,7 @@ bool BootAnimation::playAnimation(const Animation& animation) { for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); + glBindTexture(GL_TEXTURE_2D, 0); // Handle animation package if (part.animation != nullptr) { @@ -1641,8 +1642,10 @@ bool BootAnimation::playAnimation(const Animation& animation) { if (r > 0) { glBindTexture(GL_TEXTURE_2D, frame.tid); } else { - glGenTextures(1, &frame.tid); - glBindTexture(GL_TEXTURE_2D, frame.tid); + if (part.count != 1) { + glGenTextures(1, &frame.tid); + glBindTexture(GL_TEXTURE_2D, frame.tid); + } int w, h; // Set decoding option to alpha unpremultiplied so that the R, G, B channels // of transparent pixels are preserved. diff --git a/core/api/current.txt b/core/api/current.txt index 363e9d4a29bb..a3d8978caa21 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -15651,6 +15651,7 @@ package android.graphics { public final class Gainmap implements android.os.Parcelable { ctor public Gainmap(@NonNull android.graphics.Bitmap); + ctor public Gainmap(@NonNull android.graphics.Gainmap, @NonNull android.graphics.Bitmap); method public int describeContents(); method @NonNull public float getDisplayRatioForFullHdr(); method @NonNull public float[] getEpsilonHdr(); @@ -47271,9 +47272,9 @@ package android.text { } public class BoringLayout extends android.text.Layout implements android.text.TextUtils.EllipsizeCallback { - ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean); - ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int); - ctor public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean); + ctor @Deprecated public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean); + ctor @Deprecated public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int); + ctor @Deprecated public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean); method public void ellipsized(int, int); method public int getBottomPadding(); method public int getEllipsisCount(int); @@ -47286,12 +47287,12 @@ package android.text { method public int getLineTop(int); method public int getParagraphDirection(int); method public int getTopPadding(); - method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint); - method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics); - method @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics); - method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean); - method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int); - method @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean); + method @Deprecated public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint); + method @Deprecated public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics); + method @Deprecated @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics); + method @Deprecated public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean); + method @Deprecated public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int); + method @Deprecated @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean); method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean); method @NonNull public android.text.BoringLayout replaceOrMake(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean); method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int); @@ -47316,7 +47317,6 @@ package android.text { method public int getBottomPadding(); method public int getEllipsisCount(int); method public int getEllipsisStart(int); - method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig(); method public boolean getLineContainsTab(int); method public int getLineCount(); method public int getLineDescent(int); @@ -47489,20 +47489,26 @@ package android.text { method public void drawBackground(@NonNull android.graphics.Canvas); method public void drawText(@NonNull android.graphics.Canvas); method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int); - method public final android.text.Layout.Alignment getAlignment(); + method @NonNull public final android.text.Layout.Alignment getAlignment(); method public abstract int getBottomPadding(); + method public final int getBreakStrategy(); method public void getCursorPath(int, android.graphics.Path, CharSequence); method public static float getDesiredWidth(CharSequence, android.text.TextPaint); method public static float getDesiredWidth(CharSequence, int, int, android.text.TextPaint); method public abstract int getEllipsisCount(int); method public abstract int getEllipsisStart(int); - method public int getEllipsizedWidth(); + method @Nullable public final android.text.TextUtils.TruncateAt getEllipsize(); + method @IntRange(from=0) public int getEllipsizedWidth(); method public int getHeight(); + method public final int getHyphenationFrequency(); + method public final int getJustificationMode(); + method @Nullable public final int[] getLeftIndents(); method public final int getLineAscent(int); method public final int getLineBaseline(int); method public final int getLineBottom(int); method public int getLineBottom(int, boolean); method public int getLineBounds(int, android.graphics.Rect); + method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig(); method public abstract boolean getLineContainsTab(int); method public abstract int getLineCount(); method public abstract int getLineDescent(int); @@ -47513,29 +47519,35 @@ package android.text { method public float getLineLeft(int); method public float getLineMax(int); method public float getLineRight(int); + method public final float getLineSpacingAmount(); + method public final float getLineSpacingMultiplier(); method public abstract int getLineStart(int); method public abstract int getLineTop(int); method public int getLineVisibleEnd(int); method public float getLineWidth(int); + method @IntRange(from=1) public final int getMaxLines(); method public int getOffsetForHorizontal(int, float); method public int getOffsetToLeftOf(int); method public int getOffsetToRightOf(int); - method public final android.text.TextPaint getPaint(); + method @NonNull public final android.text.TextPaint getPaint(); method public final android.text.Layout.Alignment getParagraphAlignment(int); method public abstract int getParagraphDirection(int); method public final int getParagraphLeft(int); method public final int getParagraphRight(int); method public float getPrimaryHorizontal(int); method @Nullable public int[] getRangeForRect(@NonNull android.graphics.RectF, @NonNull android.text.SegmentFinder, @NonNull android.text.Layout.TextInclusionStrategy); + method @Nullable public final int[] getRightIndents(); method public float getSecondaryHorizontal(int); method public void getSelectionPath(int, int, android.graphics.Path); method public final float getSpacingAdd(); method public final float getSpacingMultiplier(); - method public final CharSequence getText(); + method @NonNull public final CharSequence getText(); + method @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic(); method public abstract int getTopPadding(); - method public final int getWidth(); + method @IntRange(from=0) public final int getWidth(); method public final void increaseWidthTo(int); method public boolean isFallbackLineSpacingEnabled(); + method public final boolean isIncludeFontPadding(); method public boolean isRtlCharAt(int); method protected final boolean isSpanned(); field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2 @@ -47563,6 +47575,26 @@ package android.text { enum_constant public static final android.text.Layout.Alignment ALIGN_OPPOSITE; } + public static final class Layout.Builder { + ctor public Layout.Builder(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextPaint, @IntRange(from=0) int); + method @NonNull public android.text.Layout build(); + method @NonNull public android.text.Layout.Builder setAlignment(@NonNull android.text.Layout.Alignment); + method @NonNull public android.text.Layout.Builder setBreakStrategy(int); + method @NonNull public android.text.Layout.Builder setEllipsize(@Nullable android.text.TextUtils.TruncateAt); + method @NonNull public android.text.Layout.Builder setEllipsizedWidth(@IntRange(from=0) int); + method @NonNull public android.text.Layout.Builder setFallbackLineSpacingEnabled(boolean); + method @NonNull public android.text.Layout.Builder setHyphenationFrequency(int); + method @NonNull public android.text.Layout.Builder setIncludeFontPadding(boolean); + method @NonNull public android.text.Layout.Builder setJustificationMode(int); + method @NonNull public android.text.Layout.Builder setLeftIndents(@Nullable int[]); + method @NonNull public android.text.Layout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig); + method @NonNull public android.text.Layout.Builder setLineSpacingAmount(float); + method @NonNull public android.text.Layout.Builder setLineSpacingMultiplier(float); + method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int); + method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]); + method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic); + } + public static class Layout.Directions { } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d48031505d5f..61415839ea2c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10389,6 +10389,7 @@ package android.os { field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1 field public static final int BUGREPORT_MODE_FULL = 0; // 0x0 field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1 + field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7 field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2 field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4 field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 3429c7c0b6fc..e61c39ff2525 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2358,6 +2358,7 @@ package android.os { public final class PowerManager { method public boolean areAutoPowerSaveModesEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean); + method public boolean isBatterySaverSupported(); field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED"; field @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public static final int SYSTEM_WAKELOCK = -2147483648; // 0x80000000 } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 9121cf09c982..02558602acd3 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -179,6 +179,10 @@ public class ApplicationPackageManager extends PackageManager { @GuardedBy("mDelegates") private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>(); + @NonNull + @GuardedBy("mPackageMonitorCallbacks") + private final ArraySet<IRemoteCallback> mPackageMonitorCallbacks = new ArraySet<>(); + UserManager getUserManager() { if (mUserManager == null) { mUserManager = UserManager.get(mContext); @@ -2872,16 +2876,25 @@ public class ApplicationPackageManager extends PackageManager { final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage) ? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build() : null; - return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, dialogInfo); + return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, + dialogInfo, 0); } @Override public String[] setPackagesSuspended(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo) { + return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, + dialogInfo, 0); + } + + @Override + public String[] setPackagesSuspended(String[] packageNames, boolean suspended, + PersistableBundle appExtras, PersistableBundle launcherExtras, + SuspendDialogInfo dialogInfo, int flags) { try { return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras, - launcherExtras, dialogInfo, mContext.getOpPackageName(), + launcherExtras, dialogInfo, flags, mContext.getOpPackageName(), getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -3926,6 +3939,14 @@ public class ApplicationPackageManager extends PackageManager { Objects.requireNonNull(callback); try { mPM.registerPackageMonitorCallback(callback, userId); + synchronized (mPackageMonitorCallbacks) { + if (mPackageMonitorCallbacks.contains(callback)) { + throw new IllegalStateException( + "registerPackageMonitorCallback: callback already registered: " + + callback); + } + mPackageMonitorCallbacks.add(callback); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3936,6 +3957,9 @@ public class ApplicationPackageManager extends PackageManager { Objects.requireNonNull(callback); try { mPM.unregisterPackageMonitorCallback(callback); + synchronized (mPackageMonitorCallbacks) { + mPackageMonitorCallbacks.remove(callback); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 4851279eea97..d0d76a4c8285 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -454,12 +454,11 @@ public class Dialog implements DialogInterface, Window.Callback, */ protected void onStart() { if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); - if (mContext != null + if (allowsRegisterDefaultOnBackInvokedCallback() && mContext != null && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { // Add onBackPressed as default back behavior. mDefaultBackCallback = this::onBackPressed; getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback); - mDefaultBackCallback = null; } } @@ -470,9 +469,18 @@ public class Dialog implements DialogInterface, Window.Callback, if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); if (mDefaultBackCallback != null) { getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback); + mDefaultBackCallback = null; } } + /** + * Whether this dialog allows to register the default onBackInvokedCallback. + * @hide + */ + protected boolean allowsRegisterDefaultOnBackInvokedCallback() { + return true; + } + private static final String DIALOG_SHOWING_TAG = "android:dialogShowing"; private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy"; @@ -697,7 +705,8 @@ public class Dialog implements DialogInterface, Window.Callback, if (event.isTracking() && !event.isCanceled()) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: - if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { + if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext) + || !allowsRegisterDefaultOnBackInvokedCallback()) { onBackPressed(); return true; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 892b45ed2292..6cad578b6d7e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1628,6 +1628,14 @@ public class Notification implements Parcelable */ public static final int GROUP_ALERT_CHILDREN = 2; + /** + * Constant for the {@link Builder#setGroup(String) group key} that is added to notifications + * that are not already grouped when {@link Builder#setSilent()} is used. + * + * @hide + */ + public static final String GROUP_KEY_SILENT = "silent"; + private int mGroupAlertBehavior = GROUP_ALERT_ALL; /** @@ -4290,6 +4298,35 @@ public class Notification implements Parcelable } /** + * If {@code true}, silences this instance of the notification, regardless of the sounds or + * vibrations set on the notification or notification channel. If {@code false}, then the + * normal sound and vibration logic applies. + * + * @hide + */ + public @NonNull Builder setSilent(boolean silent) { + if (!silent) { + return this; + } + if (mN.isGroupSummary()) { + setGroupAlertBehavior(GROUP_ALERT_CHILDREN); + } else { + setGroupAlertBehavior(GROUP_ALERT_SUMMARY); + } + + setVibrate(null); + setSound(null); + mN.defaults &= ~DEFAULT_SOUND; + mN.defaults &= ~DEFAULT_VIBRATE; + setDefaults(mN.defaults); + + if (TextUtils.isEmpty(mN.mGroupKey)) { + setGroup(GROUP_KEY_SILENT); + } + return this; + } + + /** * Set the first line of text in the platform notification template. */ @NonNull @@ -12819,7 +12856,6 @@ public class Notification implements Parcelable } else { mBackgroundColor = rawColor; } - mProtectionColor = COLOR_INVALID; // filled in at the end mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( ContrastColorUtil.resolvePrimaryColor(ctx, mBackgroundColor, nightMode), mBackgroundColor, 4.5); @@ -12836,7 +12872,6 @@ public class Notification implements Parcelable } else { int[] attrs = { R.attr.colorSurface, - R.attr.colorBackgroundFloating, R.attr.textColorPrimary, R.attr.textColorSecondary, R.attr.colorAccent, @@ -12848,15 +12883,14 @@ public class Notification implements Parcelable }; try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) { mBackgroundColor = getColor(ta, 0, nightMode ? Color.BLACK : Color.WHITE); - mProtectionColor = getColor(ta, 1, COLOR_INVALID); - mPrimaryTextColor = getColor(ta, 2, COLOR_INVALID); - mSecondaryTextColor = getColor(ta, 3, COLOR_INVALID); - mPrimaryAccentColor = getColor(ta, 4, COLOR_INVALID); - mSecondaryAccentColor = getColor(ta, 5, COLOR_INVALID); - mTertiaryAccentColor = getColor(ta, 6, COLOR_INVALID); - mOnAccentTextColor = getColor(ta, 7, COLOR_INVALID); - mErrorColor = getColor(ta, 8, COLOR_INVALID); - mRippleAlpha = Color.alpha(getColor(ta, 9, 0x33ffffff)); + mPrimaryTextColor = getColor(ta, 1, COLOR_INVALID); + mSecondaryTextColor = getColor(ta, 2, COLOR_INVALID); + mPrimaryAccentColor = getColor(ta, 3, COLOR_INVALID); + mSecondaryAccentColor = getColor(ta, 4, COLOR_INVALID); + mTertiaryAccentColor = getColor(ta, 5, COLOR_INVALID); + mOnAccentTextColor = getColor(ta, 6, COLOR_INVALID); + mErrorColor = getColor(ta, 7, COLOR_INVALID); + mRippleAlpha = Color.alpha(getColor(ta, 8, 0x33ffffff)); } mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor, mBackgroundColor, nightMode); @@ -12889,9 +12923,7 @@ public class Notification implements Parcelable } } // make sure every color has a valid value - if (mProtectionColor == COLOR_INVALID) { - mProtectionColor = ColorUtils.blendARGB(mPrimaryTextColor, mBackgroundColor, 0.8f); - } + mProtectionColor = ColorUtils.blendARGB(mPrimaryTextColor, mBackgroundColor, 0.9f); } /** calculates the contrast color for the non-colorized notifications */ diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 2b5175ca6659..634089b73618 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -248,6 +248,13 @@ public class TaskInfo { public boolean topActivityEligibleForUserAspectRatioButton; /** + * Whether the user has forced the activity to be fullscreen through the user aspect ratio + * settings. + * @hide + */ + public boolean isUserFullscreenOverrideEnabled; + + /** * Hint about the letterbox state of the top activity. * @hide */ @@ -543,7 +550,8 @@ public class TaskInfo { && isSleeping == that.isSleeping && Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId) && parentTaskId == that.parentTaskId - && Objects.equals(topActivity, that.topActivity); + && Objects.equals(topActivity, that.topActivity) + && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled; } /** @@ -574,7 +582,8 @@ public class TaskInfo { && (!hasCompatUI() || configuration.getLayoutDirection() == that.configuration.getLayoutDirection()) && (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode) - && (!hasCompatUI() || isVisible == that.isVisible); + && (!hasCompatUI() || isVisible == that.isVisible) + && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled; } /** @@ -630,6 +639,7 @@ public class TaskInfo { topActivityLetterboxHorizontalPosition = source.readInt(); topActivityLetterboxWidth = source.readInt(); topActivityLetterboxHeight = source.readInt(); + isUserFullscreenOverrideEnabled = source.readBoolean(); } /** @@ -686,6 +696,7 @@ public class TaskInfo { dest.writeInt(topActivityLetterboxHorizontalPosition); dest.writeInt(topActivityLetterboxWidth); dest.writeInt(topActivityLetterboxHeight); + dest.writeBoolean(isUserFullscreenOverrideEnabled); } @Override @@ -732,6 +743,7 @@ public class TaskInfo { + topActivityLetterboxHorizontalPosition + " topActivityLetterboxWidth=" + topActivityLetterboxWidth + " topActivityLetterboxHeight=" + topActivityLetterboxHeight + + " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl index ee7836f0f7f0..ed8484fe7266 100644 --- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl +++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl @@ -21,6 +21,7 @@ import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.IVirtualDeviceSoundEffectListener; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceParams; +import android.content.AttributionSource; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; @@ -46,7 +47,7 @@ interface IVirtualDeviceManager { */ @EnforcePermission("CREATE_VIRTUAL_DEVICE") IVirtualDevice createVirtualDevice( - in IBinder token, String packageName, int associationId, + in IBinder token, in AttributionSource attributionSource, int associationId, in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener, in IVirtualDeviceSoundEffectListener soundEffectListener); diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java index f68cfff1c053..d13bfd4f6229 100644 --- a/core/java/android/companion/virtual/VirtualDeviceInternal.java +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -145,7 +145,7 @@ public class VirtualDeviceInternal { mContext = context.getApplicationContext(); mVirtualDevice = service.createVirtualDevice( new Binder(), - mContext.getPackageName(), + mContext.getAttributionSource(), associationId, params, mActivityListenerBinder, diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 45d6dc62bfe8..b6d837589284 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -43,6 +43,7 @@ import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseIntArray; +import java.io.PrintWriter; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -501,6 +502,26 @@ public final class VirtualDeviceParams implements Parcelable { + ")"; } + /** + * Dumps debugging information about the VirtualDeviceParams + * @hide + */ + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "mName=" + mName); + pw.println(prefix + "mLockState=" + mLockState); + pw.println(prefix + "mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts); + pw.println(prefix + "mAllowedCrossTaskNavigations=" + mAllowedCrossTaskNavigations); + pw.println(prefix + "mBlockedCrossTaskNavigations=" + mBlockedCrossTaskNavigations); + pw.println(prefix + "mAllowedActivities=" + mAllowedActivities); + pw.println(prefix + "mBlockedActivities=" + mBlockedActivities); + pw.println(prefix + "mDevicePolicies=" + mDevicePolicies); + pw.println(prefix + "mDefaultNavigationPolicy=" + mDefaultNavigationPolicy); + pw.println(prefix + "mDefaultActivityPolicy=" + mDefaultActivityPolicy); + pw.println(prefix + "mVirtualSensorConfigs=" + mVirtualSensorConfigs); + pw.println(prefix + "mAudioPlaybackSessionId=" + mAudioPlaybackSessionId); + pw.println(prefix + "mAudioRecordingSessionId=" + mAudioRecordingSessionId); + } + @NonNull public static final Parcelable.Creator<VirtualDeviceParams> CREATOR = new Parcelable.Creator<VirtualDeviceParams>() { diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java index 3bdf9aa8015b..0dbe411a400e 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java @@ -104,6 +104,11 @@ public final class VirtualSensorConfig implements Parcelable { parcel.writeInt(mFlags); } + @Override + public String toString() { + return "VirtualSensorConfig{" + "mType=" + mType + ", mName='" + mName + '\'' + '}'; + } + /** * Returns the type of the sensor. * diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c11a8fc7c78d..5ef7b117d86e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -323,6 +323,7 @@ public abstract class Context { // Make sure no flag uses the sign bit (most significant bit) of the long integer, // to avoid future confusion. BIND_BYPASS_USER_NETWORK_RESTRICTIONS, + BIND_FILTER_OUT_QUARANTINED_COMPONENTS, }) @Retention(RetentionPolicy.SOURCE) public @interface BindServiceFlagsLongBits {} @@ -697,6 +698,13 @@ public abstract class Context { */ public static final long BIND_BYPASS_USER_NETWORK_RESTRICTIONS = 0x1_0000_0000L; + /** + * Flag for {@link #bindService}. + * + * @hide + */ + public static final long BIND_FILTER_OUT_QUARANTINED_COMPONENTS = 0x2_0000_0000L; + /** * These bind flags reduce the strength of the binding such that we shouldn't diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index fe108a513ec5..afeb3d295a7c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -6383,6 +6383,15 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.changed_uid_list"; /** + * This field is part of + * {@link android.content.Intent#ACTION_PACKAGES_SUSPENDED}, + * and only present if the packages were quarantined. + * @hide + */ + public static final String EXTRA_QUARANTINED = + "android.intent.extra.quarantined"; + + /** * An integer denoting a bitwise combination of restrictions set on distracting packages via * {@link PackageManager#setDistractingPackageRestrictions(String[], int)} * diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index c3df17d4b53e..529363f828bb 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -344,15 +344,22 @@ public class CrossProfileApps { // If there is a label for the launcher intent, then use that as it is typically shorter. // Otherwise, just use the top-level application name. Intent launchIntent = pm.getLaunchIntentForPackage(mContext.getPackageName()); + if (launchIntent == null) { + return getDefaultCallingApplicationLabel(); + } List<ResolveInfo> infos = pm.queryIntentActivities( launchIntent, PackageManager.ResolveInfoFlags.of(MATCH_DEFAULT_ONLY)); if (infos.size() > 0) { return infos.get(0).loadLabel(pm); } + return getDefaultCallingApplicationLabel(); + } + + private CharSequence getDefaultCallingApplicationLabel() { return mContext.getApplicationInfo() .loadSafeLabel( - pm, + mContext.getPackageManager(), /* ellipsizeDip= */ 0, TextUtils.SAFE_STRING_FLAG_SINGLE_LINE | TextUtils.SAFE_STRING_FLAG_TRIM); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 7c9ccbad10a2..ea0f5ff2896e 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -298,12 +298,14 @@ interface IPackageManager { String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, in PersistableBundle appExtras, in PersistableBundle launcherExtras, - in SuspendDialogInfo dialogInfo, String callingPackage, int userId); + in SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId); String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId); boolean isPackageSuspendedForUser(String packageName, int userId); + boolean isPackageQuarantinedForUser(String packageName, int userId); + Bundle getSuspendedPackageAppExtras(String packageName, int userId); /** diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 9f14c9731c76..885e67e1a8c8 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -834,6 +834,7 @@ public abstract class PackageManager { GET_DISABLED_COMPONENTS, GET_DISABLED_UNTIL_USED_COMPONENTS, GET_UNINSTALLED_PACKAGES, + FILTER_OUT_QUARANTINED_COMPONENTS, }) @Retention(RetentionPolicy.SOURCE) public @interface ComponentInfoFlagsBits {} @@ -857,7 +858,8 @@ public abstract class PackageManager { GET_DISABLED_COMPONENTS, GET_DISABLED_UNTIL_USED_COMPONENTS, GET_UNINSTALLED_PACKAGES, - MATCH_CLONE_PROFILE + MATCH_CLONE_PROFILE, + FILTER_OUT_QUARANTINED_COMPONENTS, }) @Retention(RetentionPolicy.SOURCE) public @interface ResolveInfoFlagsBits {} @@ -1233,6 +1235,11 @@ public abstract class PackageManager { public static final long GET_ATTRIBUTIONS_LONG = 0x80000000L; /** + * @hide + */ + public static final long FILTER_OUT_QUARANTINED_COMPONENTS = 0x100000000L; + + /** * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when * resolving an intent that matches the {@code CrossProfileIntentFilter}, * the current profile will be skipped. Only activities in the target user @@ -1685,7 +1692,7 @@ public abstract class PackageManager { /** @hide */ @IntDef(flag = true, value = { DONT_KILL_APP, - SYNCHRONOUS + SYNCHRONOUS, }) @Retention(RetentionPolicy.SOURCE) public @interface EnabledFlags {} @@ -1708,6 +1715,24 @@ public abstract class PackageManager { public static final int SYNCHRONOUS = 0x00000002; /** @hide */ + @IntDef(flag = true, value = { + FLAG_SUSPEND_QUARANTINED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SuspendedFlags {} + + /** + * Flag parameter {@link #setPackagesSuspended(String[], boolean, PersistableBundle, + * PersistableBundle, android.content.pm.SuspendDialogInfo, int)}: + * Apps in this state not only appear suspended for all user visible purposes (eg, Launcher, + * ShareSheet), but also individual components of the app can behave as disabled depending on + * the importance of the calling app. + * + * @hide + */ + public static final int FLAG_SUSPEND_QUARANTINED = 0x00000001; + + /** @hide */ @IntDef(prefix = { "INSTALL_REASON_" }, value = { INSTALL_REASON_UNKNOWN, INSTALL_REASON_POLICY, @@ -9654,6 +9679,63 @@ public abstract class PackageManager { } /** + * Puts the given packages in a suspended state, where attempts at starting activities are + * denied. + * + * <p>The suspended application's notifications and all of its windows will be hidden, any + * of its started activities will be stopped and it won't be able to ring the device. + * It doesn't remove the data or the actual package file. + * + * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app + * is suspended will be shown instead. + * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object + * to this API. This dialog will have a button that starts the + * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an + * activity which handles this action. + * + * <p>The packages being suspended must already be installed. If a package is uninstalled, it + * will no longer be suspended. + * + * <p>Optionally, the suspending app can provide extra information in the form of + * {@link PersistableBundle} objects to be shared with the apps being suspended and the + * launcher to support customization that they might need to handle the suspended state. + * + * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API except for + * device owner and profile owner. + * + * @param packageNames The names of the packages to set the suspended status. + * @param suspended If set to {@code true}, the packages will be suspended, if set to + * {@code false}, the packages will be unsuspended. + * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide + * which will be shared with the apps being suspended. Ignored if + * {@code suspended} is false. + * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can + * provide which will be shared with the launcher. Ignored if + * {@code suspended} is false. + * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that + * should be shown to the user when they try to launch a suspended app. + * Ignored if {@code suspended} is false. + * @param flags Optional behavior flags. + * + * @return an array of package names for which the suspended status could not be set as + * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}. + * + * @see #isPackageSuspended + * @see SuspendDialogInfo + * @see SuspendDialogInfo.Builder + * @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS + * + * @hide + */ + @RequiresPermission(value=Manifest.permission.SUSPEND_APPS, conditional=true) + @Nullable + public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended, + @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras, + @Nullable SuspendDialogInfo dialogInfo, @SuspendedFlags int flags) { + throw new UnsupportedOperationException("setPackagesSuspended not implemented"); + } + + /** * Returns any packages in a given set of packages that cannot be suspended via a call to {@link * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 048289f56a0c..9387ae1d6ac8 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -8005,7 +8005,7 @@ public class PackageParser { ai.enabled = true; } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { - ai.enabled = (flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0; + ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0; } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED || state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java index c2a0062b43e8..eedb25b1aa8f 100644 --- a/core/java/android/credentials/CredentialManager.java +++ b/core/java/android/credentials/CredentialManager.java @@ -123,7 +123,7 @@ public final class CredentialManager { * credential, display a picker when multiple credentials exist, etc. * Callers (e.g. browsers) may optionally set origin in {@link GetCredentialRequest} for an * app different from their own, to be able to get credentials on behalf of that app. They would - * need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN} + * need additional permission {@code CREDENTIAL_MANAGER_SET_ORIGIN} * to use this functionality * * @param context the context used to launch any UI needed; use an activity context to make sure @@ -209,9 +209,9 @@ public final class CredentialManager { * * <p>This API doesn't invoke any UI. It only performs the preparation work so that you can * later launch the remaining get-credential operation (involves UIs) through the {@link - * #getCredential(PrepareGetCredentialResponse.PendingGetCredentialHandle, Context, + * #getCredential(Context, PrepareGetCredentialResponse.PendingGetCredentialHandle, * CancellationSignal, Executor, OutcomeReceiver)} API which incurs less latency compared to - * the {@link #getCredential(GetCredentialRequest, Context, CancellationSignal, Executor, + * the {@link #getCredential(Context, GetCredentialRequest, CancellationSignal, Executor, * OutcomeReceiver)} API that executes the whole operation in one call. * * @param request the request specifying type(s) of credentials to get from the user @@ -261,7 +261,7 @@ public final class CredentialManager { * storing the new credential, etc. * Callers (e.g. browsers) may optionally set origin in {@link CreateCredentialRequest} for an * app different from their own, to be able to get credentials on behalf of that app. They would - * need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN} + * need additional permission {@code CREDENTIAL_MANAGER_SET_ORIGIN} * to use this functionality * * @param context the context used to launch any UI needed; use an activity context to make sure diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 912e8df6bdc7..af448f0c4917 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -466,6 +466,19 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java) /** + * Set if emergency call button should show, for example if biometrics are + * required to access the dialer app + * @param showEmergencyCallButton if true, shows emergency call button + * @return This builder. + * @hide + */ + @NonNull + public Builder setShowEmergencyCallButton(boolean showEmergencyCallButton) { + mPromptInfo.setShowEmergencyCallButton(showEmergencyCallButton); + return this; + } + + /** * Creates a {@link BiometricPrompt}. * * @return An instance of {@link BiometricPrompt}. diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index e27507874167..24cfd1641410 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -48,6 +48,7 @@ public class PromptInfo implements Parcelable { private boolean mAllowBackgroundAuthentication; private boolean mIgnoreEnrollmentState; private boolean mIsForLegacyFingerprintManager = false; + private boolean mShowEmergencyCallButton = false; public PromptInfo() { @@ -72,6 +73,7 @@ public class PromptInfo implements Parcelable { mAllowBackgroundAuthentication = in.readBoolean(); mIgnoreEnrollmentState = in.readBoolean(); mIsForLegacyFingerprintManager = in.readBoolean(); + mShowEmergencyCallButton = in.readBoolean(); } public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() { @@ -111,6 +113,7 @@ public class PromptInfo implements Parcelable { dest.writeBoolean(mAllowBackgroundAuthentication); dest.writeBoolean(mIgnoreEnrollmentState); dest.writeBoolean(mIsForLegacyFingerprintManager); + dest.writeBoolean(mShowEmergencyCallButton); } // LINT.IfChange @@ -228,6 +231,10 @@ public class PromptInfo implements Parcelable { mAllowedSensorIds.add(sensorId); } + public void setShowEmergencyCallButton(boolean showEmergencyCallButton) { + mShowEmergencyCallButton = showEmergencyCallButton; + } + // Getters public CharSequence getTitle() { @@ -309,4 +316,8 @@ public class PromptInfo implements Parcelable { public boolean isForLegacyFingerprintManager() { return mIsForLegacyFingerprintManager; } + + public boolean isShowEmergencyCallButton() { + return mShowEmergencyCallButton; + } } diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index 6baf91d720c3..ea951a55bfca 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -236,9 +236,10 @@ public final class CameraExtensionCharacteristics { private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER = new CameraExtensionManagerGlobal(); private final Object mLock = new Object(); - private final int PROXY_SERVICE_DELAY_MS = 1000; + private final int PROXY_SERVICE_DELAY_MS = 2000; private InitializerFuture mInitFuture = null; private ServiceConnection mConnection = null; + private int mConnectionCount = 0; private ICameraExtensionsProxyService mProxy = null; private boolean mSupportsAdvancedExtensions = false; @@ -249,6 +250,15 @@ public final class CameraExtensionCharacteristics { return GLOBAL_CAMERA_MANAGER; } + private void releaseProxyConnectionLocked(Context ctx) { + if (mConnection != null ) { + ctx.unbindService(mConnection); + mConnection = null; + mProxy = null; + mConnectionCount = 0; + } + } + private void connectToProxyLocked(Context ctx) { if (mConnection == null) { Intent intent = new Intent(); @@ -270,7 +280,6 @@ public final class CameraExtensionCharacteristics { mConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName component) { - mInitFuture.setStatus(false); mConnection = null; mProxy = null; } @@ -348,23 +357,32 @@ public final class CameraExtensionCharacteristics { public boolean registerClient(Context ctx, IBinder token) { synchronized (mLock) { + boolean ret = false; connectToProxyLocked(ctx); if (mProxy == null) { return false; } + mConnectionCount++; try { - return mProxy.registerClient(token); + ret = mProxy.registerClient(token); } catch (RemoteException e) { Log.e(TAG, "Failed to initialize extension! Extension service does " + " not respond!"); } + if (!ret) { + mConnectionCount--; + } - return false; + if (mConnectionCount <= 0) { + releaseProxyConnectionLocked(ctx); + } + + return ret; } } - public void unregisterClient(IBinder token) { + public void unregisterClient(Context ctx, IBinder token) { synchronized (mLock) { if (mProxy != null) { try { @@ -372,6 +390,11 @@ public final class CameraExtensionCharacteristics { } catch (RemoteException e) { Log.e(TAG, "Failed to de-initialize extension! Extension service does" + " not respond!"); + } finally { + mConnectionCount--; + if (mConnectionCount <= 0) { + releaseProxyConnectionLocked(ctx); + } } } } @@ -446,8 +469,8 @@ public final class CameraExtensionCharacteristics { /** * @hide */ - public static void unregisterClient(IBinder token) { - CameraExtensionManagerGlobal.get().unregisterClient(token); + public static void unregisterClient(Context ctx, IBinder token) { + CameraExtensionManagerGlobal.get().unregisterClient(ctx, token); } /** @@ -578,7 +601,7 @@ public final class CameraExtensionCharacteristics { } } } finally { - unregisterClient(token); + unregisterClient(mContext, token); } return Collections.unmodifiableList(ret); @@ -626,7 +649,7 @@ public final class CameraExtensionCharacteristics { Log.e(TAG, "Failed to query the extension for postview availability! Extension " + "service does not respond!"); } finally { - unregisterClient(token); + unregisterClient(mContext, token); } return false; @@ -722,7 +745,7 @@ public final class CameraExtensionCharacteristics { + "service does not respond!"); return Collections.emptyList(); } finally { - unregisterClient(token); + unregisterClient(mContext, token); } } @@ -791,7 +814,7 @@ public final class CameraExtensionCharacteristics { + " not respond!"); return new ArrayList<>(); } finally { - unregisterClient(token); + unregisterClient(mContext, token); } } @@ -872,7 +895,7 @@ public final class CameraExtensionCharacteristics { } } } finally { - unregisterClient(token); + unregisterClient(mContext, token); } } catch (RemoteException e) { Log.e(TAG, "Failed to query the extension supported sizes! Extension service does" @@ -957,7 +980,7 @@ public final class CameraExtensionCharacteristics { Log.e(TAG, "Failed to query the extension capture latency! Extension service does" + " not respond!"); } finally { - unregisterClient(token); + unregisterClient(mContext, token); } return null; @@ -998,7 +1021,7 @@ public final class CameraExtensionCharacteristics { Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does" + " not respond!"); } finally { - unregisterClient(token); + unregisterClient(mContext, token); } return false; @@ -1075,7 +1098,7 @@ public final class CameraExtensionCharacteristics { } catch (RemoteException e) { throw new IllegalStateException("Failed to query the available capture request keys!"); } finally { - unregisterClient(token); + unregisterClient(mContext, token); } return Collections.unmodifiableSet(ret); @@ -1155,7 +1178,7 @@ public final class CameraExtensionCharacteristics { } catch (RemoteException e) { throw new IllegalStateException("Failed to query the available capture result keys!"); } finally { - unregisterClient(token); + unregisterClient(mContext, token); } return Collections.unmodifiableSet(ret); diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index e06699bbbd86..c7e74c077f0d 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -90,7 +90,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>(); private RequestProcessor mRequestProcessor = new RequestProcessor(); private final int mSessionId; - private final IBinder mToken; + private IBinder mToken = null; private Surface mClientRepeatingRequestSurface; private Surface mClientCaptureSurface; @@ -103,6 +103,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private boolean mInitialized; private boolean mSessionClosed; + private final Context mContext; + // Lock to synchronize cross-thread access to device public interface final Object mInterfaceLock; @@ -113,14 +115,9 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession( @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice, @NonNull Map<String, CameraCharacteristics> characteristicsMap, - @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId) + @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId, + @NonNull IBinder token) throws CameraAccessException, RemoteException { - final IBinder token = new Binder(TAG + " : " + sessionId); - boolean success = CameraExtensionCharacteristics.registerClient(ctx, token); - if (!success) { - throw new UnsupportedOperationException("Unsupported extension!"); - } - String cameraId = cameraDevice.getId(); CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx, cameraId, characteristicsMap); @@ -204,8 +201,9 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension( config.getExtension()); extender.init(cameraId, characteristicsMapNative); - CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(extender, - cameraDevice, characteristicsMapNative, repeatingRequestSurface, + + CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(ctx, + extender, cameraDevice, characteristicsMapNative, repeatingRequestSurface, burstCaptureSurface, postviewSurface, config.getStateCallback(), config.getExecutor(), sessionId, token); @@ -217,13 +215,16 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes return ret; } - private CameraAdvancedExtensionSessionImpl(@NonNull IAdvancedExtenderImpl extender, + private CameraAdvancedExtensionSessionImpl(Context ctx, + @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDeviceImpl cameraDevice, Map<String, CameraMetadataNative> characteristicsMap, @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface, @Nullable Surface postviewSurface, @NonNull StateCallback callback, @NonNull Executor executor, - int sessionId, @NonNull IBinder token) { + int sessionId, + @NonNull IBinder token) { + mContext = ctx; mAdvancedExtender = extender; mCameraDevice = cameraDevice; mCharacteristicsMap = characteristicsMap; @@ -578,12 +579,16 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mSessionProcessor = null; } - CameraExtensionCharacteristics.unregisterClient(mToken); - if (mInitialized || (mCaptureSession != null)) { - notifyClose = true; - CameraExtensionCharacteristics.releaseSession(); + + if (mToken != null) { + if (mInitialized || (mCaptureSession != null)) { + notifyClose = true; + CameraExtensionCharacteristics.releaseSession(); + } + CameraExtensionCharacteristics.unregisterClient(mContext, mToken); } mInitialized = false; + mToken = null; for (ImageReader reader : mReaderMap.values()) { reader.close(); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index d3bde4b4b8a8..181ab2cf3421 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -2550,19 +2550,32 @@ public class CameraDeviceImpl extends CameraDevice HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>( mPhysicalIdsToChars); characteristicsMap.put(mCameraId, mCharacteristics); + boolean initializationFailed = true; + IBinder token = new Binder(TAG + " : " + mNextSessionId++); try { + boolean ret = CameraExtensionCharacteristics.registerClient(mContext, token); + if (!ret) { + token = null; + throw new UnsupportedOperationException("Unsupported extension!"); + } + if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) { mCurrentAdvancedExtensionSession = CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession( this, characteristicsMap, mContext, extensionConfiguration, - mNextSessionId++); + mNextSessionId, token); } else { mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession( this, characteristicsMap, mContext, extensionConfiguration, - mNextSessionId++); + mNextSessionId, token); } + initializationFailed = false; } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_ERROR); + } finally { + if (initializationFailed && (token != null)) { + CameraExtensionCharacteristics.unregisterClient(mContext, token); + } } } } diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 5d256813ffb6..bf77681bbbbd 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -91,7 +91,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private final Set<CaptureRequest.Key> mSupportedRequestKeys; private final Set<CaptureResult.Key> mSupportedResultKeys; private final ExtensionSessionStatsAggregator mStatsAggregator; - private final IBinder mToken; + private IBinder mToken = null; private boolean mCaptureResultsSupported; private CameraCaptureSession mCaptureSession = null; @@ -119,6 +119,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { // will do so internally. private boolean mInternalRepeatingRequestEnabled = true; + private final Context mContext; + // Lock to synchronize cross-thread access to device public interface final Object mInterfaceLock; @@ -135,14 +137,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { @NonNull Map<String, CameraCharacteristics> characteristicsMap, @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, - int sessionId) + int sessionId, + @NonNull IBinder token) throws CameraAccessException, RemoteException { - final IBinder token = new Binder(TAG + " : " + sessionId); - boolean success = CameraExtensionCharacteristics.registerClient(ctx, token); - if (!success) { - throw new UnsupportedOperationException("Unsupported extension!"); - } - String cameraId = cameraDevice.getId(); CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx, cameraId, characteristicsMap); @@ -234,6 +231,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { characteristicsMap.get(cameraId).getNativeMetadata()); CameraExtensionSessionImpl session = new CameraExtensionSessionImpl( + ctx, extenders.second, extenders.first, supportedPreviewSizes, @@ -256,7 +254,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { return session; } - public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender, + public CameraExtensionSessionImpl(Context ctx, @NonNull IImageCaptureExtenderImpl imageExtender, @NonNull IPreviewExtenderImpl previewExtender, @NonNull List<Size> previewSizes, @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice, @@ -269,6 +267,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { @NonNull IBinder token, @NonNull Set<CaptureRequest.Key> requestKeys, @Nullable Set<CaptureResult.Key> resultKeys) { + mContext = ctx; mImageExtender = imageExtender; mPreviewExtender = previewExtender; mCameraDevice = cameraDevice; @@ -878,12 +877,15 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { + " respond!"); } - CameraExtensionCharacteristics.unregisterClient(mToken); - if (mInitialized || (mCaptureSession != null)) { - notifyClose = true; - CameraExtensionCharacteristics.releaseSession(); + if (mToken != null) { + if (mInitialized || (mCaptureSession != null)) { + notifyClose = true; + CameraExtensionCharacteristics.releaseSession(); + } + CameraExtensionCharacteristics.unregisterClient(mContext, mToken); } mInitialized = false; + mToken = null; if (mRepeatingRequestImageCallback != null) { mRepeatingRequestImageCallback.close(); diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 21540bf1a18b..5e5337337864 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -1193,12 +1193,12 @@ public final class OutputConfiguration implements Parcelable { String physicalCameraId = source.readString(); boolean isMultiResolutionOutput = source.readInt() == 1; int[] sensorPixelModesUsed = source.createIntArray(); - long streamUseCase = source.readLong(); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); long dynamicRangeProfile = source.readLong(); DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile); int colorSpace = source.readInt(); + long streamUseCase = source.readLong(); int timestampBase = source.readInt(); int mirrorMode = source.readInt(); diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 94bff893b5a8..4700720736b5 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -370,8 +370,9 @@ public abstract class DisplayManagerInternal { /** * Returns the default size of the surface associated with the display, or null if the surface - * is not provided for layer mirroring by SurfaceFlinger. - * Only used for mirroring started from MediaProjection. + * is not provided for layer mirroring by SurfaceFlinger. Size is rotated to reflect the current + * display device orientation. + * Used for mirroring from MediaProjection, or a physical display based on display flags. */ public abstract Point getDisplaySurfaceDefaultSize(int displayId); diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java index 763246e25a26..dc66542f4f37 100644 --- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java +++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java @@ -21,6 +21,7 @@ import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.biometrics.AuthenticateOptions; +import android.hardware.biometrics.common.AuthenticateReason; import android.os.Parcelable; import com.android.internal.util.DataClass; @@ -85,7 +86,16 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions return null; } - + /** + * The Vendor extension, if any. + * + * This option may be present when a vendor would like to send additional information for each + * auth attempt. + */ + @Nullable private AuthenticateReason.Vendor mVendorReason; + private static AuthenticateReason.Vendor defaultVendorReason() { + return null; + } // Code below generated by codegen v1.0.23. // @@ -107,7 +117,8 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions boolean ignoreEnrollmentState, @AuthenticateOptions.DisplayState int displayState, @NonNull String opPackageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, + @Nullable AuthenticateReason.Vendor vendorReason) { this.mUserId = userId; this.mSensorId = sensorId; this.mIgnoreEnrollmentState = ignoreEnrollmentState; @@ -118,6 +129,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mOpPackageName); this.mAttributionTag = attributionTag; + this.mVendorReason = vendorReason; // onConstructed(); // You can define this method to get a callback } @@ -176,6 +188,17 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions } /** + * The Vendor extension, if any. + * + * This option may be present when a vendor would like to send additional information for each + * auth attempt. + */ + @DataClass.Generated.Member + public @Nullable AuthenticateReason.Vendor getVendorReason() { + return mVendorReason; + } + + /** * The sensor id for this operation. */ @DataClass.Generated.Member @@ -209,6 +232,18 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions return this; } + /** + * The Vendor extension, if any. + * + * This option may be present when a vendor would like to send additional information for each + * auth attempt. + */ + @DataClass.Generated.Member + public @NonNull FingerprintAuthenticateOptions setVendorReason(@NonNull AuthenticateReason.Vendor value) { + mVendorReason = value; + return this; + } + @Override @DataClass.Generated.Member public boolean equals(@Nullable Object o) { @@ -227,7 +262,8 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions && mIgnoreEnrollmentState == that.mIgnoreEnrollmentState && mDisplayState == that.mDisplayState && java.util.Objects.equals(mOpPackageName, that.mOpPackageName) - && java.util.Objects.equals(mAttributionTag, that.mAttributionTag); + && java.util.Objects.equals(mAttributionTag, that.mAttributionTag) + && java.util.Objects.equals(mVendorReason, that.mVendorReason); } @Override @@ -243,6 +279,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions _hash = 31 * _hash + mDisplayState; _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName); _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); + _hash = 31 * _hash + java.util.Objects.hashCode(mVendorReason); return _hash; } @@ -255,12 +292,14 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions byte flg = 0; if (mIgnoreEnrollmentState) flg |= 0x4; if (mAttributionTag != null) flg |= 0x20; + if (mVendorReason != null) flg |= 0x40; dest.writeByte(flg); dest.writeInt(mUserId); dest.writeInt(mSensorId); dest.writeInt(mDisplayState); dest.writeString(mOpPackageName); if (mAttributionTag != null) dest.writeString(mAttributionTag); + if (mVendorReason != null) dest.writeTypedObject(mVendorReason, flags); } @Override @@ -281,6 +320,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions int displayState = in.readInt(); String opPackageName = in.readString(); String attributionTag = (flg & 0x20) == 0 ? null : in.readString(); + AuthenticateReason.Vendor vendorReason = (flg & 0x40) == 0 ? null : (AuthenticateReason.Vendor) in.readTypedObject(AuthenticateReason.Vendor.CREATOR); this.mUserId = userId; this.mSensorId = sensorId; @@ -292,6 +332,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mOpPackageName); this.mAttributionTag = attributionTag; + this.mVendorReason = vendorReason; // onConstructed(); // You can define this method to get a callback } @@ -323,6 +364,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions private @AuthenticateOptions.DisplayState int mDisplayState; private @NonNull String mOpPackageName; private @Nullable String mAttributionTag; + private @Nullable AuthenticateReason.Vendor mVendorReason; private long mBuilderFieldsSet = 0L; @@ -400,10 +442,24 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions return this; } + /** + * The Vendor extension, if any. + * + * This option may be present when a vendor would like to send additional information for each + * auth attempt. + */ + @DataClass.Generated.Member + public @NonNull Builder setVendorReason(@NonNull AuthenticateReason.Vendor value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mVendorReason = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull FingerprintAuthenticateOptions build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mUserId = defaultUserId(); @@ -423,18 +479,22 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions if ((mBuilderFieldsSet & 0x20) == 0) { mAttributionTag = defaultAttributionTag(); } + if ((mBuilderFieldsSet & 0x40) == 0) { + mVendorReason = defaultVendorReason(); + } FingerprintAuthenticateOptions o = new FingerprintAuthenticateOptions( mUserId, mSensorId, mIgnoreEnrollmentState, mDisplayState, mOpPackageName, - mAttributionTag); + mAttributionTag, + mVendorReason); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -442,10 +502,10 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions } @DataClass.Generated( - time = 1677119626721L, + time = 1689703591032L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java", - inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") + inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nprivate static android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 795eb4a737ef..8f653b3808c1 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -576,6 +576,12 @@ public class InputMethodService extends AbstractInputMethodService { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE = 148086656L; + /** + * Enable the logic to allow hiding the IME caption bar ("fake" IME navigation bar). + * @hide + */ + public static final boolean ENABLE_HIDE_IME_CAPTION_BAR = true; + LayoutInflater mInflater; TypedArray mThemeAttrs; @UnsupportedAppUsage diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 78388efe98c7..c01664e55744 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -16,6 +16,8 @@ package android.inputmethodservice; +import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR; +import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import android.animation.ValueAnimator; @@ -230,6 +232,16 @@ final class NavigationBarController { setIconTintInternal(calculateTargetDarkIntensity(mAppearance, mDrawLegacyNavigationBarBackground)); + + if (ENABLE_HIDE_IME_CAPTION_BAR) { + mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> { + if (mNavigationBarFrame != null) { + boolean visible = insets.isVisible(captionBar()); + mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + } + return view.onApplyWindowInsets(insets); + }); + } } private void uninstallNavigationBarFrameIfNecessary() { @@ -240,6 +252,9 @@ final class NavigationBarController { if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(mNavigationBarFrame); } + if (ENABLE_HIDE_IME_CAPTION_BAR) { + mNavigationBarFrame.setOnApplyWindowInsetsListener(null); + } mNavigationBarFrame = null; } @@ -414,7 +429,9 @@ final class NavigationBarController { decor.bringChildToFront(mNavigationBarFrame); } } - mNavigationBarFrame.setVisibility(View.VISIBLE); + if (!ENABLE_HIDE_IME_CAPTION_BAR) { + mNavigationBarFrame.setVisibility(View.VISIBLE); + } } } @@ -435,6 +452,11 @@ final class NavigationBarController { mShouldShowImeSwitcherWhenImeIsShown; mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown; + if (ENABLE_HIDE_IME_CAPTION_BAR) { + mService.mWindow.getWindow().getDecorView().getWindowInsetsController() + .setImeCaptionBarInsetsHeight(getImeCaptionBarHeight()); + } + if (imeDrawsImeNavBar) { installNavigationBarFrameIfNecessary(); if (mNavigationBarFrame == null) { @@ -528,6 +550,16 @@ final class NavigationBarController { return drawLegacyNavigationBarBackground; } + /** + * Returns the height of the IME caption bar if this should be shown, or {@code 0} instead. + */ + private int getImeCaptionBarHeight() { + return mImeDrawsImeNavBar + ? mService.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_frame_height) + : 0; + } + @Override public String toDebugString() { return "{mImeDrawsImeNavBar=" + mImeDrawsImeNavBar diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 5704dac7a327..e4a09a651ae1 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -79,6 +79,13 @@ final class SoftInputWindow extends Dialog { @WindowState private int mWindowState = WindowState.TOKEN_PENDING; + @Override + protected boolean allowsRegisterDefaultOnBackInvokedCallback() { + // Do not register OnBackInvokedCallback from Dialog#onStart, InputMethodService will + // register CompatOnBackInvokedCallback for input method window. + return false; + } + /** * Set {@link IBinder} window token to the window. * diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 43219bc38e2d..42c56265bb4a 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1656,6 +1656,8 @@ public abstract class BatteryStats { public abstract CpuScalingPolicies getCpuScalingPolicies(); public final static class HistoryTag { + public static final int HISTORY_TAG_POOL_OVERFLOW = -1; + public String string; public int uid; @@ -6826,10 +6828,11 @@ public abstract class BatteryStats { if (bd.mask == HistoryItem.STATE_WAKE_LOCK_FLAG && wakelockTag != null) { didWake = true; sb.append("="); - if (longNames) { + if (longNames + || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { UserHandle.formatUid(sb, wakelockTag.uid); sb.append(":\""); - sb.append(wakelockTag.string); + sb.append(wakelockTag.string.replace("\"", "\"\"")); sb.append("\""); } else { sb.append(wakelockTag.poolIdx); @@ -6849,7 +6852,7 @@ public abstract class BatteryStats { } if (!didWake && wakelockTag != null) { sb.append(longNames ? " wake_lock=" : ",w="); - if (longNames) { + if (longNames || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { UserHandle.formatUid(sb, wakelockTag.uid); sb.append(":\""); sb.append(wakelockTag.string); @@ -7110,7 +7113,14 @@ public abstract class BatteryStats { if (rec.wakeReasonTag != null) { if (checkin) { item.append(",wr="); - item.append(rec.wakeReasonTag.poolIdx); + if (rec.wakeReasonTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + item.append(sUidToString.applyAsString(rec.wakeReasonTag.uid)); + item.append(":\""); + item.append(rec.wakeReasonTag.string.replace("\"", "\"\"")); + item.append("\""); + } else { + item.append(rec.wakeReasonTag.poolIdx); + } } else { item.append(" wake_reason="); item.append(rec.wakeReasonTag.uid); @@ -7138,7 +7148,15 @@ public abstract class BatteryStats { } item.append("="); if (checkin) { - item.append(rec.eventTag.poolIdx); + if (rec.eventTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + item.append(HISTORY_EVENT_INT_FORMATTERS[idx] + .applyAsString(rec.eventTag.uid)); + item.append(":\""); + item.append(rec.eventTag.string.replace("\"", "\"\"")); + item.append("\""); + } else { + item.append(rec.eventTag.poolIdx); + } } else { item.append(HISTORY_EVENT_INT_FORMATTERS[idx] .applyAsString(rec.eventTag.uid)); diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java index d9d14b038237..0456a33580cf 100644 --- a/core/java/android/os/BugreportParams.java +++ b/core/java/android/os/BugreportParams.java @@ -81,7 +81,8 @@ public final class BugreportParams { BUGREPORT_MODE_REMOTE, BUGREPORT_MODE_WEAR, BUGREPORT_MODE_TELEPHONY, - BUGREPORT_MODE_WIFI + BUGREPORT_MODE_WIFI, + BUGREPORT_MODE_ONBOARDING }) public @interface BugreportMode {} @@ -121,6 +122,11 @@ public final class BugreportParams { public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI; /** + * Options for a lightweight bugreport intended to be taken for onboarding-related flows. + */ + public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING; + + /** * Defines acceptable flags for customizing bugreport requests. * @hide */ diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index be7c1e5c0333..dbb6f92c6411 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -60,6 +60,7 @@ interface IPowerManager boolean isPowerSaveMode(); PowerSaveState getPowerSaveState(int serviceType); boolean setPowerSaveModeEnabled(boolean mode); + boolean isBatterySaverSupported(); BatterySaverPolicyConfig getFullPowerSavePolicy(); boolean setFullPowerSavePolicy(in BatterySaverPolicyConfig config); boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold); diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 6ef1dc0ec020..8f7725ecaba0 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -10,6 +10,7 @@ per-file PowerManagerInternal.java = michaelwr@google.com, santoscordon@google.c # BatteryStats per-file *BatteryConsumer* = file:/BATTERY_STATS_OWNERS per-file BatteryManager* = file:/BATTERY_STATS_OWNERS +per-file PowerMonitor* = file:/BATTERY_STATS_OWNERS per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS per-file *Stats* = file:/BATTERY_STATS_OWNERS diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index d676509d9317..4174c1c13c94 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1936,6 +1936,20 @@ public final class PowerManager { } /** + * Returns true if Battery Saver is supported on this device. + * + * @hide + */ + @TestApi + public boolean isBatterySaverSupported() { + try { + return mService.isBatterySaverSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Gets the current policy for full power save mode. * * @return The {@link BatterySaverPolicyConfig} which is currently set for the full power save diff --git a/core/java/android/os/PowerMonitor.java b/core/java/android/os/PowerMonitor.java index ebdd463d18f6..5fb0df7febc1 100644 --- a/core/java/android/os/PowerMonitor.java +++ b/core/java/android/os/PowerMonitor.java @@ -23,6 +23,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** + * A PowerMonitor represents either a Channel aka ODPM rail (on-device power monitor) or an + * EnergyConsumer, as defined in + * <a href="https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/main/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats">android.hardware.power.stats</a> + * * @hide */ public final class PowerMonitor implements Parcelable { @@ -92,6 +96,7 @@ public final class PowerMonitor implements Parcelable { return 0; } + @NonNull public static final Creator<PowerMonitor> CREATOR = new Creator<>() { @Override public PowerMonitor createFromParcel(@NonNull Parcel in) { diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java index 3d7f859a7ed5..e76705917c7a 100644 --- a/core/java/android/os/PowerMonitorReadings.java +++ b/core/java/android/os/PowerMonitorReadings.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import java.util.Arrays; @@ -43,8 +44,8 @@ public final class PowerMonitorReadings { * @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index * @hide */ - public PowerMonitorReadings(PowerMonitor[] powerMonitors, - long[] energyUws, long[] timestampsMs) { + public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors, + @NonNull long[] energyUws, @NonNull long[] timestampsMs) { mPowerMonitors = powerMonitors; mEnergyUws = energyUws; mTimestampsMs = timestampsMs; @@ -55,7 +56,7 @@ public final class PowerMonitorReadings { * Does not persist across reboots. * Represents total energy: both on-battery and plugged-in. */ - public long getConsumedEnergyUws(PowerMonitor powerMonitor) { + public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mEnergyUws[offset]; @@ -64,9 +65,10 @@ public final class PowerMonitorReadings { } /** - * Elapsed realtime when the snapshot was taken. + * Elapsed realtime, in milliseconds, when the snapshot was taken. */ - public long getTimestampMs(PowerMonitor powerMonitor) { + @ElapsedRealtimeLong + public long getTimestamp(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mTimestampsMs[offset]; diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index bf72b1d7a035..2cda787082c7 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -18,26 +18,19 @@ package android.os; import android.annotation.CallbackExecutor; import android.annotation.NonNull; -import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; -import android.hardware.vibrator.IVibrator; +import android.os.vibrator.VibratorInfoFactory; import android.util.ArrayMap; import android.util.Log; -import android.util.Range; -import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; -import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.Arrays; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.function.Function; /** * Vibrator implementation that controls the main system vibrator. @@ -82,7 +75,7 @@ public class SystemVibrator extends Vibrator { if (vibratorIds.length == 0) { // It is known that the device has no vibrator, so cache and return info that // reflects the lack of support for effects/primitives. - return mVibratorInfo = new NoVibratorInfo(); + return mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO; } VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length]; for (int i = 0; i < vibratorIds.length; i++) { @@ -96,12 +89,7 @@ public class SystemVibrator extends Vibrator { } vibratorInfos[i] = vibrator.getInfo(); } - if (vibratorInfos.length == 1) { - // Device has a single vibrator info, cache and return successfully loaded info. - return mVibratorInfo = new VibratorInfo(/* id= */ -1, vibratorInfos[0]); - } - // Device has multiple vibrators, generate a single info representing all of them. - return mVibratorInfo = new MultiVibratorInfo(vibratorInfos); + return mVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, vibratorInfos); } } @@ -275,296 +263,6 @@ public class SystemVibrator extends Vibrator { } /** - * Represents a device with no vibrator as a single {@link VibratorInfo}. - * - * @hide - */ - @VisibleForTesting - public static class NoVibratorInfo extends VibratorInfo { - public NoVibratorInfo() { - // Use empty arrays to indicate no support, while null would indicate support unknown. - super(/* id= */ -1, - /* capabilities= */ 0, - /* supportedEffects= */ new SparseBooleanArray(), - /* supportedBraking= */ new SparseBooleanArray(), - /* supportedPrimitives= */ new SparseIntArray(), - /* primitiveDelayMax= */ 0, - /* compositionSizeMax= */ 0, - /* pwlePrimitiveDurationMax= */ 0, - /* pwleSizeMax= */ 0, - /* qFactor= */ Float.NaN, - new FrequencyProfile(/* resonantFrequencyHz= */ Float.NaN, - /* minFrequencyHz= */ Float.NaN, - /* frequencyResolutionHz= */ Float.NaN, - /* maxAmplitudes= */ null)); - } - } - - /** - * Represents multiple vibrator information as a single {@link VibratorInfo}. - * - * <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive - * support. - * - * @hide - */ - @VisibleForTesting - public static class MultiVibratorInfo extends VibratorInfo { - // Epsilon used for float comparison applied in calculations for the merged info. - private static final float EPSILON = 1e-5f; - - public MultiVibratorInfo(VibratorInfo[] vibrators) { - // Need to use an extra constructor to share the computation in super initialization. - this(vibrators, frequencyProfileIntersection(vibrators)); - } - - private MultiVibratorInfo(VibratorInfo[] vibrators, - VibratorInfo.FrequencyProfile mergedProfile) { - super(/* id= */ -1, - capabilitiesIntersection(vibrators, mergedProfile.isEmpty()), - supportedEffectsIntersection(vibrators), - supportedBrakingIntersection(vibrators), - supportedPrimitivesAndDurationsIntersection(vibrators), - integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax), - integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax), - integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax), - integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax), - floatPropertyIntersection(vibrators, VibratorInfo::getQFactor), - mergedProfile); - } - - private static int capabilitiesIntersection(VibratorInfo[] infos, - boolean frequencyProfileIsEmpty) { - int intersection = ~0; - for (VibratorInfo info : infos) { - intersection &= info.getCapabilities(); - } - if (frequencyProfileIsEmpty) { - // Revoke frequency control if the merged frequency profile ended up empty. - intersection &= ~IVibrator.CAP_FREQUENCY_CONTROL; - } - return intersection; - } - - @Nullable - private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) { - for (VibratorInfo info : infos) { - if (!info.isBrakingSupportKnown()) { - // If one vibrator support is unknown, then the intersection is also unknown. - return null; - } - } - - SparseBooleanArray intersection = new SparseBooleanArray(); - SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking(); - - brakingIdLoop: - for (int i = 0; i < firstVibratorBraking.size(); i++) { - int brakingId = firstVibratorBraking.keyAt(i); - if (!firstVibratorBraking.valueAt(i)) { - // The first vibrator already doesn't support this braking, so skip it. - continue brakingIdLoop; - } - - for (int j = 1; j < infos.length; j++) { - if (!infos[j].hasBrakingSupport(brakingId)) { - // One vibrator doesn't support this braking, so the intersection doesn't. - continue brakingIdLoop; - } - } - - intersection.put(brakingId, true); - } - - return intersection; - } - - @Nullable - private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) { - for (VibratorInfo info : infos) { - if (!info.isEffectSupportKnown()) { - // If one vibrator support is unknown, then the intersection is also unknown. - return null; - } - } - - SparseBooleanArray intersection = new SparseBooleanArray(); - SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects(); - - effectIdLoop: - for (int i = 0; i < firstVibratorEffects.size(); i++) { - int effectId = firstVibratorEffects.keyAt(i); - if (!firstVibratorEffects.valueAt(i)) { - // The first vibrator already doesn't support this effect, so skip it. - continue effectIdLoop; - } - - for (int j = 1; j < infos.length; j++) { - if (infos[j].isEffectSupported(effectId) != VIBRATION_EFFECT_SUPPORT_YES) { - // One vibrator doesn't support this effect, so the intersection doesn't. - continue effectIdLoop; - } - } - - intersection.put(effectId, true); - } - - return intersection; - } - - @NonNull - private static SparseIntArray supportedPrimitivesAndDurationsIntersection( - VibratorInfo[] infos) { - SparseIntArray intersection = new SparseIntArray(); - SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives(); - - primitiveIdLoop: - for (int i = 0; i < firstVibratorPrimitives.size(); i++) { - int primitiveId = firstVibratorPrimitives.keyAt(i); - int primitiveDuration = firstVibratorPrimitives.valueAt(i); - if (primitiveDuration == 0) { - // The first vibrator already doesn't support this primitive, so skip it. - continue primitiveIdLoop; - } - - for (int j = 1; j < infos.length; j++) { - int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId); - if (vibratorPrimitiveDuration == 0) { - // One vibrator doesn't support this primitive, so the intersection doesn't. - continue primitiveIdLoop; - } else { - // The primitive vibration duration is the maximum among all vibrators. - primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration); - } - } - - intersection.put(primitiveId, primitiveDuration); - } - return intersection; - } - - private static int integerLimitIntersection(VibratorInfo[] infos, - Function<VibratorInfo, Integer> propertyGetter) { - int limit = 0; // Limit 0 means unlimited - for (VibratorInfo info : infos) { - int vibratorLimit = propertyGetter.apply(info); - if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) { - // This vibrator is limited and intersection is unlimited or has a larger limit: - // use smaller limit here for the intersection. - limit = vibratorLimit; - } - } - return limit; - } - - private static float floatPropertyIntersection(VibratorInfo[] infos, - Function<VibratorInfo, Float> propertyGetter) { - float property = propertyGetter.apply(infos[0]); - if (Float.isNaN(property)) { - // If one vibrator is undefined then the intersection is undefined. - return Float.NaN; - } - for (int i = 1; i < infos.length; i++) { - if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) { - // If one vibrator has a different value then the intersection is undefined. - return Float.NaN; - } - } - return property; - } - - @NonNull - private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) { - float freqResolution = floatPropertyIntersection(infos, - info -> info.getFrequencyProfile().getFrequencyResolutionHz()); - float resonantFreq = floatPropertyIntersection(infos, - VibratorInfo::getResonantFrequencyHz); - Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution); - - if ((freqRange == null) || Float.isNaN(freqResolution)) { - return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null); - } - - int amplitudeCount = - Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution); - float[] maxAmplitudes = new float[amplitudeCount]; - - // Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this - // will fail if the loop below is broken and do not replace filled values with actual - // vibrator measurements. - Arrays.fill(maxAmplitudes, Float.MAX_VALUE); - - for (VibratorInfo info : infos) { - Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz(); - float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes(); - int vibratorStartIdx = Math.round( - (freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution); - int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1; - - if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) { - Slog.w(TAG, "Error calculating the intersection of vibrator frequency" - + " profiles: attempted to fetch from vibrator " - + info.getId() + " max amplitude with bad index " + vibratorStartIdx); - return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null); - } - - for (int i = 0; i < maxAmplitudes.length; i++) { - maxAmplitudes[i] = Math.min(maxAmplitudes[i], - vibratorMaxAmplitudes[vibratorStartIdx + i]); - } - } - - return new FrequencyProfile(resonantFreq, freqRange.getLower(), - freqResolution, maxAmplitudes); - } - - @Nullable - private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos, - float frequencyResolution) { - Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz(); - if (firstRange == null) { - // If one vibrator is undefined then the intersection is undefined. - return null; - } - float intersectionLower = firstRange.getLower(); - float intersectionUpper = firstRange.getUpper(); - - // Generate the intersection of all vibrator supported ranges, making sure that both - // min supported frequencies are aligned w.r.t. the frequency resolution. - - for (int i = 1; i < infos.length; i++) { - Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz(); - if (vibratorRange == null) { - // If one vibrator is undefined then the intersection is undefined. - return null; - } - - if ((vibratorRange.getLower() >= intersectionUpper) - || (vibratorRange.getUpper() <= intersectionLower)) { - // If the range and intersection are disjoint then the intersection is undefined - return null; - } - - float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower()); - if ((frequencyDelta % frequencyResolution) > EPSILON) { - // If the intersection is not aligned with one vibrator then it's undefined - return null; - } - - intersectionLower = Math.max(intersectionLower, vibratorRange.getLower()); - intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper()); - } - - if ((intersectionUpper - intersectionLower) < frequencyResolution) { - // If the intersection is empty then it's undefined. - return null; - } - - return Range.create(intersectionLower, intersectionUpper); - } - } - - /** * Listener for all vibrators state change. * * <p>This registers a listener to all vibrators to merge the callbacks into a single state diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index ba1f979b4514..d8cf4ded79e8 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -4618,6 +4618,26 @@ public class UserManager { } /** + * Returns number of full users on the device. + * @hide + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + public int getFullUserCount() { + List<UserInfo> users = getUsers(/* excludePartial= */ true, /* excludeDying= */ true, + /* excludePreCreated= */ true); + int count = 0; + for (UserInfo user : users) { + if (user.isFull()) { + count++; + } + } + return count; + } + + /** * @deprecated use {@link #getAliveUsers()} for {@code getUsers(true)}, or * {@link #getUsers()} for @code getUsers(false)}. * diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index b24b45d11c3a..08b32bf2b9e0 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -525,14 +525,14 @@ public abstract class VibrationEffect implements Parcelable { public abstract long getDuration(); /** - * Checks if a given {@link Vibrator} can play this effect as intended. + * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended. * - * <p>See @link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information - * about what counts as supported by a vibrator, and what counts as not. + * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more + * information about what counts as supported by a vibrator, and what counts as not. * * @hide */ - public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator); + public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo); /** * Returns true if this effect could represent a touch haptic feedback. @@ -813,9 +813,9 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ @Override - public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { for (VibrationEffectSegment segment : mSegments) { - if (!segment.areVibrationFeaturesSupported(vibrator)) { + if (!segment.areVibrationFeaturesSupported(vibratorInfo)) { return false; } } diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 4e852e333ec8..79e0ca87eade 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -216,9 +216,7 @@ public abstract class Vibrator { */ @TestApi public boolean hasFrequencyControl() { - // We currently can only control frequency of the vibration using the compose PWLE method. - return getInfo().hasCapability( - IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); + return getInfo().hasFrequencyControl(); } /** @@ -240,7 +238,7 @@ public abstract class Vibrator { * @hide */ public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) { - return effect.areVibrationFeaturesSupported(this); + return getInfo().areVibrationFeaturesSupported(effect); } /** diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java index 02e685699398..4f8c24d1f905 100644 --- a/core/java/android/os/VibratorInfo.java +++ b/core/java/android/os/VibratorInfo.java @@ -156,6 +156,16 @@ public class VibratorInfo implements Parcelable { return false; } VibratorInfo that = (VibratorInfo) o; + return mId == that.mId && equalContent(that); + } + + /** + * Returns {@code true} only if the properties and capabilities of the provided info, except for + * the ID, equals to this info. Returns {@code false} otherwise. + * + * @hide + */ + public boolean equalContent(VibratorInfo that) { int supportedPrimitivesCount = mSupportedPrimitives.size(); if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) { return false; @@ -168,7 +178,7 @@ public class VibratorInfo implements Parcelable { return false; } } - return mId == that.mId && mCapabilities == that.mCapabilities + return mCapabilities == that.mCapabilities && mPrimitiveDelayMax == that.mPrimitiveDelayMax && mCompositionSizeMax == that.mCompositionSizeMax && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax @@ -242,6 +252,17 @@ public class VibratorInfo implements Parcelable { } /** + * Check whether the vibrator has frequency control. + * + * @return True if the hardware can control the frequency of the vibrations, otherwise false. + */ + public boolean hasFrequencyControl() { + // We currently can only control frequency of the vibration using the compose PWLE method. + return hasCapability( + IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); + } + + /** * Returns a default value to be applied to composed PWLE effects for braking. * * @return a supported braking value, one of android.hardware.vibrator.Braking.* @@ -323,6 +344,23 @@ public class VibratorInfo implements Parcelable { } /** + * Query whether or not the vibrator supports all components of a given {@link VibrationEffect} + * (i.e. the vibrator can play the given effect as intended). + * + * <p>See {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more + * information on how the vibrator support is determined. + * + * @param effect the {@link VibrationEffect} to check if it is supported + * @return {@code true} if the vibrator can play the given {@code effect} as intended, + * {@code false} otherwise. + * + * @hide + */ + public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) { + return effect.areVibrationFeaturesSupported(this); + } + + /** * Query the estimated duration of given primitive. * * @param primitiveId Which primitives to query for. @@ -417,7 +455,8 @@ public class VibratorInfo implements Parcelable { return mFrequencyProfile; } - protected long getCapabilities() { + /** Returns a single int representing all the capabilities of the vibrator. */ + public long getCapabilities() { return mCapabilities; } diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index ab30a8bf1ea1..dfc43f46c57d 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -24,6 +24,8 @@ import android.content.Context; import android.os.BatteryStats; import android.os.Build; import android.os.Bundle; +import android.os.ConditionVariable; +import android.os.Handler; import android.os.IPowerStatsService; import android.os.PowerMonitor; import android.os.PowerMonitorReadings; @@ -36,8 +38,8 @@ import com.android.internal.app.IBatteryStats; import java.util.Arrays; import java.util.Comparator; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; +import java.util.List; +import java.util.function.Consumer; /** * Provides access to data about how various system resources are used by applications. @@ -62,7 +64,8 @@ public class SystemHealthManager { private final IBatteryStats mBatteryStats; @Nullable private final IPowerStatsService mPowerStats; - private PowerMonitor[] mPowerMonitorsInfo; + private List<PowerMonitor> mPowerMonitorsInfo; + private final Object mPowerMonitorsLock = new Object(); /** * Construct a new SystemHealthManager object. @@ -161,53 +164,68 @@ public class SystemHealthManager { * @hide */ @NonNull - public PowerMonitor[] getSupportedPowerMonitors() { - synchronized (this) { + public List<PowerMonitor> getSupportedPowerMonitors() { + synchronized (mPowerMonitorsLock) { if (mPowerMonitorsInfo != null) { return mPowerMonitorsInfo; } + } + ConditionVariable lock = new ConditionVariable(); + // Populate mPowerMonitorsInfo by side-effect + getSupportedPowerMonitors(null, unused -> lock.open()); + lock.block(); - CompletableFuture<PowerMonitor[]> future = new CompletableFuture<>(); - getSupportedPowerMonitors(future); - try { - return future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } + synchronized (mPowerMonitorsLock) { + return mPowerMonitorsInfo; } } /** - * Retrieves a list of supported power monitors, see {@link #getSupportedPowerMonitors()} + * Asynchronously retrieves a list of supported power monitors, see + * {@link #getSupportedPowerMonitors()} + * + * @param handler optional Handler to deliver the callback. If not supplied, the callback + * may be invoked on an arbitrary thread. + * @param onResult callback for the result * * @hide */ - public void getSupportedPowerMonitors(@NonNull CompletableFuture<PowerMonitor[]> future) { - synchronized (this) { + public void getSupportedPowerMonitors(@Nullable Handler handler, + @NonNull Consumer<List<PowerMonitor>> onResult) { + final List<PowerMonitor> result; + synchronized (mPowerMonitorsLock) { if (mPowerMonitorsInfo != null) { - future.complete(mPowerMonitorsInfo); - return; + result = mPowerMonitorsInfo; + } else if (mPowerStats == null) { + mPowerMonitorsInfo = List.of(); + result = mPowerMonitorsInfo; + } else { + result = null; } - try { - if (mPowerStats == null) { - mPowerMonitorsInfo = new PowerMonitor[0]; - future.complete(mPowerMonitorsInfo); - return; - } - - mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - synchronized (this) { - mPowerMonitorsInfo = resultData.getParcelableArray( - IPowerStatsService.KEY_MONITORS, PowerMonitor.class); - } - future.complete(mPowerMonitorsInfo); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + } + if (result != null) { + if (handler != null) { + handler.post(() -> onResult.accept(result)); + } else { + onResult.accept(result); } + return; + } + try { + mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + PowerMonitor[] array = resultData.getParcelableArray( + IPowerStatsService.KEY_MONITORS, PowerMonitor.class); + List<PowerMonitor> result = array != null ? Arrays.asList(array) : List.of(); + synchronized (mPowerMonitorsLock) { + mPowerMonitorsInfo = result; + } + onResult.accept(result); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } @@ -215,54 +233,74 @@ public class SystemHealthManager { * Retrieves the accumulated power consumption reported by the specified power monitors. * * @param powerMonitors power monitors to be returned. + * * @hide */ @NonNull - public PowerMonitorReadings getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors) { - CompletableFuture<PowerMonitorReadings> future = new CompletableFuture<>(); - getPowerMonitorReadings(powerMonitors, future); - try { - return future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); + public PowerMonitorReadings getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors) { + PowerMonitorReadings[] outReadings = new PowerMonitorReadings[1]; + RuntimeException[] outException = new RuntimeException[1]; + ConditionVariable lock = new ConditionVariable(); + getPowerMonitorReadings(powerMonitors, null, + pms -> { + outReadings[0] = pms; + lock.open(); + }, + error -> { + outException[0] = error; + lock.open(); + } + ); + lock.block(); + if (outException[0] != null) { + throw outException[0]; } + return outReadings[0]; } private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR = Comparator.comparingInt(pm -> pm.index); /** + * Asynchronously retrieves the accumulated power consumption reported by the specified power + * monitors. + * * @param powerMonitors power monitors to be retrieved. + * @param handler optional Handler to deliver the callbacks. If not supplied, the callback + * may be invoked on an arbitrary thread. + * @param onSuccess callback for the result + * @param onError callback invoked in case of an error + * * @hide */ - public void getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors, - @NonNull CompletableFuture<PowerMonitorReadings> future) { + public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors, + @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess, + @NonNull Consumer<RuntimeException> onError) { if (mPowerStats == null) { - future.completeExceptionally( - new IllegalArgumentException("Unsupported power monitor")); + onError.accept(new IllegalArgumentException("Unsupported power monitor")); return; } - Arrays.sort(powerMonitors, POWER_MONITOR_COMPARATOR); - int[] indices = new int[powerMonitors.length]; - for (int i = 0; i < powerMonitors.length; i++) { - indices[i] = powerMonitors[i].index; + PowerMonitor[] powerMonitorsArray = + powerMonitors.toArray(new PowerMonitor[powerMonitors.size()]); + Arrays.sort(powerMonitorsArray, POWER_MONITOR_COMPARATOR); + int[] indices = new int[powerMonitors.size()]; + for (int i = 0; i < powerMonitors.size(); i++) { + indices[i] = powerMonitorsArray[i].index; } try { - mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) { + mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == IPowerStatsService.RESULT_SUCCESS) { - future.complete(new PowerMonitorReadings(powerMonitors, + onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray, resultData.getLongArray(IPowerStatsService.KEY_ENERGY), resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS))); } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { - future.completeExceptionally( - new IllegalArgumentException("Unsupported power monitor")); + onError.accept(new IllegalArgumentException("Unsupported power monitor")); } else { - future.completeExceptionally( - new IllegalStateException( - "Unrecognized result code " + resultCode)); + onError.accept(new IllegalStateException( + "Unrecognized result code " + resultCode)); } } }); diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index 059bd846327c..22e8251b3f52 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -19,6 +19,7 @@ package android.os.storage; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.pm.UserInfo; import android.os.IVold; import java.util.List; @@ -169,4 +170,19 @@ public abstract class StorageManagerInternal { */ public abstract void registerCloudProviderChangeListener( @NonNull CloudProviderChangeListener listener); + + /** + * Prepares user data directories before moving storage or apps. This is required as adoptable + * storage unlock is tied to the prepare user data and storage needs to be unlocked before + * performing any operations on it. This will also create user data directories before + * initiating the move operations, which essential for ensuring the directories to have correct + * SELinux labels and permissions. + * + * @param fromVolumeUuid the source volume UUID from which content needs to be transferred + * @param toVolumeUuid the destination volume UUID to which contents are to be transferred + * @param users a list of users for whom to prepare storage + */ + public abstract void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid, + List<UserInfo> users); + } diff --git a/core/java/android/os/vibrator/MultiVibratorInfo.java b/core/java/android/os/vibrator/MultiVibratorInfo.java new file mode 100644 index 000000000000..5f3273129213 --- /dev/null +++ b/core/java/android/os/vibrator/MultiVibratorInfo.java @@ -0,0 +1,294 @@ +/* + * Copyright 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 android.os.vibrator; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.vibrator.IVibrator; +import android.os.Vibrator; +import android.os.VibratorInfo; +import android.util.Range; +import android.util.Slog; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; + +import java.util.Arrays; +import java.util.function.Function; + +/** + * Represents multiple vibrator information as a single {@link VibratorInfo}. + * + * <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive + * support. + * + * @hide + */ +public final class MultiVibratorInfo extends VibratorInfo { + private static final String TAG = "MultiVibratorInfo"; + + // Epsilon used for float comparison applied in calculations for the merged info. + private static final float EPSILON = 1e-5f; + + public MultiVibratorInfo(int id, VibratorInfo[] vibrators) { + this(id, vibrators, frequencyProfileIntersection(vibrators)); + } + + private MultiVibratorInfo( + int id, VibratorInfo[] vibrators, VibratorInfo.FrequencyProfile mergedProfile) { + super(id, + capabilitiesIntersection(vibrators, mergedProfile.isEmpty()), + supportedEffectsIntersection(vibrators), + supportedBrakingIntersection(vibrators), + supportedPrimitivesAndDurationsIntersection(vibrators), + integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax), + integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax), + integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax), + integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax), + floatPropertyIntersection(vibrators, VibratorInfo::getQFactor), + mergedProfile); + } + + private static int capabilitiesIntersection(VibratorInfo[] infos, + boolean frequencyProfileIsEmpty) { + int intersection = ~0; + for (VibratorInfo info : infos) { + intersection &= info.getCapabilities(); + } + if (frequencyProfileIsEmpty) { + // Revoke frequency control if the merged frequency profile ended up empty. + intersection &= ~IVibrator.CAP_FREQUENCY_CONTROL; + } + return intersection; + } + + @Nullable + private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) { + for (VibratorInfo info : infos) { + if (!info.isBrakingSupportKnown()) { + // If one vibrator support is unknown, then the intersection is also unknown. + return null; + } + } + + SparseBooleanArray intersection = new SparseBooleanArray(); + SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking(); + + brakingIdLoop: + for (int i = 0; i < firstVibratorBraking.size(); i++) { + int brakingId = firstVibratorBraking.keyAt(i); + if (!firstVibratorBraking.valueAt(i)) { + // The first vibrator already doesn't support this braking, so skip it. + continue brakingIdLoop; + } + + for (int j = 1; j < infos.length; j++) { + if (!infos[j].hasBrakingSupport(brakingId)) { + // One vibrator doesn't support this braking, so the intersection doesn't. + continue brakingIdLoop; + } + } + + intersection.put(brakingId, true); + } + + return intersection; + } + + @Nullable + private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) { + for (VibratorInfo info : infos) { + if (!info.isEffectSupportKnown()) { + // If one vibrator support is unknown, then the intersection is also unknown. + return null; + } + } + + SparseBooleanArray intersection = new SparseBooleanArray(); + SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects(); + + effectIdLoop: + for (int i = 0; i < firstVibratorEffects.size(); i++) { + int effectId = firstVibratorEffects.keyAt(i); + if (!firstVibratorEffects.valueAt(i)) { + // The first vibrator already doesn't support this effect, so skip it. + continue effectIdLoop; + } + + for (int j = 1; j < infos.length; j++) { + if (infos[j].isEffectSupported(effectId) != Vibrator.VIBRATION_EFFECT_SUPPORT_YES) { + // One vibrator doesn't support this effect, so the intersection doesn't. + continue effectIdLoop; + } + } + + intersection.put(effectId, true); + } + + return intersection; + } + + @NonNull + private static SparseIntArray supportedPrimitivesAndDurationsIntersection( + VibratorInfo[] infos) { + SparseIntArray intersection = new SparseIntArray(); + SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives(); + + primitiveIdLoop: + for (int i = 0; i < firstVibratorPrimitives.size(); i++) { + int primitiveId = firstVibratorPrimitives.keyAt(i); + int primitiveDuration = firstVibratorPrimitives.valueAt(i); + if (primitiveDuration == 0) { + // The first vibrator already doesn't support this primitive, so skip it. + continue primitiveIdLoop; + } + + for (int j = 1; j < infos.length; j++) { + int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId); + if (vibratorPrimitiveDuration == 0) { + // One vibrator doesn't support this primitive, so the intersection doesn't. + continue primitiveIdLoop; + } else { + // The primitive vibration duration is the maximum among all vibrators. + primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration); + } + } + + intersection.put(primitiveId, primitiveDuration); + } + return intersection; + } + + private static int integerLimitIntersection(VibratorInfo[] infos, + Function<VibratorInfo, Integer> propertyGetter) { + int limit = 0; // Limit 0 means unlimited + for (VibratorInfo info : infos) { + int vibratorLimit = propertyGetter.apply(info); + if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) { + // This vibrator is limited and intersection is unlimited or has a larger limit: + // use smaller limit here for the intersection. + limit = vibratorLimit; + } + } + return limit; + } + + private static float floatPropertyIntersection(VibratorInfo[] infos, + Function<VibratorInfo, Float> propertyGetter) { + float property = propertyGetter.apply(infos[0]); + if (Float.isNaN(property)) { + // If one vibrator is undefined then the intersection is undefined. + return Float.NaN; + } + for (int i = 1; i < infos.length; i++) { + if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) { + // If one vibrator has a different value then the intersection is undefined. + return Float.NaN; + } + } + return property; + } + + @NonNull + private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) { + float freqResolution = floatPropertyIntersection(infos, + info -> info.getFrequencyProfile().getFrequencyResolutionHz()); + float resonantFreq = floatPropertyIntersection(infos, + VibratorInfo::getResonantFrequencyHz); + Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution); + + if ((freqRange == null) || Float.isNaN(freqResolution)) { + return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null); + } + + int amplitudeCount = + Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution); + float[] maxAmplitudes = new float[amplitudeCount]; + + // Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this + // will fail if the loop below is broken and do not replace filled values with actual + // vibrator measurements. + Arrays.fill(maxAmplitudes, Float.MAX_VALUE); + + for (VibratorInfo info : infos) { + Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz(); + float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes(); + int vibratorStartIdx = Math.round( + (freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution); + int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1; + + if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) { + Slog.w(TAG, "Error calculating the intersection of vibrator frequency" + + " profiles: attempted to fetch from vibrator " + + info.getId() + " max amplitude with bad index " + vibratorStartIdx); + return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null); + } + + for (int i = 0; i < maxAmplitudes.length; i++) { + maxAmplitudes[i] = Math.min(maxAmplitudes[i], + vibratorMaxAmplitudes[vibratorStartIdx + i]); + } + } + + return new FrequencyProfile(resonantFreq, freqRange.getLower(), + freqResolution, maxAmplitudes); + } + + @Nullable + private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos, + float frequencyResolution) { + Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz(); + if (firstRange == null) { + // If one vibrator is undefined then the intersection is undefined. + return null; + } + float intersectionLower = firstRange.getLower(); + float intersectionUpper = firstRange.getUpper(); + + // Generate the intersection of all vibrator supported ranges, making sure that both + // min supported frequencies are aligned w.r.t. the frequency resolution. + + for (int i = 1; i < infos.length; i++) { + Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz(); + if (vibratorRange == null) { + // If one vibrator is undefined then the intersection is undefined. + return null; + } + + if ((vibratorRange.getLower() >= intersectionUpper) + || (vibratorRange.getUpper() <= intersectionLower)) { + // If the range and intersection are disjoint then the intersection is undefined + return null; + } + + float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower()); + if ((frequencyDelta % frequencyResolution) > EPSILON) { + // If the intersection is not aligned with one vibrator then it's undefined + return null; + } + + intersectionLower = Math.max(intersectionLower, vibratorRange.getLower()); + intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper()); + } + + if ((intersectionUpper - intersectionLower) < frequencyResolution) { + // If the intersection is empty then it's undefined. + return null; + } + + return Range.create(intersectionLower, intersectionUpper); + } +} diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java index 42b6c2dae5eb..a035092e314f 100644 --- a/core/java/android/os/vibrator/PrebakedSegment.java +++ b/core/java/android/os/vibrator/PrebakedSegment.java @@ -23,6 +23,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; import android.os.Vibrator; +import android.os.VibratorInfo; import java.util.Objects; @@ -77,8 +78,8 @@ public final class PrebakedSegment extends VibrationEffectSegment { /** @hide */ @Override - public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { - if (vibrator.areAllEffectsSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) { + public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { + if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) { return true; } if (!mFallback) { diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java index c52a09ce69c7..95d97bfe4ad1 100644 --- a/core/java/android/os/vibrator/PrimitiveSegment.java +++ b/core/java/android/os/vibrator/PrimitiveSegment.java @@ -22,7 +22,7 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; -import android.os.Vibrator; +import android.os.VibratorInfo; import com.android.internal.util.Preconditions; @@ -77,8 +77,8 @@ public final class PrimitiveSegment extends VibrationEffectSegment { /** @hide */ @Override - public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { - return vibrator.areAllPrimitivesSupported(mPrimitiveId); + public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { + return vibratorInfo.isPrimitiveSupported(mPrimitiveId); } /** @hide */ diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java index e997bcdbdb32..5f9d1024d9a5 100644 --- a/core/java/android/os/vibrator/RampSegment.java +++ b/core/java/android/os/vibrator/RampSegment.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.TestApi; import android.os.Parcel; import android.os.VibrationEffect; -import android.os.Vibrator; +import android.os.VibratorInfo; import com.android.internal.util.Preconditions; @@ -96,7 +96,7 @@ public final class RampSegment extends VibrationEffectSegment { /** @hide */ @Override - public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { boolean areFeaturesSupported = true; // If the start/end frequencies are not the same, require frequency control since we need to // ramp up/down the frequency. @@ -104,7 +104,7 @@ public final class RampSegment extends VibrationEffectSegment { // If there is no frequency ramping, make sure that the one frequency used does not // require frequency control. || frequencyRequiresFrequencyControl(mStartFrequencyHz)) { - areFeaturesSupported &= vibrator.hasFrequencyControl(); + areFeaturesSupported &= vibratorInfo.hasFrequencyControl(); } // If the start/end amplitudes are not the same, require amplitude control since we need to // ramp up/down the amplitude. @@ -112,7 +112,7 @@ public final class RampSegment extends VibrationEffectSegment { // If there is no amplitude ramping, make sure that the amplitude used does not // require amplitude control. || amplitudeRequiresAmplitudeControl(mStartAmplitude)) { - areFeaturesSupported &= vibrator.hasAmplitudeControl(); + areFeaturesSupported &= vibratorInfo.hasAmplitudeControl(); } return areFeaturesSupported; } diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java index a585aa866ed8..9576a5bba1f1 100644 --- a/core/java/android/os/vibrator/StepSegment.java +++ b/core/java/android/os/vibrator/StepSegment.java @@ -21,7 +21,7 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; -import android.os.Vibrator; +import android.os.VibratorInfo; import com.android.internal.util.Preconditions; @@ -82,13 +82,13 @@ public final class StepSegment extends VibrationEffectSegment { /** @hide */ @Override - public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { boolean areFeaturesSupported = true; if (frequencyRequiresFrequencyControl(mFrequencyHz)) { - areFeaturesSupported &= vibrator.hasFrequencyControl(); + areFeaturesSupported &= vibratorInfo.hasFrequencyControl(); } if (amplitudeRequiresAmplitudeControl(mAmplitude)) { - areFeaturesSupported &= vibrator.hasAmplitudeControl(); + areFeaturesSupported &= vibratorInfo.hasAmplitudeControl(); } return areFeaturesSupported; } diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java index 3b286a7228fb..17ac36f3ab37 100644 --- a/core/java/android/os/vibrator/VibrationEffectSegment.java +++ b/core/java/android/os/vibrator/VibrationEffectSegment.java @@ -21,7 +21,7 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; -import android.os.Vibrator; +import android.os.VibratorInfo; /** * Representation of a single segment of a {@link VibrationEffect}. @@ -65,7 +65,7 @@ public abstract class VibrationEffectSegment implements Parcelable { * * @hide */ - public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator); + public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo); /** * Returns true if this segment could be a haptic feedback effect candidate. diff --git a/core/java/android/os/vibrator/VibratorInfoFactory.java b/core/java/android/os/vibrator/VibratorInfoFactory.java new file mode 100644 index 000000000000..d10d7ec222c3 --- /dev/null +++ b/core/java/android/os/vibrator/VibratorInfoFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright 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 android.os.vibrator; + +import android.annotation.NonNull; +import android.os.VibratorInfo; + +/** + * Factory for creating {@link VibratorInfo}s. + * + * @hide + */ +public final class VibratorInfoFactory { + /** + * Creates a single {@link VibratorInfo} that is an intersection of a given collection of + * {@link VibratorInfo}s. That is, the capabilities of the returned info will be an + * intersection of that of the provided infos. + * + * @param id the ID for the new {@link VibratorInfo}. + * @param vibratorInfos the {@link VibratorInfo}s from which to create a single + * {@link VibratorInfo}. + * @return a {@link VibratorInfo} that represents the intersection of {@code vibratorInfos}. + */ + @NonNull + public static VibratorInfo create(int id, @NonNull VibratorInfo[] vibratorInfos) { + if (vibratorInfos.length == 0) { + return new VibratorInfo.Builder(id).build(); + } + if (vibratorInfos.length == 1) { + // Create an equivalent info with the requested ID. + return new VibratorInfo(id, vibratorInfos[0]); + } + // Create a MultiVibratorInfo that intersects all the given infos and has the requested ID. + return new MultiVibratorInfo(id, vibratorInfos); + } + + private VibratorInfoFactory() {} +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index abf50a2ef412..abc8bf51b67b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5627,7 +5627,7 @@ public final class Settings { public static final String SHOW_TOUCHES = "show_touches"; /** - * Show key presses and other events dispatched to focused windows on the screen. + * Show key presses dispatched to focused windows on the screen. * 0 = no * 1 = yes * @hide @@ -5635,6 +5635,14 @@ public final class Settings { public static final String SHOW_KEY_PRESSES = "show_key_presses"; /** + * Show rotary input dispatched to focused windows on the screen. + * 0 = no + * 1 = yes + * @hide + */ + public static final String SHOW_ROTARY_INPUT = "show_rotary_input"; + + /** * Log raw orientation data from * {@link com.android.server.policy.WindowOrientationListener} for use with the * orientationplot.py tool. @@ -10126,6 +10134,13 @@ public final class Settings { public static final String AUDIO_DEVICE_INVENTORY = "audio_device_inventory"; /** + * Stores a boolean that defines whether the CSD as a feature is enabled or not. + * @hide + */ + public static final String AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED = + "audio_safe_csd_as_a_feature_enabled"; + + /** * Indicates whether notification display on the lock screen is enabled. * <p> * Type: int (0 for false, 1 for true) @@ -10642,6 +10657,14 @@ public final class Settings { "assist_long_press_home_enabled"; /** + * Whether press and hold on nav handle can trigger search. + * + * @hide + */ + public static final String SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED = + "search_press_hold_nav_handle_enabled"; + + /** * Control whether Trust Agents are in active unlock or extend unlock mode. * @hide */ @@ -11265,6 +11288,19 @@ public final class Settings { "navigation_mode"; /** + * The value is from another(source) device's {@link #NAVIGATION_MODE} during restore. + * It's supposed to be written only by + * {@link com.android.providers.settings.SettingsHelper}. + * This setting should not be added into backup array. + * <p>Value: -1 = Can't get value from restore(default), + * 0 = 3 button, + * 1 = 2 button, + * 2 = fully gestural. + * @hide + */ + public static final String NAVIGATION_MODE_RESTORE = "navigation_mode_restore"; + + /** * Scale factor for the back gesture inset size on the left side of the screen. * @hide */ @@ -18616,6 +18652,7 @@ public final class Settings { * What OS does paired device has. * @hide */ + @Readable public static final String PAIRED_DEVICE_OS_TYPE = "paired_device_os_type"; // Possible values of PAIRED_DEVICE_OS_TYPE diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS index f6b123554367..4e8d6e70091c 100644 --- a/core/java/android/security/OWNERS +++ b/core/java/android/security/OWNERS @@ -6,5 +6,4 @@ per-file NetworkSecurityPolicy.java = cbrubaker@google.com per-file NetworkSecurityPolicy.java = klyubin@google.com per-file FrameworkNetworkSecurityPolicy.java = cbrubaker@google.com per-file FrameworkNetworkSecurityPolicy.java = klyubin@google.com -per-file Confirmation*.java = jdanis@google.com -per-file Confirmation*.java = swillden@google.com +per-file Confirmation*.java = file:/keystore/OWNERS diff --git a/core/java/android/security/keystore/OWNERS b/core/java/android/security/keystore/OWNERS index 65129a46d113..d9e01161ce6d 100644 --- a/core/java/android/security/keystore/OWNERS +++ b/core/java/android/security/keystore/OWNERS @@ -1,5 +1 @@ -# Bug component: 189335 - -swillden@google.com -jdanis@google.com -jbires@google.com +include /keystore/OWNERS diff --git a/core/java/android/security/keystore/recovery/OWNERS b/core/java/android/security/keystore/recovery/OWNERS deleted file mode 100644 index 65129a46d113..000000000000 --- a/core/java/android/security/keystore/recovery/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# Bug component: 189335 - -swillden@google.com -jdanis@google.com -jbires@google.com diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index b85cf6d75cb7..fce87db2bbf0 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -25,6 +25,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -285,6 +286,7 @@ public abstract class ControlsProviderService extends Service { private IControlsSubscriber mCs; private boolean mEnforceStateless; private Context mContext; + private SubscriptionAdapter mSubscription; SubscriberProxy(boolean enforceStateless, IBinder token, IControlsSubscriber cs) { mEnforceStateless = enforceStateless; @@ -300,11 +302,14 @@ public abstract class ControlsProviderService extends Service { public void onSubscribe(Subscription subscription) { try { - mCs.onSubscribe(mToken, new SubscriptionAdapter(subscription)); + SubscriptionAdapter subscriptionAdapter = new SubscriptionAdapter(subscription); + mCs.onSubscribe(mToken, subscriptionAdapter); + mSubscription = subscriptionAdapter; } catch (RemoteException ex) { - ex.rethrowAsRuntimeException(); + handleRemoteException(ex); } } + public void onNext(@NonNull Control control) { Preconditions.checkNotNull(control); try { @@ -318,20 +323,36 @@ public abstract class ControlsProviderService extends Service { } mCs.onNext(mToken, control); } catch (RemoteException ex) { - ex.rethrowAsRuntimeException(); + handleRemoteException(ex); } } + public void onError(Throwable t) { try { mCs.onError(mToken, t.toString()); + mSubscription = null; } catch (RemoteException ex) { - ex.rethrowAsRuntimeException(); + handleRemoteException(ex); } } + public void onComplete() { try { mCs.onComplete(mToken); + mSubscription = null; } catch (RemoteException ex) { + handleRemoteException(ex); + } + } + + private void handleRemoteException(RemoteException ex) { + if (ex instanceof DeadObjectException) { + // System UI crashed or is restarting. There is no need to rethrow this + SubscriptionAdapter subscriptionAdapter = mSubscription; + if (subscriptionAdapter != null) { + subscriptionAdapter.cancel(); + } + } else { ex.rethrowAsRuntimeException(); } } diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index 75640bd47ab9..f3b4c6da4a01 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -92,6 +92,7 @@ public class NotificationRankingUpdate implements Parcelable { mapParcel.recycle(); if (buffer != null) { mRankingMapFd.unmap(buffer); + mRankingMapFd.close(); } } } else { diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 21f676e3fe2f..94d851603064 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -67,6 +67,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionManagerService; import com.android.internal.app.IVoiceInteractionSoundTriggerSession; +import com.android.internal.infra.AndroidFuture; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -1710,6 +1711,11 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { Slog.i(TAG, "onProcessRestarted"); mHandler.sendEmptyMessage(MSG_PROCESS_RESTARTED); } + + @Override + public void onOpenFile(String filename, AndroidFuture future) throws RemoteException { + throw new UnsupportedOperationException("Hotword cannot access files from the disk."); + } } void onDetectorRemoteException() { diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index d9ee859dc66b..ccf8b67826c8 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -227,6 +227,12 @@ public abstract class HotwordDetectionService extends Service public void stopDetection() { HotwordDetectionService.this.onStopDetection(); } + + @Override + public void registerRemoteStorageService(IDetectorSessionStorageService + detectorSessionStorageService) { + throw new UnsupportedOperationException("Hotword cannot access files from the disk."); + } }; @Override diff --git a/core/java/android/service/voice/IDetectorSessionStorageService.aidl b/core/java/android/service/voice/IDetectorSessionStorageService.aidl new file mode 100644 index 000000000000..592373e0ef80 --- /dev/null +++ b/core/java/android/service/voice/IDetectorSessionStorageService.aidl @@ -0,0 +1,30 @@ +/* + * 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 android.service.voice; + +import com.android.internal.infra.AndroidFuture; + +/** + * @hide + */ +oneway interface IDetectorSessionStorageService { + /** + * Called when a file open request is sent. Only files with the given names under the internal + * app storage, i.e., {@link Context#getFilesDir()} can be opened. + */ + void openFile(in String filename, in AndroidFuture future); +} diff --git a/core/java/android/service/voice/ISandboxedDetectionService.aidl b/core/java/android/service/voice/ISandboxedDetectionService.aidl index 098536dfeb64..c76ac28eb36c 100644 --- a/core/java/android/service/voice/ISandboxedDetectionService.aidl +++ b/core/java/android/service/voice/ISandboxedDetectionService.aidl @@ -24,6 +24,7 @@ import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.SharedMemory; +import android.service.voice.IDetectorSessionStorageService; import android.service.voice.IDetectorSessionVisualQueryDetectionCallback; import android.service.voice.IDspHotwordDetectionCallback; import android.view.contentcapture.IContentCaptureManager; @@ -71,4 +72,10 @@ oneway interface ISandboxedDetectionService { void ping(in IRemoteCallback callback); void stopDetection(); + + /** + * Registers the interface stub to talk to the voice interaction service for initialization/ + * detection unrelated functionalities. + */ + void registerRemoteStorageService(in IDetectorSessionStorageService detectorSessionStorageService); } diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index 128bc0daedea..f1bc792696d6 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -36,6 +36,7 @@ import android.util.Slog; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionManagerService; +import com.android.internal.infra.AndroidFuture; import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -302,6 +303,11 @@ class SoftwareHotwordDetector extends AbstractDetector { Binder.withCleanCallingIdentity(() -> mExecutor.execute( () -> mCallback.onHotwordDetectionServiceRestarted())); } + + @Override + public void onOpenFile(String filename, AndroidFuture future) throws RemoteException { + throw new UnsupportedOperationException("Hotword cannot access files from the disk."); + } } /** @hide */ diff --git a/core/java/android/service/voice/VisualQueryDetectionService.java b/core/java/android/service/voice/VisualQueryDetectionService.java index cbe7666ddf43..d184b1eb96ab 100644 --- a/core/java/android/service/voice/VisualQueryDetectionService.java +++ b/core/java/android/service/voice/VisualQueryDetectionService.java @@ -40,7 +40,12 @@ import android.util.Log; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.IContentCaptureManager; +import com.android.internal.infra.AndroidFuture; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.util.Objects; +import java.util.concurrent.ExecutionException; import java.util.function.IntConsumer; /** @@ -86,6 +91,8 @@ public abstract class VisualQueryDetectionService extends Service private ContentCaptureManager mContentCaptureManager; @Nullable private IRecognitionServiceManager mIRecognitionServiceManager; + @Nullable + private IDetectorSessionStorageService mDetectorSessionStorageService; private final ISandboxedDetectionService mInterface = new ISandboxedDetectionService.Stub() { @@ -154,6 +161,12 @@ public abstract class VisualQueryDetectionService extends Service public void updateRecognitionServiceManager(IRecognitionServiceManager manager) { mIRecognitionServiceManager = manager; } + + @Override + public void registerRemoteStorageService(IDetectorSessionStorageService + detectorSessionStorageService) { + mDetectorSessionStorageService = detectorSessionStorageService; + } }; @Override @@ -323,4 +336,23 @@ public abstract class VisualQueryDetectionService extends Service } } + /** + * Overrides {@link Context#openFileInput} to read files with the given file names under the + * internal app storage of the {@link VoiceInteractionService}, i.e., only files stored in + * {@link Context#getFilesDir()} can be opened. + */ + @Override + public @Nullable FileInputStream openFileInput(@NonNull String filename) throws + FileNotFoundException { + try { + AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>(); + mDetectorSessionStorageService.openFile(filename, future); + ParcelFileDescriptor pfd = future.get(); + return new FileInputStream(pfd.getFileDescriptor()); + } catch (RemoteException | ExecutionException | InterruptedException e) { + Log.w(TAG, "Cannot open file due to remote service failure"); + throw new FileNotFoundException(e.getMessage()); + } + } + } diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java index 9e0eb4bdfcd9..b5448d4374b2 100644 --- a/core/java/android/service/voice/VisualQueryDetector.java +++ b/core/java/android/service/voice/VisualQueryDetector.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.Context; import android.hardware.soundtrigger.SoundTrigger; import android.media.AudioFormat; import android.os.Binder; @@ -37,7 +38,10 @@ import android.util.Slog; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionManagerService; +import com.android.internal.infra.AndroidFuture; +import java.io.File; +import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -58,18 +62,20 @@ public class VisualQueryDetector { private final Callback mCallback; private final Executor mExecutor; + private final Context mContext; private final IVoiceInteractionManagerService mManagerService; private final VisualQueryDetectorInitializationDelegate mInitializationDelegate; private final String mAttributionTag; VisualQueryDetector( IVoiceInteractionManagerService managerService, - @NonNull @CallbackExecutor Executor executor, - Callback callback, @Nullable String attributionTag) { + @NonNull @CallbackExecutor Executor executor, Callback callback, Context context, + @Nullable String attributionTag) { mManagerService = managerService; mCallback = callback; mExecutor = executor; mInitializationDelegate = new VisualQueryDetectorInitializationDelegate(); + mContext = context; mAttributionTag = attributionTag; } @@ -247,7 +253,7 @@ public class VisualQueryDetector { @Override void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) { initAndVerifyDetector(options, sharedMemory, - new InitializationStateListener(mExecutor, mCallback), + new InitializationStateListener(mExecutor, mCallback, mContext), DETECTOR_TYPE_VISUAL_QUERY_DETECTOR, mAttributionTag); } @@ -332,9 +338,12 @@ public class VisualQueryDetector { private final Executor mExecutor; private final Callback mCallback; - InitializationStateListener(Executor executor, Callback callback) { + private final Context mContext; + + InitializationStateListener(Executor executor, Callback callback, Context context) { this.mExecutor = executor; this.mCallback = callback; + this.mContext = context; } @Override @@ -428,5 +437,22 @@ public class VisualQueryDetector { !TextUtils.isEmpty(errorMessage) ? errorMessage : "Error data is null"); })); } + @Override + public void onOpenFile(String filename, AndroidFuture future) throws RemoteException { + Slog.v(TAG, "BinderCallback#onOpenFile " + filename); + Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> { + Slog.v(TAG, "onOpenFile: " + filename); + File f = new File(mContext.getFilesDir(), filename); + ParcelFileDescriptor pfd = null; + try { + Slog.d(TAG, "opened a file with ParcelFileDescriptor."); + pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); + } catch (FileNotFoundException e) { + Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned."); + } finally { + future.complete(pfd); + } + })); + } } } diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 8cec17fe5340..b48b7ecd73a2 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -965,7 +965,7 @@ public class VoiceInteractionService extends Service { } VisualQueryDetector visualQueryDetector = - new VisualQueryDetector(mSystemService, executor, callback, + new VisualQueryDetector(mSystemService, executor, callback, this, getAttributionTag()); HotwordDetector visualQueryDetectorInitializationDelegate = visualQueryDetector.getInitializationDelegate(); diff --git a/core/java/android/speech/OWNERS b/core/java/android/speech/OWNERS index 162e02904075..0f2f8ad3d99e 100644 --- a/core/java/android/speech/OWNERS +++ b/core/java/android/speech/OWNERS @@ -1,5 +1,4 @@ volnov@google.com eugeniom@google.com schfan@google.com -andreaambu@google.com -hackz@google.com
\ No newline at end of file +hackz@google.com diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index 9656f36d2c4d..7f313c177053 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -38,6 +38,7 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; import java.lang.ref.WeakReference; @@ -232,39 +233,68 @@ public abstract class RecognitionService extends Service { intent, attributionSource, new ModelDownloadListener() { + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private boolean mIsTerminated = false; + @Override public void onProgress(int completedPercent) { - try { - listener.onProgress(completedPercent); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + synchronized (mLock) { + if (mIsTerminated) { + return; + } + try { + listener.onProgress(completedPercent); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } @Override public void onSuccess() { - try { - listener.onSuccess(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + synchronized (mLock) { + if (mIsTerminated) { + return; + } + mIsTerminated = true; + try { + listener.onSuccess(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } @Override public void onScheduled() { - try { - listener.onScheduled(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + synchronized (mLock) { + if (mIsTerminated) { + return; + } + mIsTerminated = true; + try { + listener.onScheduled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } @Override public void onError(int error) { - try { - listener.onError(error); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + synchronized (mLock) { + if (mIsTerminated) { + return; + } + mIsTerminated = true; + try { + listener.onError(error); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } }); diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 9ed57c3aedb1..7748d5b3c861 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.text.LineBreakConfig; import android.text.style.ParagraphStyle; /** @@ -53,7 +54,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * line width * @param includePad set whether to include extra space beyond font ascent and descent which is * needed to avoid clipping in some scripts + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad) { @@ -79,7 +82,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is * not used, {@code outerWidth} is used instead + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingmult, float spacingadd, BoringLayout.Metrics metrics, boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { @@ -111,7 +116,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * False for keeping the first font's line height. If some glyphs * requires larger vertical spaces, by passing true to this * argument, the layout increase the line height to fit all glyphs. + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public static @NonNull BoringLayout make( @NonNull CharSequence source, @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth, @@ -247,10 +254,17 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * line width * @param includePad set whether to include extra space beyond font ascent and descent which is * needed to avoid clipping in some scripts + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public BoringLayout(CharSequence source, TextPaint paint, int outerwidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad) { - super(source, paint, outerwidth, align, spacingMult, spacingAdd); + super(source, paint, outerwidth, align, TextDirectionHeuristics.LTR, spacingMult, + spacingAdd, includePad, false /* fallbackLineSpacing */, + outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */, + BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */, + null /* rightIndents */, JUSTIFICATION_MODE_NONE, + LineBreakConfig.NONE); mEllipsizedWidth = outerwidth; mEllipsizedStart = 0; @@ -277,7 +291,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is * not used, {@code outerWidth} is used instead + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { @@ -306,7 +322,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * False for keeping the first font's line height. If some glyphs * requires larger vertical spaces, by passing true to this * argument, the layout increase the line height to fit all glyphs. + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public BoringLayout( @NonNull CharSequence source, @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult, @@ -318,25 +336,58 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * but we can't use "this" for the callback until the call to * super() finishes. */ - super(source, paint, outerWidth, align, spacingMult, spacingAdd); + this(source, paint, outerWidth, align, TextDirectionHeuristics.LTR, spacingMult, + spacingAdd, includePad, useFallbackLineSpacing, + ellipsizedWidth, ellipsize, 1 /* maxLines */, + BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */, + null /* rightIndents */, JUSTIFICATION_MODE_NONE, + LineBreakConfig.NONE, metrics); + } + + /* package */ BoringLayout( + CharSequence text, + TextPaint paint, + int width, + Alignment align, + TextDirectionHeuristic textDir, + float spacingMult, + float spacingAdd, + boolean includePad, + boolean fallbackLineSpacing, + int ellipsizedWidth, + TextUtils.TruncateAt ellipsize, + int maxLines, + int breakStrategy, + int hyphenationFrequency, + int[] leftIndents, + int[] rightIndents, + int justificationMode, + LineBreakConfig lineBreakConfig, + Metrics metrics) { + + super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad, + fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy, + hyphenationFrequency, leftIndents, rightIndents, justificationMode, + lineBreakConfig); + boolean trust; if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) { - mEllipsizedWidth = outerWidth; + mEllipsizedWidth = width; mEllipsizedStart = 0; mEllipsizedCount = 0; trust = true; } else { - replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this), - paint, outerWidth, align, spacingMult, spacingAdd); + replaceWith(TextUtils.ellipsize(text, paint, ellipsizedWidth, ellipsize, true, this), + paint, width, align, spacingMult, spacingAdd); mEllipsizedWidth = ellipsizedWidth; trust = false; } - mUseFallbackLineSpacing = useFallbackLineSpacing; - init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing); + mUseFallbackLineSpacing = fallbackLineSpacing; + init(getText(), paint, align, metrics, includePad, trust, fallbackLineSpacing); } /* package */ void init(CharSequence source, TextPaint paint, Alignment align, @@ -391,7 +442,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @param paint a paint * @return layout metric for the given text. null if given text is unable to be handled by * BoringLayout. + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public static Metrics isBoring(CharSequence text, TextPaint paint) { return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null); } @@ -406,7 +459,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @return layout metric for the given text. If metrics is not null, this method fills values * to given metrics object instead of allocating new metrics object. null if given text * is unable to be handled by BoringLayout. + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) { return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics); } @@ -466,7 +521,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * argument, the layout increase the line height to fit all glyphs. * @param metrics the out metrics. * @return metrics on success. null if text cannot be rendered by BoringLayout. + * @deprecated Use {@link android.text.Layout.Builder} instead. */ + @Deprecated public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing, @Nullable Metrics metrics) { diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index ba08f2541f79..e287bd9165ce 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -388,7 +388,11 @@ public class DynamicLayout extends Layout { @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth) { super(createEllipsizer(ellipsize, display), - paint, width, align, textDir, spacingmult, spacingadd); + paint, width, align, textDir, spacingmult, spacingadd, includepad, + false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize, + Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency, + null /* leftIndents */, null /* rightIndents */, justificationMode, + lineBreakConfig); final Builder b = Builder.obtain(base, paint, width) .setAlignment(align) @@ -410,7 +414,11 @@ public class DynamicLayout extends Layout { private DynamicLayout(@NonNull Builder b) { super(createEllipsizer(b.mEllipsize, b.mDisplay), - b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd); + b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd, + b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize, + Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency, + null /* leftIndents */, null /* rightIndents */, b.mJustificationMode, + b.mLineBreakConfig); mDisplay = b.mDisplay; mIncludePad = b.mIncludePad; @@ -614,8 +622,7 @@ public class DynamicLayout extends Layout { sBuilder = null; } - if (reflowed == null) { - reflowed = new StaticLayout(null); + if (b == null) { b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth()); } @@ -631,9 +638,10 @@ public class DynamicLayout extends Layout { .setHyphenationFrequency(mHyphenationFrequency) .setJustificationMode(mJustificationMode) .setLineBreakConfig(mLineBreakConfig) - .setAddLastLineLineSpacing(!islast); + .setAddLastLineLineSpacing(!islast) + .setIncludePad(false); - reflowed.generate(b, false /*includepad*/, true /*trackpad*/); + reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, reflowed); int n = reflowed.getLineCount(); // If the new layout has a blank line at the end, but it is not // the very end of the buffer, then we already have a line that diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 64dc16de3e4d..98b9fee34a20 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -26,6 +26,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.text.LineBreakConfig; import android.graphics.text.LineBreaker; import android.os.Build; import android.text.method.TextKeyListener; @@ -278,7 +279,9 @@ public abstract class Layout { int width, Alignment align, float spacingMult, float spacingAdd) { this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, - spacingMult, spacingAdd); + spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE, + BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null, + JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE); } /** @@ -290,15 +293,44 @@ public abstract class Layout { * @param width the wrapping width for the text. * @param align whether to left, right, or center the text. Styles can * override the alignment. + * @param textDir a text direction heuristic. * @param spacingMult factor by which to scale the font size to get the * default line spacing * @param spacingAdd amount to add to the default line spacing + * @param includePad true for enabling including font padding + * @param fallbackLineSpacing true for enabling fallback line spacing + * @param ellipsizedWidth width as used for ellipsizing purpose + * @param ellipsize an ellipsize option + * @param maxLines a maximum number of lines. + * @param breakStrategy a break strategy. + * @param hyphenationFrequency a hyphenation frequency + * @param leftIndents a visually left margins + * @param rightIndents a visually right margins + * @param justificationMode a justification mode + * @param lineBreakConfig a line break config * * @hide */ - protected Layout(CharSequence text, TextPaint paint, - int width, Alignment align, TextDirectionHeuristic textDir, - float spacingMult, float spacingAdd) { + protected Layout( + CharSequence text, + TextPaint paint, + int width, + Alignment align, + TextDirectionHeuristic textDir, + float spacingMult, + float spacingAdd, + boolean includePad, + boolean fallbackLineSpacing, + int ellipsizedWidth, + TextUtils.TruncateAt ellipsize, + int maxLines, + int breakStrategy, + int hyphenationFrequency, + int[] leftIndents, + int[] rightIndents, + int justificationMode, + LineBreakConfig lineBreakConfig + ) { if (width < 0) throw new IllegalArgumentException("Layout: " + width + " < 0"); @@ -320,11 +352,17 @@ public abstract class Layout { mSpacingAdd = spacingAdd; mSpannedText = text instanceof Spanned; mTextDir = textDir; - } - - /** @hide */ - protected void setJustificationMode(@JustificationMode int justificationMode) { + mIncludePad = includePad; + mFallbackLineSpacing = fallbackLineSpacing; + mEllipsizedWidth = ellipsize == null ? width : ellipsizedWidth; + mEllipsize = ellipsize; + mMaxLines = maxLines; + mBreakStrategy = breakStrategy; + mHyphenationFrequency = hyphenationFrequency; + mLeftIndents = leftIndents; + mRightIndents = rightIndents; mJustificationMode = justificationMode; + mLineBreakConfig = lineBreakConfig; } /** @@ -910,37 +948,6 @@ public abstract class Layout { } /** - * Return the text that is displayed by this Layout. - */ - public final CharSequence getText() { - return mText; - } - - /** - * Return the base Paint properties for this layout. - * Do NOT change the paint, which may result in funny - * drawing for this layout. - */ - public final TextPaint getPaint() { - return mPaint; - } - - /** - * Return the width of this layout. - */ - public final int getWidth() { - return mWidth; - } - - /** - * Return the width to which this Layout is ellipsizing, or - * {@link #getWidth} if it is not doing anything special. - */ - public int getEllipsizedWidth() { - return mWidth; - } - - /** * Increase the width of this layout to the specified width. * Be careful to use this only when you know it is appropriate— * it does not cause the text to reflow to use the full new width. @@ -972,35 +979,6 @@ public abstract class Layout { } /** - * Return the base alignment of this layout. - */ - public final Alignment getAlignment() { - return mAlignment; - } - - /** - * Return what the text height is multiplied by to get the line height. - */ - public final float getSpacingMultiplier() { - return mSpacingMult; - } - - /** - * Return the number of units of leading that are added to each line. - */ - public final float getSpacingAdd() { - return mSpacingAdd; - } - - /** - * Return the heuristic used to determine paragraph text direction. - * @hide - */ - public final TextDirectionHeuristic getTextDirectionHeuristic() { - return mTextDir; - } - - /** * Return the number of lines of text in this layout. */ public abstract int getLineCount(); @@ -1105,15 +1083,6 @@ public abstract class Layout { } /** - * Return true if the fallback line space is enabled in this Layout. - * - * @return true if the fallback line space is enabled. Otherwise returns false. - */ - public boolean isFallbackLineSpacingEnabled() { - return false; - } - - /** * Returns true if the character at offset and the preceding character * are at different run levels (and thus there's a split caret). * @param offset the offset @@ -3254,7 +3223,17 @@ public abstract class Layout { private boolean mSpannedText; private TextDirectionHeuristic mTextDir; private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; + private boolean mIncludePad; + private boolean mFallbackLineSpacing; + private int mEllipsizedWidth; + private TextUtils.TruncateAt mEllipsize; + private int mMaxLines; + private int mBreakStrategy; + private int mHyphenationFrequency; + private int[] mLeftIndents; + private int[] mRightIndents; private int mJustificationMode; + private LineBreakConfig mLineBreakConfig; /** @hide */ @IntDef(prefix = { "DIR_" }, value = { @@ -3352,4 +3331,683 @@ public abstract class Layout { */ boolean isSegmentInside(@NonNull RectF segmentBounds, @NonNull RectF area); } + + /** + * A builder class for Layout object. + * + * Different from {@link StaticLayout.Builder}, this builder generates the optimal layout based + * on input. If the given text and parameters can be rendered with {@link BoringLayout}, this + * builder generates {@link BoringLayout} instance. Otherwise, {@link StaticLayout} instance is + * generated. + * + * @see StaticLayout.Builder + */ + public static final class Builder { + /** + * Construct a builder class. + * + * @param text a text to be displayed. + * @param start an inclusive start index of the text to be displayed. + * @param end an exclusive end index of the text to be displayed. + * @param paint a paint object to be used for drawing text. + * @param width a width constraint in pixels. + */ + public Builder( + @NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextPaint paint, + @IntRange(from = 0) int width) { + mText = text; + mStart = start; + mEnd = end; + mPaint = paint; + mWidth = width; + mEllipsizedWidth = width; + } + + /** + * Set the text alignment. + * + * The default value is {@link Layout.Alignment#ALIGN_NORMAL}. + * + * @param alignment an alignment. + * @return this builder instance. + * @see Layout.Alignment + * @see Layout#getAlignment() + * @see StaticLayout.Builder#setAlignment(Alignment) + */ + @NonNull + public Builder setAlignment(@NonNull Alignment alignment) { + mAlignment = alignment; + return this; + } + + /** + * Set the text direction heuristics. + * + * The text direction heuristics is used to resolve text direction on the text. + * + * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR} + * + * @param textDirection a text direction heuristic. + * @return this builder instance. + * @see TextDirectionHeuristics + * @see Layout#getTextDirectionHeuristic() + * @see StaticLayout.Builder#setTextDirection(TextDirectionHeuristic) + */ + @NonNull + public Builder setTextDirectionHeuristic(@NonNull TextDirectionHeuristic textDirection) { + mTextDir = textDirection; + return this; + } + + /** + * Set the line spacing amount. + * + * The specified amount of pixels will be added to each line. + * + * The default value is {@code 0}. + * + * @param amount an amount of pixels to be added to line height. + * @return this builder instance. + * @see Layout#getLineSpacingAmount() + * @see Layout#getSpacingAdd() + * @see StaticLayout.Builder#setLineSpacing(float, float) + */ + @NonNull + public Builder setLineSpacingAmount(float amount) { + mSpacingAdd = amount; + return this; + } + + /** + * Set the line spacing multiplier. + * + * The specified value will be multiplied to each line. + * + * The default value is {@code 1}. + * + * @param multiplier a multiplier to be applied to the line height + * @return this builder instance. + * @see Layout#getLineSpacingMultiplier() + * @see Layout#getSpacingMultiplier() + * @see StaticLayout.Builder#setLineSpacing(float, float) + */ + @NonNull + public Builder setLineSpacingMultiplier(float multiplier) { + mSpacingMult = multiplier; + return this; + } + + /** + * Set whether including extra padding into the first and the last line height. + * + * By setting true, the first line of the text and the last line of the text will have extra + * vertical space for avoiding clipping. + * + * The default value is {@code true}. + * + * @param includeFontPadding true for including extra space into first and last line. + * @return this builder instance. + * @see Layout#isIncludeFontPadding() + * @see StaticLayout.Builder#setIncludePad(boolean) + */ + @NonNull + public Builder setIncludeFontPadding(boolean includeFontPadding) { + mIncludePad = includeFontPadding; + return this; + } + + /** + * Set whether to respect the ascent and descent of the fallback fonts. + * + * Set whether to respect the ascent and descent of the fallback fonts that are used in + * displaying the text (which is needed to avoid text from consecutive lines running into + * each other). If set, fallback fonts that end up getting used can increase the ascent + * and descent of the lines that they are used on. + * + * The default value is {@code false} + * + * @param fallbackLineSpacing whether to expand line height based on fallback fonts. + * @return this builder instance. + * @see Layout#isFallbackLineSpacingEnabled() + * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean) + */ + @NonNull + public Builder setFallbackLineSpacingEnabled(boolean fallbackLineSpacing) { + mFallbackLineSpacing = fallbackLineSpacing; + return this; + } + + /** + * Set the width as used for ellipsizing purpose in pixels. + * + * The passed value is ignored and forced to set to the value of width constraint passed in + * constructor if no ellipsize option is set. + * + * The default value is the width constraint. + * + * @param ellipsizeWidth a ellipsizing width in pixels. + * @return this builder instance. + * @see Layout#getEllipsizedWidth() + * @see StaticLayout.Builder#setEllipsizedWidth(int) + */ + @NonNull + public Builder setEllipsizedWidth(@IntRange(from = 0) int ellipsizeWidth) { + mEllipsizedWidth = ellipsizeWidth; + return this; + } + + /** + * Set the ellipsizing type. + * + * By setting null, the ellipsize is disabled. + * + * The default value is {@code null}. + * + * @param ellipsize type of the ellipsize. null for disabling ellipsize. + * @return this builder instance. + * @see Layout#getEllipsize() + * @see StaticLayout.Builder#getEllipsize() + * @see android.text.TextUtils.TruncateAt + */ + @NonNull + public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) { + mEllipsize = ellipsize; + return this; + } + + /** + * Set the maximum number of lines. + * + * The default value is unlimited. + * + * @param maxLines maximum number of lines in the layout. + * @return this builder instance. + * @see Layout#getMaxLines() + * @see StaticLayout.Builder#setMaxLines(int) + */ + @NonNull + public Builder setMaxLines(@IntRange(from = 1) int maxLines) { + mMaxLines = maxLines; + return this; + } + + /** + * Set the line break strategy. + * + * The default value is {@link Layout#BREAK_STRATEGY_SIMPLE}. + * + * @param breakStrategy a break strategy for line breaking. + * @return this builder instance. + * @see Layout#getBreakStrategy() + * @see StaticLayout.Builder#setBreakStrategy(int) + * @see Layout#BREAK_STRATEGY_SIMPLE + * @see Layout#BREAK_STRATEGY_HIGH_QUALITY + * @see Layout#BREAK_STRATEGY_BALANCED + */ + @NonNull + public Builder setBreakStrategy(@BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + return this; + } + + /** + * Set the hyphenation frequency. + * + * The default value is {@link Layout#HYPHENATION_FREQUENCY_NONE}. + * + * @param hyphenationFrequency a hyphenation frequency. + * @return this builder instance. + * @see Layout#getHyphenationFrequency() + * @see StaticLayout.Builder#setHyphenationFrequency(int) + * @see Layout#HYPHENATION_FREQUENCY_NONE + * @see Layout#HYPHENATION_FREQUENCY_NORMAL + * @see Layout#HYPHENATION_FREQUENCY_FULL + * @see Layout#HYPHENATION_FREQUENCY_NORMAL_FAST + * @see Layout#HYPHENATION_FREQUENCY_FULL_FAST + */ + @NonNull + public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + return this; + } + + /** + * Set visually left indents in pixels per lines. + * + * For the lines past the last element in the array, the last element repeats. Passing null + * for disabling indents. + * + * Note that even with the RTL layout, this method reserve spacing at the visually left of + * the line. + * + * The default value is {@code null}. + * + * @param leftIndents array of indents values for the left margins in pixels. + * @return this builder instance. + * @see Layout#getLeftIndents() + * @see Layout#getRightIndents() + * @see Layout.Builder#setRightIndents(int[]) + * @see StaticLayout.Builder#setIndents(int[], int[]) + */ + @NonNull + public Builder setLeftIndents(@Nullable int[] leftIndents) { + mLeftIndents = leftIndents; + return this; + } + + /** + * Set visually right indents in pixels per lines. + * + * For the lines past the last element in the array, the last element repeats. Passing null + * for disabling indents. + * + * Note that even with the RTL layout, this method reserve spacing at the visually right of + * the line. + * + * The default value is {@code null}. + * + * @param rightIndents array of indents values for the right margins in pixels. + * @return this builder instance. + * @see Layout#getLeftIndents() + * @see Layout#getRightIndents() + * @see Layout.Builder#setLeftIndents(int[]) + * @see StaticLayout.Builder#setIndents(int[], int[]) + */ + @NonNull + public Builder setRightIndents(@Nullable int[] rightIndents) { + mRightIndents = rightIndents; + return this; + } + + /** + * Set justification mode. + * + * When justification mode is {@link Layout#JUSTIFICATION_MODE_INTER_WORD}, the word spacing + * on the given Paint passed to the constructor will be ignored. This behavior also affects + * spans which change the word spacing. + * + * The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. + * + * @param justificationMode justification mode. + * @return this builder instance. + * @see Layout#getJustificationMode() + * @see StaticLayout.Builder#setJustificationMode(int) + * @see Layout#JUSTIFICATION_MODE_NONE + * @see Layout#JUSTIFICATION_MODE_INTER_WORD + */ + @NonNull + public Builder setJustificationMode(@JustificationMode int justificationMode) { + mJustificationMode = justificationMode; + return this; + } + + /** + * Set the line break configuration. + * + * The default value is a LinebreakConfig instance that has + * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE} and + * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE}. + * + * @param lineBreakConfig the line break configuration + * @return this builder instance. + * @see Layout#getLineBreakConfig() + * @see StaticLayout.Builder#setLineBreakConfig(LineBreakConfig) + */ + @NonNull + public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) { + mLineBreakConfig = lineBreakConfig; + return this; + } + + private BoringLayout.Metrics isBoring() { + if (mStart != 0 || mEnd != mText.length()) { // BoringLayout only support entire text. + return null; + } + BoringLayout.Metrics metrics = BoringLayout.isBoring(mText, mPaint, mTextDir, + mFallbackLineSpacing, null); + if (metrics == null) { + return null; + } + if (metrics.width <= mWidth) { + return metrics; + } + if (mEllipsize != null) { + return metrics; + } + return null; + } + + /** + * Build a Layout object. + */ + @NonNull + public Layout build() { + BoringLayout.Metrics metrics = isBoring(); + if (metrics == null) { // we cannot use BoringLayout, create StaticLayout. + return StaticLayout.Builder.obtain(mText, mStart, mEnd, mPaint, mWidth) + .setAlignment(mAlignment) + .setLineSpacing(mSpacingAdd, mSpacingMult) + .setTextDirection(mTextDir) + .setIncludePad(mIncludePad) + .setUseLineSpacingFromFallbacks(mFallbackLineSpacing) + .setEllipsizedWidth(mEllipsizedWidth) + .setEllipsize(mEllipsize) + .setMaxLines(mMaxLines) + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency) + .setIndents(mLeftIndents, mRightIndents) + .setJustificationMode(mJustificationMode) + .setLineBreakConfig(mLineBreakConfig) + .build(); + } else { + return new BoringLayout( + mText, mPaint, mWidth, mAlignment, mTextDir, mSpacingMult, mSpacingAdd, + mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines, + mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents, + mJustificationMode, mLineBreakConfig, metrics); + } + } + + private final CharSequence mText; + private final int mStart; + private final int mEnd; + private final TextPaint mPaint; + private final int mWidth; + private Alignment mAlignment = Alignment.ALIGN_NORMAL; + private float mSpacingMult = 1.0f; + private float mSpacingAdd = 0.0f; + private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; + private boolean mIncludePad = true; + private boolean mFallbackLineSpacing = false; + private int mEllipsizedWidth; + private TextUtils.TruncateAt mEllipsize = null; + private int mMaxLines = Integer.MAX_VALUE; + private int mBreakStrategy = BREAK_STRATEGY_SIMPLE; + private int mHyphenationFrequency = HYPHENATION_FREQUENCY_NONE; + private int[] mLeftIndents = null; + private int[] mRightIndents = null; + private int mJustificationMode = JUSTIFICATION_MODE_NONE; + private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Getters of parameters that is used for building Layout instance + /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Return the text used for creating this layout. + * + * @return the text used for creating this layout. + * @see Layout.Builder + */ + @NonNull + public final CharSequence getText() { + return mText; + } + + /** + * Return the paint used for creating this layout. + * + * Do not modify the returned paint object. This paint object will still be used for + * drawing/measuring text. + * + * @return the paint used for creating this layout. + * @see Layout.Builder + */ + @NonNull + public final TextPaint getPaint() { + return mPaint; + } + + /** + * Return the width used for creating this layout in pixels. + * + * @return the width used for creating this layout in pixels. + * @see Layout.Builder + */ + @IntRange(from = 0) + public final int getWidth() { + return mWidth; + } + + /** + * Returns the alignment used for creating this layout in pixels. + * + * @return the alignment used for creating this layout. + * @see Layout.Builder#setAlignment(Alignment) + * @see StaticLayout.Builder#setAlignment(Alignment) + */ + @NonNull + public final Alignment getAlignment() { + return mAlignment; + } + + /** + * Returns the text direction heuristic used for creating this layout. + * + * @return the text direction heuristic used for creating this layout + * @see Layout.Builder#setTextDirectionHeuristic(TextDirectionHeuristic) + * @see StaticLayout.Builder#setTextDirection(TextDirectionHeuristic) + */ + @NonNull + public final TextDirectionHeuristic getTextDirectionHeuristic() { + return mTextDir; + } + + /** + * Returns the multiplier applied to the line height. + * + * This is an alias of {@link #getLineSpacingMultiplier}. + * + * @return the line height multiplier. + * @see Layout.Builder#setLineSpacingMultiplier(float) + * @see StaticLayout.Builder#setLineSpacing(float, float) + * @see Layout#getLineSpacingMultiplier() + */ + public final float getSpacingMultiplier() { + return getLineSpacingMultiplier(); + } + + /** + * Returns the multiplier applied to the line height. + * + * @return the line height multiplier. + * @see Layout.Builder#setLineSpacingMultiplier(float) + * @see StaticLayout.Builder#setLineSpacing(float, float) + * @see Layout#getSpacingMultiplier() + */ + public final float getLineSpacingMultiplier() { + return mSpacingMult; + } + + /** + * Returns the amount added to the line height. + * + * This is an alias of {@link #getLineSpacingAmount()}. + * + * @return the line height additional amount. + * @see Layout.Builder#setLineSpacingAmount(float) + * @see StaticLayout.Builder#setLineSpacing(float, float) + * @see Layout#getLineSpacingAmount() + */ + public final float getSpacingAdd() { + return getLineSpacingAmount(); + } + + /** + * Returns the amount added to the line height. + * + * @return the line height additional amount. + * @see Layout.Builder#setLineSpacingAmount(float) + * @see StaticLayout.Builder#setLineSpacing(float, float) + * @see Layout#getSpacingAdd() + */ + public final float getLineSpacingAmount() { + return mSpacingAdd; + } + + /** + * Returns true if this layout is created with increased line height. + * + * @return true if the layout is created with increased line height. + * @see Layout.Builder#setIncludeFontPadding(boolean) + * @see StaticLayout.Builder#setIncludePad(boolean) + */ + public final boolean isIncludeFontPadding() { + return mIncludePad; + } + + /** + * Return true if the fallback line space is enabled in this Layout. + * + * @return true if the fallback line space is enabled. Otherwise, returns false. + * @see Layout.Builder#setFallbackLineSpacingEnabled(boolean) + * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean) + */ + // not being final because of already published API. + public boolean isFallbackLineSpacingEnabled() { + return mFallbackLineSpacing; + } + + /** + * Return the width to which this layout is ellipsized. + * + * If no ellipsize is applied, the same amount of {@link #getWidth} is returned. + * + * @return the amount of ellipsized width in pixels. + * @see Layout.Builder#setEllipsizedWidth(int) + * @see StaticLayout.Builder#setEllipsizedWidth(int) + * @see Layout.Builder#setEllipsize(TextUtils.TruncateAt) + * @see StaticLayout.Builder#setEllipsize(TextUtils.TruncateAt) + * @see Layout#getEllipsize() + */ + @IntRange(from = 0) + public int getEllipsizedWidth() { // not being final because of already published API. + return mEllipsizedWidth; + } + + /** + * Return the ellipsize option used for creating this layout. + * + * May return null if no ellipsize option was selected. + * + * @return The ellipsize option used for creating this layout, or null if no ellipsize option + * was selected. + * @see Layout.Builder#setEllipsize(TextUtils.TruncateAt) + * @see StaticLayout.Builder#setEllipsize(TextUtils.TruncateAt) + * @see Layout.Builder#setEllipsizedWidth(int) + * @see StaticLayout.Builder#setEllipsizedWidth(int) + * @see Layout#getEllipsizedWidth() + */ + @Nullable + public final TextUtils.TruncateAt getEllipsize() { + return mEllipsize; + } + + /** + * Return the maximum lines allowed used for creating this layout. + * + * Note that this is not an actual line count of this layout. Use {@link #getLineCount()} for + * getting the actual line count of this layout. + * + * @return the maximum lines allowed used for creating this layout. + * @see Layout.Builder#setMaxLines(int) + * @see StaticLayout.Builder#setMaxLines(int) + */ + @IntRange(from = 1) + public final int getMaxLines() { + return mMaxLines; + } + + /** + * Return the break strategy used for creating this layout. + * + * @return the break strategy used for creating this layout. + * @see Layout.Builder#setBreakStrategy(int) + * @see StaticLayout.Builder#setBreakStrategy(int) + */ + @BreakStrategy + public final int getBreakStrategy() { + return mBreakStrategy; + } + + /** + * Return the hyphenation frequency used for creating this layout. + * + * @return the hyphenation frequency used for creating this layout. + * @see Layout.Builder#setHyphenationFrequency(int) + * @see StaticLayout.Builder#setHyphenationFrequency(int) + */ + @HyphenationFrequency + public final int getHyphenationFrequency() { + return mHyphenationFrequency; + } + + /** + * Return a copy of the left indents used for this layout. + * + * May return null if no left indentation is applied. + * + * @return the array of left indents in pixels. + * @see Layout.Builder#setLeftIndents(int[]) + * @see Layout.Builder#setRightIndents(int[]) + * @see StaticLayout.Builder#setIndents(int[], int[]) + */ + @Nullable + public final int[] getLeftIndents() { + if (mLeftIndents == null) { + return null; + } + int[] newArray = new int[mLeftIndents.length]; + System.arraycopy(mLeftIndents, 0, newArray, 0, newArray.length); + return newArray; + } + + /** + * Return a copy of the right indents used for this layout. + * + * May return null if no right indentation is applied. + * + * @return the array of right indents in pixels. + * @see Layout.Builder#setLeftIndents(int[]) + * @see Layout.Builder#setRightIndents(int[]) + * @see StaticLayout.Builder#setIndents(int[], int[]) + */ + @Nullable + public final int[] getRightIndents() { + if (mRightIndents == null) { + return mLeftIndents; + } + int[] newArray = new int[mRightIndents.length]; + System.arraycopy(mRightIndents, 0, newArray, 0, newArray.length); + return newArray; + } + + /** + * Return the justification mode used for creating this layout. + * + * @return the justification mode used for creating this layout. + * @see Layout.Builder#setJustificationMode(int) + * @see StaticLayout.Builder#setJustificationMode(int) + */ + @JustificationMode + public final int getJustificationMode() { + return mJustificationMode; + } + + /** + * Gets the {@link LineBreakConfig} used for creating this layout. + * + * Do not modify the returned object. + * + * @return The line break config used for creating this layout. + */ + // not being final because of subclass has already published API. + @NonNull + public LineBreakConfig getLineBreakConfig() { + return mLineBreakConfig; + } } diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS index a6be687ab4c9..0935ffd9dd81 100644 --- a/core/java/android/text/OWNERS +++ b/core/java/android/text/OWNERS @@ -1,5 +1,6 @@ set noparent +grantapher@google.com halilibo@google.com haoyuchang@google.com justinghan@google.com diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index ab9cff078ac4..3d1895c3eaf3 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -431,11 +431,33 @@ public class StaticLayout extends Layout { */ @NonNull public StaticLayout build() { - StaticLayout result = new StaticLayout(this); + StaticLayout result = new StaticLayout(this, mIncludePad, mEllipsize != null + ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL); Builder.recycle(this); return result; } + /** + * DO NOT USE THIS METHOD OTHER THAN DynamicLayout. + * + * This class generates a very weird StaticLayout only for getting a result of line break. + * Since DynamicLayout keeps StaticLayout reference in the static context for object + * recycling but keeping text reference in static context will end up with leaking Context + * due to TextWatcher via TextView. + * + * So, this is a dirty work around that creating StaticLayout without passing text reference + * to the super constructor, but calculating the text layout by calling generate function + * directly. + */ + /* package */ @NonNull StaticLayout buildPartialStaticLayoutForDynamicLayout( + boolean trackpadding, StaticLayout recycle) { + if (recycle == null) { + recycle = new StaticLayout(); + } + recycle.generate(this, mIncludePad, trackpadding); + return recycle; + } + private CharSequence mText; private int mStart; private int mEnd; @@ -464,6 +486,37 @@ public class StaticLayout extends Layout { } /** + * DO NOT USE THIS CONSTRUCTOR OTHER THAN FOR DYNAMIC LAYOUT. + * See Builder#buildPartialStaticLayoutForDynamicLayout for the reason of this constructor. + */ + private StaticLayout() { + super( + null, // text + null, // paint + 0, // width + null, // alignment + null, // textDir + 1, // spacing multiplier + 0, // spacing amount + false, // include font padding + false, // fallback line spacing + 0, // ellipsized width + null, // ellipsize + 1, // maxLines + BREAK_STRATEGY_SIMPLE, + HYPHENATION_FREQUENCY_NONE, + null, // leftIndents + null, // rightIndents + JUSTIFICATION_MODE_NONE, + null // lineBreakConfig + ); + + mColumns = COLUMNS_ELLIPSIZE; + mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2); + mLines = ArrayUtils.newUnpaddedIntArray(2 * mColumns); + } + + /** * @deprecated Use {@link Builder} instead. */ @Deprecated @@ -515,88 +568,32 @@ public class StaticLayout extends Layout { float spacingmult, float spacingadd, boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) { - super((ellipsize == null) - ? source - : (source instanceof Spanned) - ? new SpannedEllipsizer(source) - : new Ellipsizer(source), - paint, outerwidth, align, textDir, spacingmult, spacingadd); - - Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth) - .setAlignment(align) - .setTextDirection(textDir) - .setLineSpacing(spacingadd, spacingmult) - .setIncludePad(includepad) - .setEllipsizedWidth(ellipsizedWidth) - .setEllipsize(ellipsize) - .setMaxLines(maxLines); - /* - * This is annoying, but we can't refer to the layout until superclass construction is - * finished, and the superclass constructor wants the reference to the display text. - * - * In other words, the two Ellipsizer classes in Layout.java need a (Dynamic|Static)Layout - * as a parameter to do their calculations, but the Ellipsizers also need to be the input - * to the superclass's constructor (Layout). In order to go around the circular - * dependency, we construct the Ellipsizer with only one of the parameters, the text. And - * we fill in the rest of the needed information (layout, width, and method) later, here. - * - * This will break if the superclass constructor ever actually cares about the content - * instead of just holding the reference. - */ - if (ellipsize != null) { - Ellipsizer e = (Ellipsizer) getText(); - - e.mLayout = this; - e.mWidth = ellipsizedWidth; - e.mMethod = ellipsize; - mEllipsizedWidth = ellipsizedWidth; - - mColumns = COLUMNS_ELLIPSIZE; - } else { - mColumns = COLUMNS_NORMAL; - mEllipsizedWidth = outerwidth; - } - - mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2); - mLines = ArrayUtils.newUnpaddedIntArray(2 * mColumns); - mMaximumVisibleLineCount = maxLines; - - generate(b, b.mIncludePad, b.mIncludePad); - - Builder.recycle(b); + this(Builder.obtain(source, bufstart, bufend, paint, outerwidth) + .setAlignment(align) + .setTextDirection(textDir) + .setLineSpacing(spacingadd, spacingmult) + .setIncludePad(includepad) + .setEllipsize(ellipsize) + .setEllipsizedWidth(ellipsizedWidth) + .setMaxLines(maxLines), includepad, + ellipsize != null ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL); } - /** - * Used by DynamicLayout. - */ - /* package */ StaticLayout(@Nullable CharSequence text) { - super(text, null, 0, null, 0, 0); - - mColumns = COLUMNS_ELLIPSIZE; - mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2); - mLines = ArrayUtils.newUnpaddedIntArray(2 * mColumns); - } - - private StaticLayout(Builder b) { - super((b.mEllipsize == null) - ? b.mText - : (b.mText instanceof Spanned) - ? new SpannedEllipsizer(b.mText) - : new Ellipsizer(b.mText), - b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd); + private StaticLayout(Builder b, boolean trackPadding, int columnSize) { + super((b.mEllipsize == null) ? b.mText : (b.mText instanceof Spanned) + ? new SpannedEllipsizer(b.mText) : new Ellipsizer(b.mText), + b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd, + b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize, + b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents, + b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig); + mColumns = columnSize; if (b.mEllipsize != null) { Ellipsizer e = (Ellipsizer) getText(); e.mLayout = this; e.mWidth = b.mEllipsizedWidth; e.mMethod = b.mEllipsize; - mEllipsizedWidth = b.mEllipsizedWidth; - - mColumns = COLUMNS_ELLIPSIZE; - } else { - mColumns = COLUMNS_NORMAL; - mEllipsizedWidth = b.mWidth; } mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2); @@ -605,9 +602,8 @@ public class StaticLayout extends Layout { mLeftIndents = b.mLeftIndents; mRightIndents = b.mRightIndents; - setJustificationMode(b.mJustificationMode); - generate(b, b.mIncludePad, b.mIncludePad); + generate(b, b.mIncludePad, trackPadding); } private static int getBaseHyphenationFrequency(int frequency) { @@ -648,7 +644,7 @@ public class StaticLayout extends Layout { mLineCount = 0; mEllipsized = false; mMaxLineHeight = mMaximumVisibleLineCount < 1 ? 0 : DEFAULT_MAX_LINE_HEIGHT; - mFallbackLineSpacing = b.mFallbackLineSpacing; + boolean isFallbackLineSpacing = b.mFallbackLineSpacing; int v = 0; boolean needMultiply = (spacingmult != 1 || spacingadd != 0); @@ -887,17 +883,17 @@ public class StaticLayout extends Layout { boolean moreChars = (endPos < bufEnd); - final int ascent = mFallbackLineSpacing + final int ascent = isFallbackLineSpacing ? Math.min(fmAscent, Math.round(ascents[breakIndex])) : fmAscent; - final int descent = mFallbackLineSpacing + final int descent = isFallbackLineSpacing ? Math.max(fmDescent, Math.round(descents[breakIndex])) : fmDescent; // The fallback ascent/descent may be larger than top/bottom of the default font // metrics. Adjust top/bottom with ascent/descent for avoiding unexpected // clipping. - if (mFallbackLineSpacing) { + if (isFallbackLineSpacing) { if (ascent < fmTop) { fmTop = ascent; } @@ -1410,16 +1406,6 @@ public class StaticLayout extends Layout { return mLines[mColumns * line + ELLIPSIS_START]; } - @Override - public int getEllipsizedWidth() { - return mEllipsizedWidth; - } - - @Override - public boolean isFallbackLineSpacingEnabled() { - return mFallbackLineSpacing; - } - /** * Return the total height of this layout. * @@ -1445,8 +1431,6 @@ public class StaticLayout extends Layout { private int mTopPadding, mBottomPadding; @UnsupportedAppUsage private int mColumns; - private int mEllipsizedWidth; - private boolean mFallbackLineSpacing; /** * Keeps track if ellipsize is applied to the text. diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index 751cd210b94c..6e73a3c93fd7 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -19,7 +19,10 @@ package android.view; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.graphics.Matrix; import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; import android.view.inputmethod.InputMethodManager; import android.widget.TextView; @@ -78,11 +81,17 @@ public class HandwritingInitiator { private int mConnectionCount = 0; private final InputMethodManager mImm; + private final RectF mTempRectF = new RectF(); + + private final Region mTempRegion = new Region(); + + private final Matrix mTempMatrix = new Matrix(); + /** * The handwrite-able View that is currently the target of a hovering stylus pointer. This is * used to help determine whether the handwriting PointerIcon should be shown in * {@link #onResolvePointerIcon(Context, MotionEvent)} so that we can reduce the number of calls - * to {@link #findBestCandidateView(float, float)}. + * to {@link #findBestCandidateView(float, float, boolean)}. */ @Nullable private WeakReference<View> mCachedHoverTarget = null; @@ -184,8 +193,8 @@ public class HandwritingInitiator { final float y = motionEvent.getY(pointerIndex); if (largerThanTouchSlop(x, y, mState.mStylusDownX, mState.mStylusDownY)) { mState.mExceedHandwritingSlop = true; - View candidateView = - findBestCandidateView(mState.mStylusDownX, mState.mStylusDownY); + View candidateView = findBestCandidateView(mState.mStylusDownX, + mState.mStylusDownY, /* isHover */ false); if (candidateView != null) { if (candidateView == getConnectedView()) { if (!candidateView.hasFocus()) { @@ -393,13 +402,14 @@ public class HandwritingInitiator { final View cachedHoverTarget = getCachedHoverTarget(); if (cachedHoverTarget != null) { final Rect handwritingArea = getViewHandwritingArea(cachedHoverTarget); - if (isInHandwritingArea(handwritingArea, hoverX, hoverY, cachedHoverTarget) + if (isInHandwritingArea(handwritingArea, hoverX, hoverY, cachedHoverTarget, + /* isHover */ true) && shouldTriggerStylusHandwritingForView(cachedHoverTarget)) { return cachedHoverTarget; } } - final View candidateView = findBestCandidateView(hoverX, hoverY); + final View candidateView = findBestCandidateView(hoverX, hoverY, /* isHover */ true); if (candidateView != null) { mCachedHoverTarget = new WeakReference<>(candidateView); @@ -429,14 +439,14 @@ public class HandwritingInitiator { * @param y the y coordinates of the stylus event, in the coordinates of the window. */ @Nullable - private View findBestCandidateView(float x, float y) { + private View findBestCandidateView(float x, float y, boolean isHover) { // If the connectedView is not null and do not set any handwriting area, it will check // whether the connectedView's boundary contains the initial stylus position. If true, // directly return the connectedView. final View connectedView = getConnectedView(); if (connectedView != null) { Rect handwritingArea = getViewHandwritingArea(connectedView); - if (isInHandwritingArea(handwritingArea, x, y, connectedView) + if (isInHandwritingArea(handwritingArea, x, y, connectedView, isHover) && shouldTriggerStylusHandwritingForView(connectedView)) { return connectedView; } @@ -450,7 +460,7 @@ public class HandwritingInitiator { for (HandwritableViewInfo viewInfo : handwritableViewInfos) { final View view = viewInfo.getView(); final Rect handwritingArea = viewInfo.getHandwritingArea(); - if (!isInHandwritingArea(handwritingArea, x, y, view) + if (!isInHandwritingArea(handwritingArea, x, y, view, isHover) || !shouldTriggerStylusHandwritingForView(view)) { continue; } @@ -546,15 +556,48 @@ public class HandwritingInitiator { * Return true if the (x, y) is inside by the given {@link Rect} with the View's * handwriting bounds with offsets applied. */ - private static boolean isInHandwritingArea(@Nullable Rect handwritingArea, - float x, float y, View view) { + private boolean isInHandwritingArea(@Nullable Rect handwritingArea, + float x, float y, View view, boolean isHover) { if (handwritingArea == null) return false; - return contains(handwritingArea, x, y, + if (!contains(handwritingArea, x, y, view.getHandwritingBoundsOffsetLeft(), view.getHandwritingBoundsOffsetTop(), view.getHandwritingBoundsOffsetRight(), - view.getHandwritingBoundsOffsetBottom()); + view.getHandwritingBoundsOffsetBottom())) { + return false; + } + + // The returned handwritingArea computed by ViewParent#getChildVisibleRect didn't consider + // the case where a view is stacking on top of the editor. (e.g. DrawerLayout, popup) + // We must check the hit region of the editor again, and avoid the case where another + // view on top of the editor is handling MotionEvents. + ViewParent parent = view.getParent(); + if (parent == null) { + return true; + } + + Region region = mTempRegion; + mTempRegion.set(0, 0, view.getWidth(), view.getHeight()); + Matrix matrix = mTempMatrix; + matrix.reset(); + if (!parent.getChildLocalHitRegion(view, region, matrix, isHover)) { + return false; + } + + // It's not easy to extend the region by the given handwritingBoundsOffset. Instead, we + // create a rectangle surrounding the motion event location and check if this rectangle + // overlaps with the hit region of the editor. + float left = x - view.getHandwritingBoundsOffsetRight(); + float top = y - view.getHandwritingBoundsOffsetBottom(); + float right = Math.max(x + view.getHandwritingBoundsOffsetLeft(), left + 1); + float bottom = Math.max(y + view.getHandwritingBoundsOffsetTop(), top + 1); + RectF rectF = mTempRectF; + rectF.set(left, top, right, bottom); + matrix.mapRect(rectF); + + return region.op(Math.round(rectF.left), Math.round(rectF.top), + Math.round(rectF.right), Math.round(rectF.bottom), Region.Op.INTERSECT); } /** diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 6c5f195ba2a0..fabfed39a913 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -28,7 +28,6 @@ import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION; import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS; import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH; import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX; -import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.AnimationType; import static android.view.InsetsController.DEBUG; import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; @@ -285,15 +284,11 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro return false; } final Insets offset = Insets.subtract(mShownInsets, mPendingInsets); - ArrayList<SurfaceParams> params = new ArrayList<>(); - updateLeashesForSide(ISIDE_LEFT, offset.left, mPendingInsets.left, params, outState, - mPendingAlpha); - updateLeashesForSide(ISIDE_TOP, offset.top, mPendingInsets.top, params, outState, - mPendingAlpha); - updateLeashesForSide(ISIDE_RIGHT, offset.right, mPendingInsets.right, params, outState, - mPendingAlpha); - updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params, outState, - mPendingAlpha); + final ArrayList<SurfaceParams> params = new ArrayList<>(); + updateLeashesForSide(ISIDE_LEFT, offset.left, params, outState, mPendingAlpha); + updateLeashesForSide(ISIDE_TOP, offset.top, params, outState, mPendingAlpha); + updateLeashesForSide(ISIDE_RIGHT, offset.right, params, outState, mPendingAlpha); + updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, params, outState, mPendingAlpha); mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()])); mCurrentInsets = mPendingInsets; @@ -457,7 +452,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha); } - private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset, + private void updateLeashesForSide(@InternalInsetsSide int side, int offset, ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha) { final ArraySet<InsetsSourceControl> controls = mSideControlsMap.get(side); if (controls == null) { @@ -475,9 +470,9 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro } addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame); - final boolean visible = mHasZeroInsetsIme && side == ISIDE_BOTTOM - ? (mAnimationType == ANIMATION_TYPE_SHOW || !mFinished) - : inset != 0; + final boolean visible = mPendingFraction == 0 && source != null + ? source.isVisible() + : !mFinished || mShownOnFinish; if (outState != null && source != null) { outState.addSource(new InsetsSource(source) diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 4ecfc4044b1d..8ec7d6779392 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -16,10 +16,12 @@ package android.view; +import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR; import static android.os.Trace.TRACE_TAG_VIEW; import static android.view.InsetsControllerProto.CONTROL; import static android.view.InsetsControllerProto.STATE; import static android.view.InsetsSource.ID_IME; +import static android.view.InsetsSource.ID_IME_CAPTION_BAR; import static android.view.ViewRootImpl.CAPTION_ON_SHELL; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.LAST; @@ -40,6 +42,7 @@ import android.app.ActivityThread; import android.content.Context; import android.content.res.CompatibilityInfo; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; import android.os.Handler; @@ -652,6 +655,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mLastWindowingMode; private boolean mStartingAnimation; private int mCaptionInsetsHeight = 0; + private int mImeCaptionBarInsetsHeight = 0; private boolean mAnimationsDisabled; private boolean mCompatSysUiVisibilityStaled; @@ -662,9 +666,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** Set of inset types for which an animation was started since last resetting this field */ private @InsetsType int mLastStartedAnimTypes; - /** Set of inset types which cannot be controlled by the user animation */ - private @InsetsType int mDisabledUserAnimationInsetsTypes; - /** Set of inset types which are existing */ private @InsetsType int mExistingTypes = 0; @@ -693,6 +694,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (!CAPTION_ON_SHELL && source1.getType() == captionBar()) { return; } + if (source1.getId() == ID_IME_CAPTION_BAR) { + return; + } // Don't change the indexes of the sources while traversing. Remove it later. mPendingRemoveIndexes.add(index1); @@ -823,6 +827,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (mFrame.equals(frame)) { return; } + if (mImeCaptionBarInsetsHeight != 0) { + setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight); + } mHost.notifyInsetsChanged(); mFrame.set(frame); } @@ -877,21 +884,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mState.set(newState, 0 /* types */); @InsetsType int existingTypes = 0; @InsetsType int visibleTypes = 0; - @InsetsType int disabledUserAnimationTypes = 0; @InsetsType int[] cancelledUserAnimationTypes = {0}; for (int i = 0, size = newState.sourceSize(); i < size; i++) { final InsetsSource source = newState.sourceAt(i); @InsetsType int type = source.getType(); @AnimationType int animationType = getAnimationType(type); - if (!source.isUserControllable()) { - // The user animation is not allowed when visible frame is empty. - disabledUserAnimationTypes |= type; - if (animationType == ANIMATION_TYPE_USER) { - // Existing user animation needs to be cancelled. - animationType = ANIMATION_TYPE_NONE; - cancelledUserAnimationTypes[0] |= type; - } - } final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId()); if (consumer != null) { consumer.updateSource(source, animationType); @@ -921,28 +918,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } InsetsState.traverse(mState, newState, mRemoveGoneSources); - updateDisabledUserAnimationTypes(disabledUserAnimationTypes); - if (cancelledUserAnimationTypes[0] != 0) { mHandler.post(() -> show(cancelledUserAnimationTypes[0])); } } - private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) { - @InsetsType int diff = mDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes; - if (diff != 0) { - for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { - InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); - if (consumer.getControl() != null && (consumer.getType() & diff) != 0) { - mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); - mHandler.post(mInvokeControllableInsetsChangedListeners); - break; - } - } - mDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes; - } - } - private boolean captionInsetsUnchanged() { if (CAPTION_ON_SHELL) { return false; @@ -1007,6 +987,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // Ensure to update all existing source consumers for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + if (consumer.getId() == ID_IME_CAPTION_BAR) { + // The inset control for the IME caption bar will never be dispatched + // by the server. + continue; + } + final InsetsSourceControl control = mTmpControlArray.get(consumer.getId()); if (control != null) { controllableTypes |= control.getType(); @@ -1316,26 +1302,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation + " while an existing " + Type.toString(mTypesBeingCancelled) + " is being cancelled."); } - if (animationType == ANIMATION_TYPE_USER) { - final @InsetsType int disabledTypes = types & mDisabledUserAnimationInsetsTypes; - if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes); - types &= ~mDisabledUserAnimationInsetsTypes; - - if ((disabledTypes & ime()) != 0) { - ImeTracker.forLogging().onFailed(statsToken, - ImeTracker.PHASE_CLIENT_DISABLED_USER_ANIMATION); - - if (fromIme - && !mState.isSourceOrDefaultVisible(mImeSourceConsumer.getId(), ime())) { - // We've requested IMM to show IME, but the IME is not controllable. We need to - // cancel the request. - setRequestedVisibleTypes(0 /* visibleTypes */, ime()); - if (mImeSourceConsumer.onAnimationStateChanged(false /* running */)) { - notifyVisibilityChanged(); - } - } - } - } if (types == 0) { // nothing to animate. listener.onCancelled(null); @@ -1499,7 +1465,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation continue; } final InsetsSourceControl control = consumer.getControl(); - if (control != null && control.getLeash() != null) { + if (control != null + && (control.getLeash() != null || control.getId() == ID_IME_CAPTION_BAR)) { controls.put(control.getId(), new InsetsSourceControl(control)); typesReady |= consumer.getType(); } @@ -1885,6 +1852,35 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } @Override + public void setImeCaptionBarInsetsHeight(int height) { + if (!ENABLE_HIDE_IME_CAPTION_BAR) { + return; + } + Rect newFrame = new Rect(mFrame.left, mFrame.bottom - height, mFrame.right, mFrame.bottom); + InsetsSource source = mState.peekSource(ID_IME_CAPTION_BAR); + if (mImeCaptionBarInsetsHeight != height + || (source != null && !newFrame.equals(source.getFrame()))) { + mImeCaptionBarInsetsHeight = height; + if (mImeCaptionBarInsetsHeight != 0) { + mState.getOrCreateSource(ID_IME_CAPTION_BAR, captionBar()) + .setFrame(newFrame); + getSourceConsumer(ID_IME_CAPTION_BAR, captionBar()).setControl( + new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(), + null /* leash */, false /* initialVisible */, + new Point(), Insets.NONE), + new int[1], new int[1]); + } else { + mState.removeSource(ID_IME_CAPTION_BAR); + InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR); + if (sourceConsumer != null) { + sourceConsumer.setControl(null, new int[1], new int[1]); + } + } + mHost.notifyInsetsChanged(); + } + } + + @Override public void setSystemBarsBehavior(@Behavior int behavior) { mHost.setSystemBarsBehavior(behavior); } @@ -1908,7 +1904,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); InsetsSource source = mState.peekSource(consumer.getId()); - if (consumer.getControl() != null && source != null && source.isUserControllable()) { + if (consumer.getControl() != null && source != null) { result |= consumer.getType(); } } diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 64411866f020..0d5704eed4b3 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -20,6 +20,7 @@ import static android.view.InsetsSourceProto.FRAME; import static android.view.InsetsSourceProto.TYPE; import static android.view.InsetsSourceProto.VISIBLE; import static android.view.InsetsSourceProto.VISIBLE_FRAME; +import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.ime; import android.annotation.IntDef; @@ -47,6 +48,9 @@ public class InsetsSource implements Parcelable { /** The insets source ID of IME */ public static final int ID_IME = createId(null, 0, ime()); + /** The insets source ID of the IME caption bar ("fake" IME navigation bar). */ + static final int ID_IME_CAPTION_BAR = + InsetsSource.createId(null /* owner */, 1 /* index */, captionBar()); /** * Controls whether this source suppresses the scrim. If the scrim is ignored, the system won't @@ -183,11 +187,6 @@ public class InsetsSource implements Parcelable { return (mFlags & flags) == flags; } - boolean isUserControllable() { - // If mVisibleFrame is null, it will be the same area as mFrame. - return mVisibleFrame == null || !mVisibleFrame.isEmpty(); - } - /** * Calculates the insets this source will cause to a client window. * @@ -215,8 +214,12 @@ public class InsetsSource implements Parcelable { // During drag-move and drag-resizing, the caption insets position may not get updated // before the app frame get updated. To layout the app content correctly during drag events, // we always return the insets with the corresponding height covering the top. + // However, with the "fake" IME navigation bar treated as a caption bar, we return the + // insets with the corresponding height the bottom. if (getType() == WindowInsets.Type.captionBar()) { - return Insets.of(0, frame.height(), 0, 0); + return getId() == ID_IME_CAPTION_BAR + ? Insets.of(0, 0, 0, frame.height()) + : Insets.of(0, frame.height(), 0, 0); } // Checks for whether there is shared edge with insets for 0-width/height window. final boolean hasIntersection = relativeFrame.isEmpty() diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index e8f62fc0963f..a4cbc52416b3 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -44,6 +44,7 @@ public class PendingInsetsController implements WindowInsetsController { private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners = new ArrayList<>(); private int mCaptionInsetsHeight = 0; + private int mImeCaptionBarInsetsHeight = 0; private WindowInsetsAnimationControlListener mLoggingListener; private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible(); @@ -91,6 +92,11 @@ public class PendingInsetsController implements WindowInsetsController { } @Override + public void setImeCaptionBarInsetsHeight(int height) { + mImeCaptionBarInsetsHeight = height; + } + + @Override public void setSystemBarsBehavior(int behavior) { if (mReplayedInsetsController != null) { mReplayedInsetsController.setSystemBarsBehavior(behavior); @@ -168,6 +174,9 @@ public class PendingInsetsController implements WindowInsetsController { if (mCaptionInsetsHeight != 0) { controller.setCaptionInsetsHeight(mCaptionInsetsHeight); } + if (mImeCaptionBarInsetsHeight != 0) { + controller.setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight); + } if (mAnimationsDisabled) { controller.setAnimationsDisabled(true); } diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 4464d1918565..d31f82398cdf 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.IntDef; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.input.InputManagerGlobal; +import android.os.IInputConstants; import android.util.ArrayMap; import android.util.Pools.SynchronizedPool; @@ -53,11 +54,13 @@ public final class VelocityTracker { public @interface VelocityTrackableMotionEventAxis {} /** - * Velocity Tracker Strategy: Invalid. + * Use the default Velocity Tracker Strategy. Different axes may use different default + * strategies. * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1; + public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT = + IInputConstants.VELOCITY_TRACKER_STRATEGY_DEFAULT; /** * Velocity Tracker Strategy: Impulse. @@ -66,7 +69,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0; + public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE = + IInputConstants.VELOCITY_TRACKER_STRATEGY_IMPULSE; /** * Velocity Tracker Strategy: LSQ1. @@ -77,7 +81,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1; + public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 = + IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ1; /** * Velocity Tracker Strategy: LSQ2. @@ -88,7 +93,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2; + public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 = + IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ2; /** * Velocity Tracker Strategy: LSQ3. @@ -98,7 +104,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3; + public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 = + IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ3; /** * Velocity Tracker Strategy: WLSQ2_DELTA. @@ -106,7 +113,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4; + public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = + IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA; /** * Velocity Tracker Strategy: WLSQ2_CENTRAL. @@ -114,7 +122,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5; + public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = + IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL; /** * Velocity Tracker Strategy: WLSQ2_RECENT. @@ -122,7 +131,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6; + public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = + IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT; /** * Velocity Tracker Strategy: INT1. @@ -134,7 +144,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_INT1 = 7; + public static final int VELOCITY_TRACKER_STRATEGY_INT1 = + IInputConstants.VELOCITY_TRACKER_STRATEGY_INT1; /** * Velocity Tracker Strategy: INT2. @@ -144,7 +155,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_INT2 = 8; + public static final int VELOCITY_TRACKER_STRATEGY_INT2 = + IInputConstants.VELOCITY_TRACKER_STRATEGY_INT2; /** * Velocity Tracker Strategy: Legacy. @@ -155,7 +167,8 @@ public final class VelocityTracker { * * @hide */ - public static final int VELOCITY_TRACKER_STRATEGY_LEGACY = 9; + public static final int VELOCITY_TRACKER_STRATEGY_LEGACY = + IInputConstants.VELOCITY_TRACKER_STRATEGY_LEGACY; /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 92509c969055..5e19c675bb88 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9279,8 +9279,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } while (parentGroup != null && !parentGroup.isImportantForAutofill()) { - ignoredParentLeft += parentGroup.mLeft; - ignoredParentTop += parentGroup.mTop; + ignoredParentLeft += parentGroup.mLeft - parentGroup.mScrollX; + ignoredParentTop += parentGroup.mTop - parentGroup.mScrollY; viewParent = parentGroup.getParent(); if (viewParent instanceof View) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1b1098d9d57a..7bdff8c5b858 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -7361,6 +7361,90 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * @hide + */ + @Override + public boolean getChildLocalHitRegion(@NonNull View child, @NonNull Region region, + @NonNull Matrix matrix, boolean isHover) { + if (!child.hasIdentityMatrix()) { + matrix.preConcat(child.getInverseMatrix()); + } + + final int dx = child.mLeft - mScrollX; + final int dy = child.mTop - mScrollY; + matrix.preTranslate(-dx, -dy); + + final int width = mRight - mLeft; + final int height = mBottom - mTop; + + // Map the bounds of this view into the region's coordinates and clip the region. + final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF(); + rect.set(0, 0, width, height); + matrix.mapRect(rect); + + boolean notEmpty = region.op(Math.round(rect.left), Math.round(rect.top), + Math.round(rect.right), Math.round(rect.bottom), Region.Op.INTERSECT); + + if (isHover) { + HoverTarget target = mFirstHoverTarget; + boolean childIsHit = false; + while (target != null) { + final HoverTarget next = target.next; + if (target.child == child) { + childIsHit = true; + break; + } + target = next; + } + if (!childIsHit) { + target = mFirstHoverTarget; + while (notEmpty && target != null) { + final HoverTarget next = target.next; + final View hoveredView = target.child; + + rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight, + hoveredView.mBottom); + matrix.mapRect(rect); + notEmpty = region.op(Math.round(rect.left), Math.round(rect.top), + Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE); + target = next; + } + } + } else { + TouchTarget target = mFirstTouchTarget; + boolean childIsHit = false; + while (target != null) { + final TouchTarget next = target.next; + if (target.child == child) { + childIsHit = true; + break; + } + target = next; + } + if (!childIsHit) { + target = mFirstTouchTarget; + while (notEmpty && target != null) { + final TouchTarget next = target.next; + final View touchedView = target.child; + + rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight, + touchedView.mBottom); + matrix.mapRect(rect); + notEmpty = region.op(Math.round(rect.left), Math.round(rect.top), + Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE); + target = next; + } + } + } + + if (notEmpty && mParent != null) { + notEmpty = mParent.getChildLocalHitRegion(this, region, matrix, isHover); + } + return notEmpty; + } + + private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) { final int[] locationInWindow = new int[2]; view.getLocationInWindow(locationInWindow); diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 1020d2ef02be..54bc3484d295 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -18,6 +18,7 @@ package android.view; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; @@ -686,6 +687,36 @@ public interface ViewParent { } /** + * Compute the region where the child can receive the {@link MotionEvent}s from the root view. + * + * <p> Given region where the child will accept {@link MotionEvent}s. + * Modify the region to the unblocked region where the child can receive the + * {@link MotionEvent}s from the view root. + * </p> + * + * <p> The given region is always clipped by the bounds of the parent views. When there are + * on-going {@link MotionEvent}s, this method also makes use of the event dispatching results to + * determine whether a sibling view will also block the child's hit region. + * </p> + * + * @param child a child View, whose hit region we want to compute. + * @param region the initial hit region where the child view will handle {@link MotionEvent}s, + * defined in the child coordinates. Will be overwritten to the result hit region. + * @param matrix the matrix that maps the given child view's coordinates to the region + * coordinates. It will be modified to a matrix that maps window coordinates to + * the result region's coordinates. + * @param isHover if true it will return the hover events' hit region, otherwise it will + * return the touch events' hit region. + * @return true if the returned region is not empty. + * @hide + */ + default boolean getChildLocalHitRegion(@NonNull View child, @NonNull Region region, + @NonNull Matrix matrix, boolean isHover) { + region.setEmpty(); + return false; + } + + /** * Unbuffered dispatch has been requested by a child of this view parent. * This method is called by the View hierarchy to signal ancestors that a View needs to * request unbuffered dispatch. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3aa610af60b0..ddd3269925a4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -128,6 +128,7 @@ import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.RecordingCanvas; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; @@ -321,13 +322,6 @@ public final class ViewRootImpl implements ViewParent, SystemProperties.getBoolean("persist.wm.debug.client_immersive_confirmation", false); /** - * Whether the client should compute the window frame on its own. - * @hide - */ - public static final boolean LOCAL_LAYOUT = - SystemProperties.getBoolean("persist.debug.local_layout", true); - - /** * Set this system property to true to force the view hierarchy to render * at 60 Hz. This can be used to measure the potential framerate. */ @@ -1911,8 +1905,8 @@ public final class ViewRootImpl implements ViewParent, final float compatScale = frames.compatScale; final boolean frameChanged = !mWinFrame.equals(frame); final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration); - final boolean attachedFrameChanged = LOCAL_LAYOUT - && !Objects.equals(mTmpFrames.attachedFrame, attachedFrame); + final boolean attachedFrameChanged = + !Objects.equals(mTmpFrames.attachedFrame, attachedFrame); final boolean displayChanged = mDisplay.getDisplayId() != displayId; final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale; final boolean dragResizingChanged = mPendingDragResizing != dragResizing; @@ -2397,6 +2391,22 @@ public final class ViewRootImpl implements ViewParent, } @Override + public boolean getChildLocalHitRegion(@NonNull View child, @NonNull Region region, + @NonNull Matrix matrix, boolean isHover) { + if (child != mView) { + throw new IllegalArgumentException("child " + child + " is not the root view " + + mView + " managed by this ViewRootImpl"); + } + + RectF rectF = new RectF(0, 0, mWidth, mHeight); + matrix.mapRect(rectF); + // Note: don't apply scroll offset, because we want to know its + // visibility in the virtual canvas being given to the view hierarchy. + return region.op(Math.round(rectF.left), Math.round(rectF.top), + Math.round(rectF.right), Math.round(rectF.bottom), Region.Op.INTERSECT); + } + + @Override public void bringChildToFront(View child) { } @@ -8292,8 +8302,7 @@ public final class ViewRootImpl implements ViewParent, final int measuredWidth = mMeasuredWidth; final int measuredHeight = mMeasuredHeight; final boolean relayoutAsync; - if (LOCAL_LAYOUT - && (mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0 + if ((mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0 && mWindowAttributes.type != TYPE_APPLICATION_STARTING && mSyncSeqId <= mLastSyncSeqId && winConfigFromAm.diff(winConfigFromWm, false /* compareUndefined */) == 0) { diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index bc0bab7b5e95..cc2cd7982841 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -250,6 +250,16 @@ public interface WindowInsetsController { void setCaptionInsetsHeight(int height); /** + * Sets the insets height for the IME caption bar, which corresponds to the + * "fake" IME navigation bar. + * + * @param height the insets height of the IME caption bar. + * @hide + */ + default void setImeCaptionBarInsetsHeight(int height) { + } + + /** * Controls the behavior of system bars. * * @param behavior Determines how the bars behave when being hidden by the application. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 5d1a81fbb566..62e37a4f50d2 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1302,7 +1302,7 @@ public interface WindowManager extends ViewManager { * ratio or orientation specified in the app manifest. * * <p>The aspect ratio compatibility override is exposed to users in device - * settings. A menu in device settings lists all apps that don't opt out of + * settings. A menu in device settings lists all apps that have not opted out of * the compatibility override. Users select apps from the menu and set the * app aspect ratio on a per-app basis. Typically, the menu is available * only on large screen devices. @@ -1347,11 +1347,11 @@ public interface WindowManager extends ViewManager { * Application level * {@link android.content.pm.PackageManager.Property PackageManager.Property} * tag that (when set to false) informs the system the app has opted out of the - * full-screen option of the aspect ratio compatibility override. (For - * background information about the aspect ratio compatibility override, see + * full-screen option of the user aspect ratio compatibility override settings. (For + * background information about the user aspect ratio compatibility override, see * {@link #PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE}.) * - * <p>When users apply the aspect ratio compatibility override, the orientation + * <p>When users apply the full-screen compatibility override, the orientation * of the activity is forced to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}. * * <p>The user override is intended to improve the app experience on devices @@ -3839,6 +3839,7 @@ public interface WindowManager extends ViewManager { * @see #ROTATION_ANIMATION_ROTATE * @see #ROTATION_ANIMATION_CROSSFADE * @see #ROTATION_ANIMATION_JUMPCUT + * @see #ROTATION_ANIMATION_SEAMLESS */ public int rotationAnimation = ROTATION_ANIMATION_ROTATE; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7a96fd2a04df..e1de05bda9df 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -6948,6 +6948,7 @@ public class RemoteViews implements Parcelable, Filter { // something is going to start. opts.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + opts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); return Pair.create(intent, opts); } } diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl index 380118846dc7..ba87caa0697c 100644 --- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl +++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl @@ -22,6 +22,7 @@ import android.service.voice.HotwordDetectionServiceFailure; import android.service.voice.HotwordRejectedResult; import android.service.voice.SoundTriggerFailure; import android.service.voice.VisualQueryDetectionServiceFailure; +import com.android.internal.infra.AndroidFuture; /** * @hide @@ -113,4 +114,9 @@ oneway interface IHotwordRecognitionStatusCallback { /** Called when the hotword detection process is restarted */ void onProcessRestarted(); + + /** + * Called when a file open request is sent. + */ + void onOpenFile(in String filename, in AndroidFuture future); } diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index a5e775a461a9..1e0b2a0263cf 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -297,7 +297,7 @@ public class SuspendedAppActivity extends AlertActivity final IPackageManager ipm = AppGlobals.getPackageManager(); try { final String[] errored = ipm.setPackagesSuspendedAsUser( - new String[]{mSuspendedPackage}, false, null, null, null, + new String[]{mSuspendedPackage}, false, null, null, null, 0, mSuspendingPackage, mUserId); if (ArrayUtils.contains(errored, mSuspendedPackage)) { Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage); diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 0a938ef4d81c..79152b4b618f 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -185,7 +185,7 @@ public class BatteryStatsHistory { private boolean mHaveBatteryLevel; private boolean mRecordingHistory; - private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe; + static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe; private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024; private final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>(); @@ -1872,6 +1872,7 @@ public class BatteryStatsHistory { } return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; } else { + tag.poolIdx = HistoryTag.HISTORY_TAG_POOL_OVERFLOW; // Tag pool overflow: include the tag itself in the parcel return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; } diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java index ccc3454624f8..4c2b2854df88 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java @@ -309,7 +309,11 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag(); tag.readFromParcel(src); tag.poolIdx = index & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG; - mHistoryTags.put(tag.poolIdx, tag); + if (tag.poolIdx < BatteryStatsHistory.HISTORY_TAG_INDEX_LIMIT) { + mHistoryTags.put(tag.poolIdx, tag); + } else { + tag.poolIdx = BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW; + } outTag.setTo(tag); } else { diff --git a/core/java/com/android/internal/widget/BigPictureNotificationImageView.java b/core/java/com/android/internal/widget/BigPictureNotificationImageView.java index 3a7cf74df4a8..f95f5db8358e 100644 --- a/core/java/com/android/internal/widget/BigPictureNotificationImageView.java +++ b/core/java/com/android/internal/widget/BigPictureNotificationImageView.java @@ -37,13 +37,16 @@ import com.android.internal.R; * Icon.loadDrawable(). */ @RemoteViews.RemoteView -public class BigPictureNotificationImageView extends ImageView { +public class BigPictureNotificationImageView extends ImageView implements + NotificationDrawableConsumer { private static final String TAG = BigPictureNotificationImageView.class.getSimpleName(); private final int mMaximumDrawableWidth; private final int mMaximumDrawableHeight; + private NotificationIconManager mIconManager; + public BigPictureNotificationImageView(@NonNull Context context) { this(context, null, 0, 0); } @@ -69,6 +72,19 @@ public class BigPictureNotificationImageView extends ImageView { : R.dimen.notification_big_picture_max_height); } + + /** + * Sets an {@link NotificationIconManager} on this ImageView, which handles the loading of + * icons, instead of using the {@link LocalImageResolver} directly. + * If set, it overrides the behaviour of {@link #setImageIconAsync} and {@link #setImageIcon}, + * and it expects that the content of this imageView is only updated calling these two methods. + * + * @param iconManager to be called, when the icon is updated + */ + public void setIconManager(NotificationIconManager iconManager) { + mIconManager = iconManager; + } + @Override @android.view.RemotableViewMethod(asyncImpl = "setImageURIAsync") public void setImageURI(@Nullable Uri uri) { @@ -84,11 +100,20 @@ public class BigPictureNotificationImageView extends ImageView { @Override @android.view.RemotableViewMethod(asyncImpl = "setImageIconAsync") public void setImageIcon(@Nullable Icon icon) { + if (mIconManager != null) { + mIconManager.updateIcon(this, icon).run(); + return; + } + // old code path setImageDrawable(loadImage(icon)); } /** @hide **/ public Runnable setImageIconAsync(@Nullable Icon icon) { + if (mIconManager != null) { + return mIconManager.updateIcon(this, icon); + } + // old code path final Drawable drawable = loadImage(icon); return () -> setImageDrawable(drawable); } diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 7dda91d7b25e..42be784d8baa 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -149,6 +149,7 @@ public class ConversationLayout extends FrameLayout private View mAppNameDivider; private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this); private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>(); + private boolean mPrecomputedTextEnabled = false; public ConversationLayout(@NonNull Context context) { super(context); @@ -389,34 +390,37 @@ public class ConversationLayout extends FrameLayout */ @RemotableViewMethod(asyncImpl = "setDataAsync") public void setData(Bundle extras) { + bind(parseMessagingData(extras, /* usePrecomputedText= */ false)); + } + + @NonNull + private MessagingData parseMessagingData(Bundle extras, boolean usePrecomputedText) { Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); - List<Notification.MessagingStyle.Message> newMessages - = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); + List<Notification.MessagingStyle.Message> newMessages = + Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES); - List<Notification.MessagingStyle.Message> newHistoricMessages - = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages); + List<Notification.MessagingStyle.Message> newHistoricMessages = + Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages); // mUser now set (would be nice to avoid the side effect but WHATEVER) final Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, Person.class); // Append remote input history to newMessages (again, side effect is lame but WHATEVS) RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) - extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, android.app.RemoteInputHistoryItem.class); + extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + RemoteInputHistoryItem.class); addRemoteInputHistoryToMessages(newMessages, history); boolean showSpinner = extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); int unreadCount = extras.getInt(Notification.EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT); - // convert MessagingStyle.Message to MessagingMessage, re-using ones from a previous binding - // if they exist final List<MessagingMessage> newMessagingMessages = - createMessages(newMessages, false /* isHistoric */); + createMessages(newMessages, /* isHistoric= */false, usePrecomputedText); final List<MessagingMessage> newHistoricMessagingMessages = - createMessages(newHistoricMessages, true /* isHistoric */); - // bind it, baby - bindViews(user, showSpinner, unreadCount, - newMessagingMessages, - newHistoricMessagingMessages); + createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText); + + return new MessagingData(user, showSpinner, unreadCount, + newHistoricMessagingMessages, newMessagingMessages); } /** @@ -428,7 +432,33 @@ public class ConversationLayout extends FrameLayout */ @NonNull public Runnable setDataAsync(Bundle extras) { - return () -> setData(extras); + if (!mPrecomputedTextEnabled) { + return () -> setData(extras); + } + + final MessagingData messagingData = + parseMessagingData(extras, /* usePrecomputedText= */ true); + + return () -> { + finalizeInflate(messagingData.getHistoricMessagingMessages()); + finalizeInflate(messagingData.getNewMessagingMessages()); + + bind(messagingData); + }; + } + + /** + * enable/disable precomputed text usage + * @hide + */ + public void setPrecomputedTextEnabled(boolean precomputedTextEnabled) { + mPrecomputedTextEnabled = precomputedTextEnabled; + } + + private void finalizeInflate(List<MessagingMessage> historicMessagingMessages) { + for (MessagingMessage messagingMessage : historicMessagingMessages) { + messagingMessage.finalizeInflate(); + } } @Override @@ -458,17 +488,12 @@ public class ConversationLayout extends FrameLayout } } + private void bind(MessagingData messagingData) { + setUser(messagingData.getUser()); + setUnreadCount(messagingData.getUnreadCount()); - private void bindViews(Person user, - boolean showSpinner, int unreadCount, List<MessagingMessage> newMessagingMessages, - List<MessagingMessage> newHistoricMessagingMessages) { - setUser(user); - setUnreadCount(unreadCount); - bind(showSpinner, newMessagingMessages, newHistoricMessagingMessages); - } - - private void bind(boolean showSpinner, List<MessagingMessage> messages, - List<MessagingMessage> historicMessages) { + List<MessagingMessage> messages = messagingData.getNewMessagingMessages(); + List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages(); // Copy our groups, before they get clobbered ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups); @@ -481,7 +506,7 @@ public class ConversationLayout extends FrameLayout // Let's now create the views and reorder them accordingly // side-effect: updates mGroups, mAddedGroups - createGroupViews(groups, senders, showSpinner); + createGroupViews(groups, senders, messagingData.getShowSpinner()); // Let's first check which groups were removed altogether and remove them in one animation removeGroups(oldGroups); @@ -583,7 +608,7 @@ public class ConversationLayout extends FrameLayout // When collapsed, we're displaying the image message in a dedicated container // on the right of the layout instead of inline. Let's add the isolated image there - MessagingGroup messagingGroup = mGroups.get(mGroups.size() -1); + MessagingGroup messagingGroup = mGroups.get(mGroups.size() - 1); MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage(); if (isolatedMessage != null) { newMessage = isolatedMessage.getView(); @@ -981,15 +1006,17 @@ public class ConversationLayout extends FrameLayout * @param newMessages the messages to parse. */ private List<MessagingMessage> createMessages( - List<Notification.MessagingStyle.Message> newMessages, boolean historic) { + List<Notification.MessagingStyle.Message> newMessages, boolean isHistoric, + boolean usePrecomputedText) { List<MessagingMessage> result = new ArrayList<>(); for (int i = 0; i < newMessages.size(); i++) { Notification.MessagingStyle.Message m = newMessages.get(i); MessagingMessage message = findAndRemoveMatchingMessage(m); if (message == null) { - message = MessagingMessage.createMessage(this, m, mImageResolver); + message = MessagingMessage.createMessage(this, m, + mImageResolver, usePrecomputedText); } - message.setIsHistoric(historic); + message.setIsHistoric(isHistoric); result.add(message); } return result; @@ -1038,7 +1065,7 @@ public class ConversationLayout extends FrameLayout } if (visibleChildren > 0 && group.getVisibility() == GONE) { group.setVisibility(VISIBLE); - } else if (visibleChildren == 0 && group.getVisibility() != GONE) { + } else if (visibleChildren == 0 && group.getVisibility() != GONE) { group.setVisibility(GONE); } } @@ -1255,7 +1282,7 @@ public class ConversationLayout extends FrameLayout public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); - for (TouchDelegate delegate: mDelegates) { + for (TouchDelegate delegate : mDelegates) { event.setLocation(x, y); if (delegate.onTouchEvent(event)) { return true; diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java new file mode 100644 index 000000000000..85b02018e7c7 --- /dev/null +++ b/core/java/com/android/internal/widget/MessagingData.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.app.Person; + +import java.util.List; + +/** + * @hide + */ +final class MessagingData { + private final Person mUser; + private final boolean mShowSpinner; + private final List<MessagingMessage> mHistoricMessagingMessages; + private final List<MessagingMessage> mNewMessagingMessages; + private final int mUnreadCount; + + MessagingData(Person user, boolean showSpinner, + List<MessagingMessage> historicMessagingMessages, + List<MessagingMessage> newMessagingMessages) { + this(user, showSpinner, /* unreadCount= */0, + historicMessagingMessages, newMessagingMessages); + } + + MessagingData(Person user, boolean showSpinner, + int unreadCount, + List<MessagingMessage> historicMessagingMessages, + List<MessagingMessage> newMessagingMessages) { + mUser = user; + mShowSpinner = showSpinner; + mUnreadCount = unreadCount; + mHistoricMessagingMessages = historicMessagingMessages; + mNewMessagingMessages = newMessagingMessages; + } + + public Person getUser() { + return mUser; + } + + public boolean getShowSpinner() { + return mShowSpinner; + } + + public List<MessagingMessage> getHistoricMessagingMessages() { + return mHistoricMessagingMessages; + } + + public List<MessagingMessage> getNewMessagingMessages() { + return mNewMessagingMessages; + } + + public int getUnreadCount() { + return mUnreadCount; + } +} diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java index 098bce14e619..c132d6a90f6c 100644 --- a/core/java/com/android/internal/widget/MessagingImageMessage.java +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -93,8 +93,9 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage } @Override - public boolean setMessage(Notification.MessagingStyle.Message message) { - MessagingMessage.super.setMessage(message); + public boolean setMessage(Notification.MessagingStyle.Message message, + boolean usePrecomputedText) { + MessagingMessage.super.setMessage(message, usePrecomputedText); Drawable drawable; try { Uri uri = message.getDataUri(); @@ -114,32 +115,42 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage } mDrawable = drawable; mAspectRatio = ((float) mDrawable.getIntrinsicWidth()) / intrinsicHeight; - setImageDrawable(drawable); - setContentDescription(message.getText()); + if (!usePrecomputedText) { + finalizeInflate(); + } return true; } static MessagingMessage createMessage(IMessagingLayout layout, - Notification.MessagingStyle.Message m, ImageResolver resolver) { + Notification.MessagingStyle.Message m, ImageResolver resolver, + boolean usePrecomputedText) { MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); MessagingImageMessage createdMessage = sInstancePool.acquire(); if (createdMessage == null) { createdMessage = (MessagingImageMessage) LayoutInflater.from( layout.getContext()).inflate( - R.layout.notification_template_messaging_image_message, - messagingLinearLayout, - false); + R.layout.notification_template_messaging_image_message, + messagingLinearLayout, + false); createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); } createdMessage.setImageResolver(resolver); - boolean created = createdMessage.setMessage(m); - if (!created) { + // MessagingImageMessage does not use usePrecomputedText. + boolean populated = createdMessage.setMessage(m, /* usePrecomputedText= */false); + if (!populated) { createdMessage.recycle(); - return MessagingTextMessage.createMessage(layout, m); + return MessagingTextMessage.createMessage(layout, m, usePrecomputedText); } return createdMessage; } + + @Override + public void finalizeInflate() { + setImageDrawable(mDrawable); + setContentDescription(getMessage().getText()); + } + private void setImageResolver(ImageResolver resolver) { mImageResolver = resolver; } diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 8345c5cc9ef9..b6d7503119fe 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -87,7 +87,7 @@ public class MessagingLayout extends FrameLayout private ImageResolver mImageResolver; private CharSequence mConversationTitle; private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>(); - + private boolean mPrecomputedTextEnabled = false; public MessagingLayout(@NonNull Context context) { super(context); } @@ -162,15 +162,23 @@ public class MessagingLayout extends FrameLayout */ @RemotableViewMethod(asyncImpl = "setDataAsync") public void setData(Bundle extras) { + bind(parseMessagingData(extras, /* usePrecomputedText= */false)); + } + + @NonNull + private MessagingData parseMessagingData(Bundle extras, boolean usePrecomputedText) { Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); - List<Notification.MessagingStyle.Message> newMessages - = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); + List<Notification.MessagingStyle.Message> newMessages = + Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages); Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES); - List<Notification.MessagingStyle.Message> newHistoricMessages - = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages); - setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, android.app.Person.class)); - RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) - extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, android.app.RemoteInputHistoryItem.class); + List<Notification.MessagingStyle.Message> newHistoricMessages = + Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages); + setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, + Person.class)); + RemoteInputHistoryItem[] history = + (RemoteInputHistoryItem[]) extras.getParcelableArray( + Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + RemoteInputHistoryItem.class); addRemoteInputHistoryToMessages(newMessages, history); final Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, Person.class); @@ -178,10 +186,12 @@ public class MessagingLayout extends FrameLayout extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); final List<MessagingMessage> historicMessagingMessages = createMessages(newHistoricMessages, - true /* isHistoric */); + /* isHistoric= */true, usePrecomputedText); final List<MessagingMessage> newMessagingMessages = - createMessages(newMessages, false /* isHistoric */); - bindViews(user, showSpinner, historicMessagingMessages, newMessagingMessages); + createMessages(newMessages, /* isHistoric */false, usePrecomputedText); + + return new MessagingData(user, showSpinner, + historicMessagingMessages, newMessagingMessages); } /** @@ -193,7 +203,32 @@ public class MessagingLayout extends FrameLayout */ @NonNull public Runnable setDataAsync(Bundle extras) { - return () -> setData(extras); + if (!mPrecomputedTextEnabled) { + return () -> setData(extras); + } + + final MessagingData messagingData = + parseMessagingData(extras, /* usePrecomputedText= */true); + + return () -> { + finalizeInflate(messagingData.getHistoricMessagingMessages()); + finalizeInflate(messagingData.getNewMessagingMessages()); + bind(messagingData); + }; + } + + /** + * enable/disable precomputed text usage + * @hide + */ + public void setPrecomputedTextEnabled(boolean precomputedTextEnabled) { + mPrecomputedTextEnabled = precomputedTextEnabled; + } + + private void finalizeInflate(List<MessagingMessage> historicMessagingMessages) { + for (MessagingMessage messagingMessage: historicMessagingMessages) { + messagingMessage.finalizeInflate(); + } } @Override @@ -218,17 +253,13 @@ public class MessagingLayout extends FrameLayout } } - private void bindViews(Person user, boolean showSpinner, - List<MessagingMessage> historicMessagingMessages, - List<MessagingMessage> newMessagingMessages) { - setUser(user); - bind(showSpinner, historicMessagingMessages, newMessagingMessages); - } + private void bind(MessagingData messagingData) { + setUser(messagingData.getUser()); - private void bind(boolean showSpinner, List<MessagingMessage> historicMessages, - List<MessagingMessage> messages) { + List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages(); + List<MessagingMessage> messages = messagingData.getNewMessagingMessages(); ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups); - addMessagesToGroups(historicMessages, messages, showSpinner); + addMessagesToGroups(historicMessages, messages, messagingData.getShowSpinner()); // Let's first check which groups were removed altogether and remove them in one animation removeGroups(oldGroups); @@ -518,15 +549,17 @@ public class MessagingLayout extends FrameLayout * @param newMessages the messages to parse. */ private List<MessagingMessage> createMessages( - List<Notification.MessagingStyle.Message> newMessages, boolean historic) { + List<Notification.MessagingStyle.Message> newMessages, boolean isHistoric, + boolean usePrecomputedText) { List<MessagingMessage> result = new ArrayList<>(); for (int i = 0; i < newMessages.size(); i++) { Notification.MessagingStyle.Message m = newMessages.get(i); MessagingMessage message = findAndRemoveMatchingMessage(m); if (message == null) { - message = MessagingMessage.createMessage(this, m, mImageResolver); + message = MessagingMessage.createMessage(this, m, + mImageResolver, usePrecomputedText); } - message.setIsHistoric(historic); + message.setIsHistoric(isHistoric); result.add(message); } return result; diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java index 5ecd3b82053d..ad90a63ab187 100644 --- a/core/java/com/android/internal/widget/MessagingMessage.java +++ b/core/java/com/android/internal/widget/MessagingMessage.java @@ -34,11 +34,12 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { String IMAGE_MIME_TYPE_PREFIX = "image/"; static MessagingMessage createMessage(IMessagingLayout layout, - Notification.MessagingStyle.Message m, ImageResolver resolver) { + Notification.MessagingStyle.Message m, ImageResolver resolver, + boolean usePrecomputedText) { if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) { - return MessagingImageMessage.createMessage(layout, m, resolver); + return MessagingImageMessage.createMessage(layout, m, resolver, usePrecomputedText); } else { - return MessagingTextMessage.createMessage(layout, m); + return MessagingTextMessage.createMessage(layout, m, usePrecomputedText); } } @@ -55,9 +56,11 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { /** * Set a message for this view. + * * @return true if setting the message worked */ - default boolean setMessage(Notification.MessagingStyle.Message message) { + default boolean setMessage(Notification.MessagingStyle.Message message, + boolean usePrecomputedText) { getState().setMessage(message); return true; } @@ -151,4 +154,10 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { void setVisibility(int visibility); int getVisibility(); + + /** + * Finalize inflation of the MessagingMessages, which should be called on Main Thread. + * @hide + */ + void finalizeInflate(); } diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java index 19791dbad31e..bd62aad15b34 100644 --- a/core/java/com/android/internal/widget/MessagingTextMessage.java +++ b/core/java/com/android/internal/widget/MessagingTextMessage.java @@ -23,7 +23,9 @@ import android.annotation.StyleRes; import android.app.Notification; import android.content.Context; import android.text.Layout; +import android.text.PrecomputedText; import android.util.AttributeSet; +import android.util.Log; import android.view.LayoutInflater; import android.widget.RemoteViews; @@ -35,10 +37,13 @@ import com.android.internal.R; @RemoteViews.RemoteView public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage { + private static final String TAG = "MessagingTextMessage"; private static final MessagingPool<MessagingTextMessage> sInstancePool = new MessagingPool<>(20); private final MessagingMessageState mState = new MessagingMessageState(this); + private PrecomputedText mPrecomputedText = null; + public MessagingTextMessage(@NonNull Context context) { super(context); } @@ -63,25 +68,32 @@ public class MessagingTextMessage extends ImageFloatingTextView implements Messa } @Override - public boolean setMessage(Notification.MessagingStyle.Message message) { - MessagingMessage.super.setMessage(message); - setText(message.getText()); + public boolean setMessage(Notification.MessagingStyle.Message message, + boolean usePrecomputedText) { + MessagingMessage.super.setMessage(message, usePrecomputedText); + if (usePrecomputedText) { + mPrecomputedText = PrecomputedText.create(message.getText(), getTextMetricsParams()); + } else { + setText(message.getText()); + mPrecomputedText = null; + } + return true; } static MessagingMessage createMessage(IMessagingLayout layout, - Notification.MessagingStyle.Message m) { + Notification.MessagingStyle.Message m, boolean usePrecomputedText) { MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout(); MessagingTextMessage createdMessage = sInstancePool.acquire(); if (createdMessage == null) { createdMessage = (MessagingTextMessage) LayoutInflater.from( layout.getContext()).inflate( - R.layout.notification_template_messaging_text_message, - messagingLinearLayout, - false); + R.layout.notification_template_messaging_text_message, + messagingLinearLayout, + false); createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); } - createdMessage.setMessage(m); + createdMessage.setMessage(m, usePrecomputedText); return createdMessage; } @@ -135,4 +147,20 @@ public class MessagingTextMessage extends ImageFloatingTextView implements Messa public void setColor(int color) { setTextColor(color); } + + @Override + public void finalizeInflate() { + try { + setText(mPrecomputedText != null ? mPrecomputedText + : getState().getMessage().getText()); + } catch (IllegalArgumentException exception) { + Log.wtf( + /* tag = */ TAG, + /* msg = */ "PrecomputedText setText failed for TextView:" + this, + /* tr = */ exception + ); + mPrecomputedText = null; + setText(getState().getMessage().getText()); + } + } } diff --git a/core/java/com/android/internal/widget/NotificationDrawableConsumer.java b/core/java/com/android/internal/widget/NotificationDrawableConsumer.java new file mode 100644 index 000000000000..7c4d92968afb --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationDrawableConsumer.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.graphics.drawable.Drawable; + +import androidx.annotation.Nullable; + +/** + * An interface for the class, who will use {@link NotificationIconManager} to load icons. + */ +public interface NotificationDrawableConsumer { + + /** + * Sets a drawable as the content of this consumer. + * + * @param drawable the {@link Drawable} to set, or {@code null} to clear the content + */ + void setImageDrawable(@Nullable Drawable drawable); +} diff --git a/core/java/com/android/internal/widget/NotificationIconManager.java b/core/java/com/android/internal/widget/NotificationIconManager.java new file mode 100644 index 000000000000..221845cbbfd9 --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationIconManager.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.graphics.drawable.Icon; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * An interface used for Notification views to delegate handling the loading of icons. + */ +public interface NotificationIconManager { + + /** + * Called when a new icon is provided to display. + * + * @param drawableConsumer a consumer, which can display the loaded drawable. + * @param icon the updated icon to be displayed. + * + * @return a {@link Runnable} that sets the drawable on the consumer + */ + @NonNull + Runnable updateIcon( + @NonNull NotificationDrawableConsumer drawableConsumer, + @Nullable Icon icon + ); +} diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 9aa992bae72c..b5d70d379e0c 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -353,7 +353,7 @@ status_t AndroidRuntime::callMain(const String8& className, jclass clazz, JNIEnv* env; jmethodID methodId; - ALOGD("Calling main entry %s", className.string()); + ALOGD("Calling main entry %s", className.c_str()); env = getJNIEnv(); if (clazz == NULL || env == NULL) { @@ -362,7 +362,7 @@ status_t AndroidRuntime::callMain(const String8& className, jclass clazz, methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V"); if (methodId == NULL) { - ALOGE("ERROR: could not find method %s.main(String[])\n", className.string()); + ALOGE("ERROR: could not find method %s.main(String[])\n", className.c_str()); return UNKNOWN_ERROR; } @@ -378,7 +378,7 @@ status_t AndroidRuntime::callMain(const String8& className, jclass clazz, strArray = env->NewObjectArray(numArgs, stringClass, NULL); for (size_t i = 0; i < numArgs; i++) { - jstring argStr = env->NewStringUTF(args[i].string()); + jstring argStr = env->NewStringUTF(args[i].c_str()); env->SetObjectArrayElement(strArray, i, argStr); } @@ -1269,7 +1269,7 @@ void AndroidRuntime::start(const char* className, const Vector<String8>& options env->SetObjectArrayElement(strArray, 0, classNameStr); for (size_t i = 0; i < options.size(); ++i) { - jstring optionsStr = env->NewStringUTF(options.itemAt(i).string()); + jstring optionsStr = env->NewStringUTF(options.itemAt(i).c_str()); assert(optionsStr != NULL); env->SetObjectArrayElement(strArray, i + 1, optionsStr); } diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 624bd5f4da23..69fc515444b2 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -292,7 +292,7 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject if (status) { String8 message; message.appendFormat("Failed to initialize display event receiver. status=%d", status); - jniThrowRuntimeException(env, message.string()); + jniThrowRuntimeException(env, message.c_str()); return 0; } @@ -316,7 +316,7 @@ static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) { if (status) { String8 message; message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status); - jniThrowRuntimeException(env, message.string()); + jniThrowRuntimeException(env, message.c_str()); } } diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 061f66958797..833952def02b 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -340,7 +340,7 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak, if (status) { String8 message; message.appendFormat("Failed to initialize input event sender. status=%d", status); - jniThrowRuntimeException(env, message.string()); + jniThrowRuntimeException(env, message.c_str()); return 0; } diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp index 05c9f6865061..03e9a6a9d139 100644 --- a/core/jni/android_view_VelocityTracker.cpp +++ b/core/jni/android_view_VelocityTracker.cpp @@ -16,13 +16,14 @@ #define LOG_TAG "VelocityTracker-JNI" +#include <android-base/logging.h> #include <android_runtime/AndroidRuntime.h> #include <cutils/properties.h> #include <input/Input.h> #include <input/VelocityTracker.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedUtfChars.h> -#include <utils/Log.h> + #include "android_view_MotionEvent.h" #include "core_jni_helpers.h" @@ -102,7 +103,7 @@ static void android_view_VelocityTracker_nativeAddMovement(JNIEnv* env, jclass c jobject eventObj) { const MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj); if (!event) { - ALOGW("nativeAddMovement failed because MotionEvent was finalized."); + LOG(WARNING) << "nativeAddMovement failed because MotionEvent was finalized."; return; } diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index d7969cf0b3ff..ed0081c08cad 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -137,6 +137,7 @@ message SecureSettingsProto { optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_press_hold_nav_handle_enabled = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Assist assist = 7; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 67710f64e1e4..f55501ab1a8f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7790,8 +7790,9 @@ android:process=":ui"> </activity> <activity android:name="com.android.internal.app.PlatLogoActivity" - android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen" + android:theme="@style/Theme.NoTitleBar.Fullscreen" android:configChanges="orientation|screenSize|screenLayout|keyboardHidden" + android:enableOnBackInvokedCallback="true" android:icon="@drawable/platlogo" android:process=":ui"> </activity> @@ -7863,12 +7864,6 @@ </intent-filter> </activity> - <activity android:name="com.android.internal.app.NetInitiatedActivity" - android:theme="@style/Theme.Dialog.Confirmation" - android:excludeFromRecents="true" - android:process=":ui"> - </activity> - <activity android:name="com.android.internal.app.SystemUserHomeActivity" android:enabled="false" android:process=":ui" diff --git a/core/res/res/drawable/focus_event_rotary_input_background.xml b/core/res/res/drawable/focus_event_rotary_input_background.xml new file mode 100644 index 000000000000..512cd687f2b1 --- /dev/null +++ b/core/res/res/drawable/focus_event_rotary_input_background.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:name="focus_event_rotary_input_background" + android:shape="rectangle"> + + <!-- View background color --> + <solid android:color="#80741b47" /> + + <!-- View border color and width --> + <stroke android:width="1dp" android:color="#ffff00ff" /> + + <!-- The radius makes the corners rounded --> + <corners android:radius="4dp" /> + +</shape>
\ No newline at end of file diff --git a/core/res/res/raw/default_ringtone_vibration_effect.ahv b/core/res/res/raw/default_ringtone_vibration_effect.ahv new file mode 100644 index 000000000000..c66fc046316b --- /dev/null +++ b/core/res/res/raw/default_ringtone_vibration_effect.ahv @@ -0,0 +1,47 @@ +<?xml version='1.0' encoding='utf-8' standalone='no' ?> + +<!-- +** +** Copyright 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. +*/ +--> + +<vibration> + <waveform-effect> + <waveform-entry amplitude="0" durationMs="0" /> + <waveform-entry amplitude="255" durationMs="12" /> + <waveform-entry amplitude="0" durationMs="250" /> + <waveform-entry amplitude="255" durationMs="12" /> + <waveform-entry amplitude="0" durationMs="500" /> + <repeating> + <waveform-entry amplitude="77" durationMs="50" /> + <waveform-entry amplitude="77" durationMs="50" /> + <waveform-entry amplitude="78" durationMs="50" /> + <waveform-entry amplitude="79" durationMs="50" /> + <waveform-entry amplitude="81" durationMs="50" /> + <waveform-entry amplitude="84" durationMs="50" /> + <waveform-entry amplitude="87" durationMs="50" /> + <waveform-entry amplitude="93" durationMs="50" /> + <waveform-entry amplitude="101" durationMs="50" /> + <waveform-entry amplitude="114" durationMs="50" /> + <waveform-entry amplitude="133" durationMs="50" /> + <waveform-entry amplitude="162" durationMs="50" /> + <waveform-entry amplitude="205" durationMs="50" /> + <waveform-entry amplitude="255" durationMs="50" /> + <waveform-entry amplitude="255" durationMs="300" /> + <waveform-entry amplitude="0" durationMs="1000" /> + </repeating> + </waveform-effect> +</vibration>
\ No newline at end of file diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 2d13ab5b868d..7675da54b587 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gebruik biometrie of skermslot"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifieer dat dit jy is"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gebruik jou biometrie om voort te gaan"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Gebruik jou vingerafdruk om voort te gaan"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Gebruik jou gesig om voort te gaan"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Gebruik jou biometriese data of skermslot om voort te gaan"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometriese hardeware is nie beskikbaar nie"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Stawing is gekanselleer"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Vingerafdruk word nie herken nie"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Vingerafdruk word nie herken nie"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Kan nie gesig herken nie. Gebruik eerder vingerafdruk."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk is gestaaf"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesig is gestaaf"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesig is gestaaf; druk asseblief bevestig"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Laat ’n metgeselapp toe om voorgronddienste van agtergrond af te begin"</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofoon is beskikbaar"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofoon is geblokkeer"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dubbelskerm"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dubbelskerm is aan"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen is aan"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruik tans albei skerms om inhoud te wys"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Toestel is te warm"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dubbelskerm is nie beskikbaar nie omdat jou foon tans te warm word"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen is nie beskikbaar nie omdat jou foon tans te warm word"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen is nie beskikbaar nie"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen is nie beskikbaar nie omdat Batterybespaarder aan is. Jy kan dit in Instellings afskakel."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Gaan na Instellings"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 2dd8f3be0527..8a03761cfa8b 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ባዮሜትሪክስ ወይም ማያ ገፅ መቆለፊያን ይጠቀሙ"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"እርስዎን መሆንዎን ያረጋግጡ"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ለመቀጠል ባዮሜትሪክዎን ይጠቀሙ"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ለመቀጠል የእርስዎን የጣት አሻራ ይጠቀሙ"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ለመቀጠል የእርስዎን መልክ ይጠቀሙ"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ለመቀጠል የባዮሜትሪክ ወይም የማያ ገፅ ቁልፍዎን ይጠቀሙ"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ባዮሜትራዊ ሃርድዌር አይገኝም"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ማረጋገጥ ተሰርዟል"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"የጣት አሻራ አልታወቀም"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"የጣት አሻራ አልታወቀም"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"መልክን መለየት አልተቻለም። በምትኩ የጣት አሻራ ይጠቀሙ።"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ፊት ተረጋግጧል"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ፊት ተረጋግጧል፣ እባክዎ አረጋግጥን ይጫኑ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 322ae8d8e085..fea0e8fce736 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -622,8 +622,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استخدام المقاييس الحيوية أو قفل الشاشة"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"إثبات هويتك"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"استخدام المقاييس الحيوية للمتابعة"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"استخدِم بصمة إصبعك للمتابعة"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"استخدِم وجهك للمتابعة"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"استخدام المقاييس الحيوية أو قفل الشاشة للمتابعة"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"معدّات المقاييس الحيوية غير متاحة."</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"تم إلغاء المصادقة."</string> @@ -649,6 +647,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"لم يتمّ التعرّف على البصمة."</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"لم يتمّ التعرّف على بصمة الإصبع."</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"يتعذّر التعرّف على الوجه. استخدِم بصمة الإصبع بدلاً من ذلك."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"تم مصادقة بصمة الإصبع"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"تمّت مصادقة الوجه"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string> @@ -2335,11 +2334,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"يسمح هذا الإذن للتطبيق المصاحب ببدء الخدمات التي تعمل في المقدّمة من الخلفية."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"الميكروفون متاح."</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"تم حظر الميكروفون."</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"استخدام الشاشتين"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ميزة \"استخدام الشاشتين\" مفعّلة"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ميزة Dual Screen مفعّلة"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"يستخدم \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" كلتا الشاشتين لعرض المحتوى."</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"الجهاز ساخن للغاية"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"ميزة \"استخدام الشاشتين\" غير متاحة لأن هاتفك ساخن للغاية."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"ميزة Dual Screen غير متاحة لأنّ هاتفك ساخن للغاية."</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"ميزة Dual Screen غير متاحة"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"ميزة Dual Screen غير متاحة لأن ميزة \"توفير شحن البطارية\" مفعّلة. ويمكنك إيقاف هذا الإجراء من خلال \"الإعدادات\"."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"الانتقال إلى الإعدادات"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 12e0948ea9d9..24d2932b4dfd 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"বায়\'মেট্ৰিক অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"এইয়া আপুনিয়েই বুলি সত্যাপন কৰক"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"অব্যাহত ৰাখিবলৈ আপোনাৰ বায়\'মেট্ৰিক ব্যৱহাৰ কৰক"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"অব্যাহত ৰাখিবলৈ আপোনাৰ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"অব্যাহত ৰাখিবলৈ নিজৰ মুখাৱয়ব ব্যৱহাৰ কৰক"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"অব্যাহত ৰাখিবলৈ আপোনাৰ বায়’মেট্ৰিক অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"বায়োমেট্ৰিক হাৰ্ডৱেৰ উপলব্ধ নহয়"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"বিশ্বাসযোগ্যতাৰ প্ৰমাণীকৰণ বাতিল কৰা হৈছে"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ফিংগাৰপ্ৰিণ্ট চিনাক্ত কৰিব পৰা নাই"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ফিংগাৰপ্ৰিণ্ট চিনাক্ত কৰিব পৰা নাই"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"মুখাৱয়ব চিনিব নোৱাৰি। ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক।"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index ff7436fc5be3..4a6d7ff3ffad 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrik məlumatlardan və ya ekran kilidindən istifadə edin"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Kimliyinizi doğrulayın"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Davam etmək üçün biometrik məlumatlarınızdan istifadə edin"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Barmaq izi ilə davam edin"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Üz ilə davam edin"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Davam etmək üçün biometrik məlumatlar və ya ekran kilidinizdən istifadə edin"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrik proqram əlçatan deyil"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Doğrulama ləğv edildi"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Barmaq izi tanınmır"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Barmaq izi tanınmır"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Tanımaq olmur. Barmaq izini işlədin."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmaq izi doğrulandı"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Üz doğrulandı"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Üz təsdiq edildi, təsdiq düyməsinə basın"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index c32f95a9cf34..7a07cacace02 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Koristite biometriju ili zaključavanje ekrana"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite svoj identitet"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Koristite biometrijski podatak da biste nastavili"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Nastavite pomoću otiska prsta"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Potvrdite identitet licem da biste nastavili"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Koristite biometrijski podatak ili zaključavanje ekrana da biste nastavili"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Potvrda identiteta je otkazana"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisak prsta nije prepoznat"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Otisak prsta nije prepoznat"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Lice nije prepoznato. Koristite otisak prsta."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je potvrđeno"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je potvrđeno. Pritisnite Potvrdi"</string> @@ -2332,11 +2331,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Dozvoljava pratećoj aplikaciji da pokrene usluge u prvom planu iz pozadine."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofon je dostupan"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je blokiran"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dvojni ekran"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dvojni ekran je uključen"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen je uključen"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> koristi oba ekrana za prikazivanje sadržaja"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Uređaj je previše zagrejan"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dvojni ekran je nedostupan jer je telefon previše zagrejan"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen je nedostupan jer je telefon previše zagrejan"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen nije dostupan"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen nije dostupan zato što je Ušteda baterije uključena. To možete da isključite u podešavanjima."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Idi u Podešavanja"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 3be47fa89b02..2c352f9d896e 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Выкарыстоўваць біяметрыю ці блакіроўку экрана"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Спраўдзіце, што гэта вы"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Каб працягнуць, скарыстайце свае біяметрычныя даныя"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Каб працягнуць, скарыстайце адбітак пальца"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Каб працягнуць, скарыстайце распазнаванне твару"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Каб працягнуць, скарыстайце біяметрычныя даныя ці сродак разблакіроўкі экрана"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Біяметрычнае абсталяванне недаступнае"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аўтэнтыфікацыя скасавана"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Адбітак пальца не распазнаны"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Адбітак пальца не распазнаны"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Твар не распазнаны. Скарыстайце адбітак пальца."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Адбітак пальца распазнаны"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Твар распазнаны"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Твар распазнаны. Націсніце, каб пацвердзіць"</string> @@ -2333,11 +2332,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Спадарожная праграма зможа запускаць актыўныя сэрвісы з фонавага рэжыму."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Мікрафон даступны"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Мікрафон заблакіраваны"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Двайны экран"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Уключана функцыя \"Двайны экран\""</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Уключана функцыя Dual Screen"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" выкарыстоўвае абодва экраны для паказу змесціва"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Прылада моцна нагрэлася"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Функцыя \"Двайны экран\" недаступная, бо тэлефон моцна награваецца"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Функцыя Dual Screen недаступная, бо тэлефон моцна награваецца"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Функцыя Dual Screen недаступная"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Функцыя Dual Screen недаступная, бо ўключаны рэжым энергазберажэння. Вы можаце выключыць яго ў Наладах."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Перайсці ў Налады."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 10d747b8878f..078ec8586f68 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Използване на биометрични данни или опцията за заключване на екрана"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Потвърдете, че сте вие"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Използвайте биометричните си данни, за да продължите"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Използвайте отпечатъка си, за да продължите"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Използвайте лицето си, за да продължите"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Използвайте биометричните си данни или опцията за заключване на екрана, за да продължите"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометричният хардуер не е налице"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Удостоверяването бе анулирано"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отпечатъкът не е разпознат"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Отпечатъкът не е разпознат"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Лицето не е разпознато. Използвайте отпечатък."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатъкът е удостоверен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е удостоверено"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е удостоверено. Моля, натиснете „Потвърждаване“"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 9d276cb3927b..f451da3b4f23 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"বায়োমেট্রিক্স অথবা স্ক্রিন লক ব্যবহার করুন"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"আপনার পরিচয় যাচাই করুন"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"চালিয়ে যেতে আপনার বায়োমেট্রিক্স ব্যবহার করুন"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"চালিয়ে যেতে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"চালিয়ে যেতে আপনার ফেস ব্যবহার করুন"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"চালিয়ে যেতে আপনার বায়োমেট্রিক্স বা স্ক্রিন লক ব্যবহার করুন"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"যাচাইকরণ বাতিল হয়েছে"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ফিঙ্গারপ্রিন্ট শনাক্ত করা যায়নি"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ফিঙ্গারপ্রিন্ট শনাক্ত করা যায়নি"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"মুখ শনাক্ত করতে পারছি না। পরিবর্তে আঙ্গুলের ছাপ ব্যবহার করুন।"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ফেস যাচাই করা হয়েছে"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ফেস যাচাই করা হয়েছে, \'কনফার্ম করুন\' বোতাম প্রেস করুন"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 33ac0c31939e..fb027633cadd 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Koristi biometriju ili zaključavanje ekrana"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite identitet"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Koristite biometriju da nastavite"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Potvrdite identitet otiskom prsta da nastavite"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Potvrdite identitet licem da nastavite"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Koristite biometriju ili zaključavanje ekrana da nastavite"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikacija je otkazana"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisak prsta nije prepoznat"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Otisak prsta nije prepoznat"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Nije moguće prepoznati lice. Koristite otisak prsta."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je provjereno"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je provjereno, pritisnite dugme za potvrdu"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 356576d6acbd..edfd9d182e8d 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Fes servir la biometria o el bloqueig de pantalla"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica la teva identitat"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilitza la teva biometria per continuar"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Utilitza l\'empremta digital per continuar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Utilitza la cara per continuar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Utilitza la biometria o el bloqueig de pantalla per continuar"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Maquinari biomètric no disponible"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"S\'ha cancel·lat l\'autenticació"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"L\'empremta digital no s\'ha reconegut"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"L\'empremta digital no s\'ha reconegut"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"No podem detectar la cara. Usa l\'empremta digital."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index a4e9ab5f29db..d2baa9b60c2c 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Použít biometrii nebo zámek obrazovky"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrďte, že jste to vy"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Pokračujte biometrickým ověřením"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Pokračujte přiložením prstu"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Pokračujte ověřením obličeje"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Pokračujte ověřením pomocí biometrických údajů nebo zámku obrazovky"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrický hardware není k dispozici"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ověření bylo zrušeno"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisk prstu nebyl rozpoznán"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Otisk prstu nebyl rozpoznán"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Obličej se nepodařilo rozpoznat. Použijte místo něj otisk prstu."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisk byl ověřen"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Obličej byl ověřen"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Obličej byl ověřen, stiskněte tlačítko pro potvrzení"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index b8bd5bafa881..6de92f9fb140 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Brug biometri eller skærmlås"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verificer, at det er dig"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Brug dine biometriske data for at fortsætte"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Brug dit fingeraftryk for at fortsætte"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Brug dit ansigt for at fortsætte"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Brug dine biometriske data eller din skærmlås for at fortsætte"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk hardware er ikke tilgængelig"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Godkendelsen blev annulleret"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingeraftrykket blev ikke genkendt"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingeraftrykket blev ikke genkendt"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Ansigtet kan ikke genkendes. Brug fingeraftryk i stedet."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeraftrykket blev godkendt"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansigtet er godkendt"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansigtet er godkendt. Tryk på Bekræft."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 84f2c1f1cec4..ec8bb7bde82c 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrisches Verfahren oder Displaysperre verwenden"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Deine Identität bestätigen"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Mithilfe eines biometrischen Verfahrens fortfahren"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Fingerabdruck verwenden, um fortzufahren"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Gesichtserkennung verwenden, um fortzufahren"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Verwende deine biometrischen Daten oder deine Display-Entsperrmethode, um fortzufahren"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrische Hardware nicht verfügbar"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentifizierung abgebrochen"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingerabdruck nicht erkannt"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingerabdruck nicht erkannt"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Gesicht wurde nicht erkannt. Verwende stattdessen den Fingerabdruck."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerabdruck wurde authentifiziert"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesicht authentifiziert"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesicht authentifiziert, bitte bestätigen"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index d603a2604822..66148a950cdd 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Χρήση βιομετρικών ή κλειδώματος οθόνης"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Επαλήθευση ταυτότητας"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Χρησιμοποιήστε βιομετρικά για να συνεχίσετε"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Χρησιμοποιήστε το δακτυλικό σας αποτύπωμα για να συνεχίσετε"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Χρησιμοποιήστε το πρόσωπό σας για να συνεχίσετε"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Χρήση βιομετρικών στοιχείων ή κλειδώματος οθόνης για συνέχεια"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Δεν υπάρχει διαθέσιμος βιομετρικός εξοπλισμός"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ο έλεγχος ταυτότητας ακυρώθηκε"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Δεν είναι δυνατή η αναγνώριση του δακτυλικού αποτυπώματος"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Δεν είναι δυνατή η αναγνώριση του δακτυλικού αποτυπώματος"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Το πρόσωπο δεν αναγνωρίζεται. Χρησιμ. δακτ. αποτ."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Έγινε έλεγχος ταυτότητας προσώπου"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index c92f46083ee3..906e94ce3d55 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify that it’s you"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use your fingerprint to continue"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use your face to continue"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Use your biometric or screen lock to continue"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingerprint not recognised"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingerprint not recognised"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Can’t recognise face. Use fingerprint instead."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index bf94625d36e5..2d7ba8d4063e 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify it’s you"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use your fingerprint to continue"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use your face to continue"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Use your biometric or screen lock to continue"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication canceled"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingerprint not recognized"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingerprint not recognized"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Can’t recognize face. Use fingerprint instead."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated, please press confirm"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 48a10df78d40..ad9c777a8080 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify that it’s you"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use your fingerprint to continue"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use your face to continue"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Use your biometric or screen lock to continue"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingerprint not recognised"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingerprint not recognised"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Can’t recognise face. Use fingerprint instead."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index a3f2d7db8aad..c9db594c9f2f 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify that it’s you"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use your fingerprint to continue"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use your face to continue"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Use your biometric or screen lock to continue"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingerprint not recognised"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingerprint not recognised"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Can’t recognise face. Use fingerprint instead."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 17e3fcb12808..5888963db967 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Use biometrics or screen lock"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verify it’s you"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use your biometric to continue"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use your fingerprint to continue"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use your face to continue"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Use your biometric or screen lock to continue"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication canceled"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingerprint not recognized"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingerprint not recognized"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Can’t recognize face. Use fingerprint instead."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated, please press confirm"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 347d53742289..756a1981e3e4 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar datos biométricos o bloqueo de pantalla"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Comprueba que eres tú"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Usa tus datos biométricos para continuar"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Usa tu huella dactilar para continuar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Usa el rostro para continuar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Usa tus datos biométricos o bloqueo de pantalla para continuar"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"No hay hardware biométrico disponible"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Se canceló la autenticación"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"No se reconoció la huella dactilar"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"No se reconoció la huella dactilar"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"No se reconoce el rostro. Usa la huella dactilar."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Se autenticó la huella dactilar"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Se autenticó el rostro"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se autenticó el rostro; presiona Confirmar"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 70610d11d7ec..57480bdbb375 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar biometría o bloqueo de pantalla"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica que eres tú"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Usa tu biometría para continuar"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Usa la huella digital para continuar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Usa la cara para continuar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Usa la biometría o tu bloqueo de pantalla para continuar"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico no disponible"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticación cancelada"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Huella digital no reconocida"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Huella digital no reconocida"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"No se reconoce la cara. Usa la huella digital."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Se ha autenticado la huella digital"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se ha autenticado la cara, pulsa para confirmar"</string> @@ -1376,7 +1375,7 @@ <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Se ha detectado un accesorio de audio analógico"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información."</string> <string name="adb_active_notification_title" msgid="408390247354560331">"Depuración por USB activa"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"Toca para desactivar depuración USB"</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"Toca para desactivar la depuración USB"</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Seleccionar para inhabilitar la depuración por USB"</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Depuración inalámbrica conectada"</string> <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Toca para desactivar la depuración inalámbrica"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 36ef58397b29..335944767b32 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biomeetria või ekraaniluku kasutamine"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Kinnitage oma isik"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Jätkamiseks kasutage biomeetriat"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Jätkamiseks kasutage oma sõrmejälge"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Jätkamiseks kasutage oma nägu"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Jätkamiseks kasutage oma biomeetrilisi andmeid või ekraanilukku"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biomeetriline riistvara ei ole saadaval"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentimine tühistati"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Sõrmejälge ei tuvastatud"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Sõrmejälge ei tuvastatud"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Nägu ei õnnestu tuvastada. Kasutage sõrmejälge."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sõrmejälg autenditi"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Nägu on autenditud"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Nägu on autenditud, vajutage käsku Kinnita"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index bd0a0457d80f..c2ef534017ff 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Erabili sistema biometrikoak edo pantailaren blokeoa"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Egiaztatu zeu zarela"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Aurrera egiteko, erabili sistema biometrikoak"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Aurrera egiteko, erabili hatz-marka"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Aurrera egiteko, erabili aurpegia"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Aurrera egiteko, erabili sistema biometrikoak edo pantailaren blokeoa"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrikoa ez dago erabilgarri"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Utzi da autentifikazioa"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Ez da ezagutu hatz-marka"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Ez da ezagutu hatz-marka"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Ezin da hauteman aurpegia. Erabili hatz-marka."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentifikatu da hatz-marka"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autentifikatu da aurpegia"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autentifikatu da aurpegia; sakatu Berretsi"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Aurreko planoko zerbitzuak atzeko planotik abiarazteko baimena ematen die aplikazio osagarriei."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Erabilgarri dago mikrofonoa"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Blokeatuta dago mikrofonoa"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Bi pantailako modua"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Bi pantailako modua aktibatuta dago"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen aktibatuta dago"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> bi pantailak erabiltzen ari da edukia erakusteko"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Gailua beroegi dago"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Bi pantailako modua ez dago erabilgarri telefonoa berotzen ari delako"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen ez dago erabilgarri telefonoa berotzen ari delako"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen ez dago erabilgarri"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen ez dago erabilgarri, bateria-aurreztailea aktibatuta dagoelako. Aukera hori desaktibatzeko, joan ezarpenetara."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Joan Ezarpenak atalera"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 2a5f064c8a98..3841eaa4b991 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استفاده از زیستسنجشی یا قفل صفحه"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"تأیید کنید این شمایید"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"برای ادامه، از زیستسنجشی استفاده کنید"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"برای ادامه، از اثر انگشتتان استفاده کنید"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"برای ادامه، از چهرهتان استفاده کنید"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"برای ادامه، از زیستسنجشی یا قفل صفحه استفاده کنید"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"سختافزار زیستسنجی دردسترس نیست"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"اصالتسنجی لغو شد"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"اثر انگشت تشخیص داده نشد"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"اثر انگشت تشخیص داده نشد"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"چهره شناسایی نشد. درعوض از اثر انگشت استفاده کنید."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالتسنجی شد"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالتسنجی شد"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالتسنجی شد، لطفاً تأیید را فشار دهید"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 16c864c95956..5c9f0e4d4f60 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Käytä biometriikkaa tai näytön lukitusta"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Vahvista henkilöllisyytesi"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Jatka käyttämällä biometriikkaa"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Jatka sormenjäljen avulla"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Jatka kasvojen avulla"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Jatka biometriikan tai näytön lukituksen avulla"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrinen laitteisto ei käytettävissä"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Todennus peruutettu"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Sormenjälkeä ei tunnistettu"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Sormenjälkeä ei tunnistettu"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Kasvoja ei voi tunnistaa. Käytä sormenjälkeä."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sormenjälki tunnistettu"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Kasvot tunnistettu"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Kasvot tunnistettu, valitse Vahvista"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 60575ad71d5b..9b0c5363e4f9 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utiliser les données biométriques ou le verrouillage de l\'écran"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmez que c\'est vous"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilisez votre méthode d\'authentification biométrique pour continuer"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Utilisez votre empreinte digitale pour continuer"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Utilisez votre visage pour continuer"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Utilisez vos données biométriques ou le verrouillage de l\'écran pour continuer"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Matériel biométrique indisponible"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentification annulée"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Empreinte digitale non reconnue"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Empreinte digitale non reconnue"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Visage non reconnu. Utilisez plutôt l\'empreinte digitale."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string> @@ -2332,11 +2331,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permet à une application compagnon en arrière-plan de lancer des services d\'avant-plan."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Le microphone est accessible"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Le microphone est bloqué"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Double écran"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Le double écran est activé"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen activé"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> utilise les deux écrans pour afficher le contenu"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"L\'appareil est trop chaud"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Le double écran n\'est pas accessible, car votre téléphone est trop chaud"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen n\'est pas accessible, car votre téléphone est trop chaud"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"La fonctionnalité Dual Screen n\'est pas accessible"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"La fonctionnalité Dual Screen n\'est pas accessible, car l\'économiseur de pile est activé. Vous pouvez désactiver cette option dans les paramètres."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Accéder aux paramètres"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 0edaac5fcdfc..f5f4316ab559 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utiliser la biométrie ou le verrouillage de l\'écran"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmez votre identité"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilisez la biométrie pour continuer"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Utilisez votre empreinte digitale pour continuer"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Utilisez la reconnaissance faciale pour continuer"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Utilisez la biométrie ou le verrouillage de l\'écran pour continuer"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Matériel biométrique indisponible"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentification annulée"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Empreinte digitale non reconnue"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Empreinte digitale non reconnue"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Visage non reconnu. Utilisez votre empreinte."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index e672b2f35651..ef6d3171ed7d 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utilizar desbloqueo biométrico ou credencial do dispositivo"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica que es ti"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Para continuar, utiliza o desbloqueo biométrico"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Usa a impresión dixital para continuar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Usa o recoñecemento facial para continuar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Para continuar, utiliza o desbloqueo biométrico ou o bloqueo de pantalla"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"O hardware biométrico non está dispoñible"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Cancelouse a autenticación"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Non se recoñeceu a impresión dixital"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Non se recoñeceu a impresión dixital"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Non se recoñeceu a cara. Usa a impresión dixital."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autenticouse a impresión dixital"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autenticouse a cara"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autenticouse a cara, preme Confirmar"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 05c839b28bf9..1f1bc518dd29 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"બાયોમેટ્રિક્સ અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"તે તમે જ છો એ ચકાસો"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"આગળ વધવા માટે બાયોમેટ્રિકનો ઉપયોગ કરો"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ચાલુ રાખવા માટે તમારી ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ચાલુ રાખવા માટે તમારા ચહેરાનો ઉપયોગ કરો"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ચાલુ રાખવા માટે તમારા બાયોમેટ્રિક ડેટા અથવા સ્ક્રીન લૉક સુવિધાનો ઉપયોગ કરો"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"પ્રમાણીકરણ રદ કર્યું"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ફિંગરપ્રિન્ટ ઓળખી શકાઈ નથી"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ફિંગરપ્રિન્ટ ઓળખી શકાઈ નથી"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"ચહેરો ઓળખી શકતા નથી. તેને બદલે ફિંગરપ્રિન્ટ વાપરો."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ચહેરા પ્રમાણિત"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ચહેરા પ્રમાણિત, કૃપા કરીને કન્ફર્મ કરો"</string> @@ -2335,9 +2334,9 @@ <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen ચાલુ છે"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"કન્ટેન્ટ બતાવવા માટે <xliff:g id="APP_NAME">%1$s</xliff:g> બન્ને ડિસ્પ્લેનો ઉપયોગ કરી રહી છે"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"ડિવાઇસ ખૂબ જ ગરમ છે"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"ડ્યૂઅલ સ્ક્રીન અનુપલબ્ધ છે કારણ કે તમારો ફોન ખૂબ જ ગરમ થઈ રહ્યો છે"</string> - <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"ડ્યૂઅલ સ્ક્રીન અનુપલબ્ધ છે"</string> - <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"બૅટરી સેવર ચાલુ હોવાને કારણે ડ્યૂઅલ સ્ક્રીન અનુપલબ્ધ છે. તમે સેટિંગમાં જઈને આને બંધ કરી શકો છો."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen ઉપલબ્ધ નથી કારણ કે તમારો ફોન ખૂબ જ ગરમ થઈ રહ્યો છે"</string> + <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen ઉપલબ્ધ નથી"</string> + <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"બૅટરી સેવર ચાલુ હોવાને કારણે Dual Screen ઉપલબ્ધ નથી. તમે સેટિંગમાં જઈને આને બંધ કરી શકો છો."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"સેટિંગ પર જાઓ"</string> <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"બંધ કરો"</string> <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g>ની ગોઠવણી કરવામાં આવી છે"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 888fc0f7fb34..01dec7457eee 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"बायोमेट्रिक्स या स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"अपनी पहचान की पुष्टि करें"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"जारी रखने के लिए, बायोमेट्रिक्स इस्तेमाल करें"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"जारी रखने के लिए, अपने फ़िंगरप्रिंट की मदद से पहचान की पुष्टि करें"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"जारी रखने के लिए, अपने चेहरा की मदद से पहचान की पुष्टि करें"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"जारी रखने के लिए, बायोमेट्रिक या स्क्रीन लॉक क्रेडेंशियल डालकर पुष्टि करें"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेयर उपलब्ध नहीं है"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"प्रमाणीकरण रद्द किया गया"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"फ़िंगरप्रिंट की पहचान नहीं हो पाई"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"फ़िंगरप्रिंट की पहचान नहीं हो पाई"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"चेहरे की पहचान नहीं हुई. फ़िंगरप्रिंट इस्तेमाल करें."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"फ़िंगरप्रिंट की पुष्टि हो गई"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरे की पहचान की गई"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 2e1b8ad96289..c1d7a2308a14 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Upotreba biometrijske autentifikacije ili zaključavanja zaslona"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite da ste to vi"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Upotrijebite svoju biometrijsku autentifikaciju da biste nastavili"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Za nastavak upotrijebite otisak prsta"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Za nastavak se identificirajte licem"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Za nastavak se identificirajte biometrijski ili vjerodajnicom zaključavanja zaslona"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikacija otkazana"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisak prsta nije prepoznat"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Otisak prsta nije prepoznat"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Prepoznavanje lica nije uspjelo. Upotrijebite otisak prsta."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentificirano otiskom prsta"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je autentificirano"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je autentificirano, pritisnite Potvrdi"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index bdf81a5ed798..75e1ee2b682d 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"A folytatás biometriai feloldással vagy képernyőzárral lehetséges"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Igazolja, hogy Ön az"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"A folytatás biometriai feloldással lehetséges"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"A folytatáshoz használja ujjlenyomatát"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"A folytatáshoz használja az arcalapú feloldást"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"A folytatás biometriai feloldással vagy a képernyőzár feloldásával lehetséges"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrikus hardver nem áll rendelkezésre"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Hitelesítés megszakítva"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Az ujjlenyomat nem ismerhető fel"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Az ujjlenyomat nem ismerhető fel"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Az arc nem felismerhető. Használjon ujjlenyomatot."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Ujjlenyomat hitelesítve"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Arc hitelesítve"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Arc hitelesítve; nyomja meg a Megerősítés lehetőséget"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 6eec876da763..b2e1230623ae 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Օգտագործել կենսաչափական համակարգեր կամ էկրանի կողպում"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Հաստատեք ձեր ինքնությունը"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Շարունակելու համար օգտագործեք կենսաչափական համակարգեր"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Շարունակելու համար օգտագործեք ձեր մատնահետքը"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Շարունակելու համար օգտագործեք դեմքով իսկորոշումը"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Շարունակելու համար օգտագործեք ձեր կենսաչափական տվյալները կամ էկրանի կողպումը"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Կենսաչափական սարքը հասանելի չէ"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Նույնականացումը չեղարկվեց"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Մատնահետքը չի ճանաչվել"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Մատնահետքը չի ճանաչվել"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Դեմքը չի հաջողվում ճանաչել։ Օգտագործեք մատնահետքը։"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Մատնահետքը նույնականացվեց"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Դեմքը ճանաչվեց"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Թույլատրում է ուղեկցող հավելվածին ակտիվ ծառայություններ գործարկել ֆոնային ռեժիմից։"</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Խոսափողը հասանելի է"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Խոսափողն արգելափակված է"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Կրկնակի էկրան"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Կրկնակի էկրանը միացված է"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen-ը միացված է"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն օգտագործում է երկու էկրանները"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Սարքը գերտաքացել է"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Կրկնակի էկրանն անհասանելի է, քանի որ ձեր հեռախոսը գերտաքանում է"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen-ն անհասանելի է, քանի որ ձեր հեռախոսը գերտաքանում է"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen-ը հասանելի չէ"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen-ն անհասանելի է, քանի որ Մարտկոցի տնտեսումը միացված է։ Դուք կարող եք անջատել այս գործառույթը Կարգավորումներում։"</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Անցնել Կարգավորումներ"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index e65d8e3d9d31..f0c8b10a8722 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gunakan biometrik atau kunci layar"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifikasi bahwa ini memang Anda"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gunakan biometrik untuk melanjutkan"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Gunakan sidik jari untuk melanjutkan"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Gunakan wajah untuk melanjutkan"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Gunakan biometrik atau kunci layar untuk melanjutkan"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrik tidak tersedia"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentikasi dibatalkan"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Sidik jari tidak dikenali"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Sidik jari tidak dikenali"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Tidak dapat mengenali wajah. Gunakan sidik jari."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sidik jari diautentikasi"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah diautentikasi"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah diautentikasi, silakan tekan konfirmasi"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Mengizinkan aplikasi pendamping memulai layanan latar depan dari latar belakang."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofon tersedia"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon diblokir"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Layar ganda"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen aktif"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> menggunakan kedua layar untuk menampilkan konten"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Suhu perangkat terlalu panas"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Layar ganda tidak tersedia karena suhu ponsel terlalu panas"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen tidak tersedia karena suhu ponsel terlalu panas"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen tidak tersedia"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen tidak tersedia karena Penghemat Baterai aktif. Anda dapat menonaktifkannya di Setelan."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Buka Setelan"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index e904c437e090..7584d1cdfd17 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -296,9 +296,9 @@ <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="user_owner_label" msgid="8628726904184471211">"Skipta yfir í eigið snið"</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 í eigið snið <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="user_owner_app_label" msgid="1553595155465750298">"Skipta yfir í einkasnið <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="managed_profile_app_label" msgid="367401088383965725">"Skipta yfir í vinnusnið <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="permgrouplab_contacts" msgid="4254143639307316920">"Tengiliðir"</string> <string name="permgroupdesc_contacts" msgid="9163927941244182567">"fá aðgang að tengiliðunum þínum"</string> @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Nota lífkenni eða skjálás"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Staðfestu hver þú ert"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Notaðu lífkenni til að halda áfram"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Notaðu fingrafarið til að halda áfram"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Notaðu andlitið til að halda áfram"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Notaðu lífkenni eða skjálás til að halda áfram"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Lífkennavélbúnaður ekki tiltækur"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Hætt við auðkenningu"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingrafar þekkist ekki"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingrafar þekkist ekki"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Andlit þekkist ekki. Notaðu fingrafar í staðinn."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingrafar staðfest"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Andlit staðfest"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Andlit staðfest, ýttu til að staðfesta"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 66ee83232982..ac0c7df1f365 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usa la biometria o il blocco schermo"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica la tua identità"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Usa la biometria per continuare"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Utilizza la tua impronta per continuare"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Usa il tuo volto per continuare"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Per continuare devi usare i tuoi dati biometrici o il tuo blocco schermo"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrico non disponibile"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticazione annullata"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Impronta non riconosciuta"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Impronta non riconosciuta"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Impossibile riconoscere il volto. Usa l\'impronta."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impronta autenticata"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Volto autenticato"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Volto autenticato, premi Conferma"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 16b2d179ae6f..a17e9892f9a1 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"שימוש במידע ביומטרי בנעילת מסך"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"אימות הזהות שלך"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"יש להשתמש במידע ביומטרי כדי להמשיך"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"צריך להשתמש בטביעת האצבע כדי להמשיך"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"צריך להשתמש בזיהוי הפנים כדי להמשיך"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"יש להשתמש במידע הביומטרי או בנעילת המסך כדי להמשיך"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"חומרה ביומטרית לא זמינה"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"האימות בוטל"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"טביעת האצבע לא זוהתה"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"טביעת האצבע לא זוהתה"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"לא ניתן לזהות את הפנים. יש להשתמש בטביעת אצבע במקום."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"טביעת האצבע אומתה"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"זיהוי הפנים בוצע"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index d6064685013c..30652021fc49 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"生体認証または画面ロックの使用"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"本人確認"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"続行するには生体認証を使用してください"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"続行するには指紋認証を使用してください"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"続行するには顔認証を使用してください"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"続行するには、生体認証または画面ロックを使用してください"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"生体認証ハードウェアが利用できません"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"認証をキャンセルしました"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"指紋を認識できません"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"指紋を認識できません"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"顔を認識できません。指紋認証を使用してください。"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋認証を完了しました"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"顔を認証しました"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"顔を認証しました。[確認] を押してください"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 2badd0a642db..dd7aff74280e 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"გამოიყენეთ ბიომეტრიული სისტემა ან ეკრანის დაბლოკვა"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"დაადასტურეთ ვინაობა"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"გასაგრძელებლად გამოიყენეთ თქვენი ბიომეტრიული მონაცემები"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"გასაგრძელებლად გამოიყენეთ თქვენი თითის ანაბეჭდი"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"გასაგრძელებლად გამოიყენეთ სახის ამოცნობა"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"გასაგრძელებლად გამოიყენეთ თქვენი ბიომეტრიული მონაცემები ან ეკრანის განბლოკვის ნიმუში"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ბიომეტრიული აპარატურა მიუწვდომელია"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ავტორიზაცია გაუქმდა"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"თითის ანაბეჭდის ამოცნობა ვერ მოხერხდა"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"თითის ანაბეჭდის ამოცნობა ვერ მოხერხდა"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"სახის ამოცნობა ვერ ხერხდება. სანაცვლოდ თითის ანაბეჭდი გამოიყენეთ."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"თითის ანაბეჭდი ავტორიზებულია"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"სახე ავტორიზებულია"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"სახე ავტორიზებულია, დააჭირეთ დადასტურებას"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 0bb57f93ecd8..8cfc1c00f713 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Биометриканы немесе экран құлпын пайдалану"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Бұл сіз екеніңізді растаңыз"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Жалғастыру үшін биометрикаңызды пайдаланыңыз."</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Жалғастыру үшін саусақ ізін пайдаланыңыз."</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Жалғастыру үшін бетті анықтау функциясын пайдаланыңыз."</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Жалғастыру үшін биометриканы немесе экран құлпын пайдаланыңыз."</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалық жабдық жоқ"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аутентификациядан бас тартылды."</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Саусақ ізі танылмады."</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Саусақ ізі танылмады."</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Бет танылмады. Орнына саусақ ізін пайдаланыңыз."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Саусақ ізі аутентификацияланды"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Бет танылды"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Бет танылды, \"Растау\" түймесін басыңыз"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Қосымша қолданбаға экрандық режимдегі қызметтерді фоннан іске қосуға рұқсат беріледі."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофон қолжетімді."</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон блокталған."</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Қос экран"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Қос экран функциясы қосулы"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen функциясы қосулы"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы контентті көрсету үшін екі дисплейді де пайдаланады."</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Құрылғы қатты қызып кетті."</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Қос экран функциясы істемейді, себебі телефон қатты қызып кетеді."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen функциясы істемейді, себебі телефон қатты қызып кетеді."</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen қолжетімсіз"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Батареяны үнемдеу режимі қосулы болғандықтан, Dual Screen қолжетімсіз. Мұны параметрлерден өшіруге болады."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Параметрлерге өту"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 8cc3c64c7d10..db89e91a34bd 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ប្រើជីវមាត្រ ឬការចាក់សោអេក្រង់"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ផ្ទៀងផ្ទាត់ថាជាអ្នក"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ប្រើជីវមាត្ររបស់អ្នក ដើម្បីបន្ត"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ប្រើស្នាមម្រាមដៃរបស់អ្នក ដើម្បីបន្ត"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ប្រើមុខរបស់អ្នក ដើម្បីបន្ត"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ប្រើការចាក់សោអេក្រង់ ឬជីវមាត្ររបស់អ្នក ដើម្បីបន្ត"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"មិនអាចប្រើឧបករណ៍ស្កេនស្នាមម្រាមដៃបានទេ"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"បានបោះបង់ការផ្ទៀងផ្ទាត់"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"មិនស្គាល់ស្នាមម្រាមដៃទេ"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"មិនស្គាល់ស្នាមម្រាមដៃទេ"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"មិនអាចសម្គាល់មុខបានទេ។ សូមប្រើស្នាមម្រាមដៃជំនួសវិញ។"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"បានផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"បានផ្ទៀងផ្ទាត់មុខ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"បានផ្ទៀងផ្ទាត់មុខ សូមចុចបញ្ជាក់"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index a2a048ce477e..73060ebd1c10 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ಬಯೋಮೆಟ್ರಿಕ್ಸ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸಿ"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ಇದು ನೀವೇ ಎಂದು ಪರಿಶೀಲಿಸಿ"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಬಯೋಮೆಟ್ರಿಕ್ ಬಳಸಿ"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಅನ್ನು ಬಳಸಿ"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಬಯೋಮೆಟ್ರಿಕ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸಿ"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"ಮುಖ ಗುರುತಿಸಲಾಗುತ್ತಿಲ್ಲ ಬದಲಿಗೆ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಬಳಸಿ."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 5b1dfd1737aa..4fe36b6097f8 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"생체 인식 또는 화면 잠금을 사용"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"본인 확인"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"생체 인식을 사용하여 계속하세요"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"계속하려면 지문을 인증하세요"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"계속하려면 얼굴로 인증하세요"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"계속하려면 생체 인식이나 화면 잠금을 사용하세요"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"생체 인식 하드웨어를 사용할 수 없음"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"인증이 취소되었습니다."</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"지문이 인식되지 않았습니다."</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"지문을 인식할 수 없습니다."</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"얼굴을 인식할 수 없습니다. 대신 지문을 사용하세요."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"지문이 인증됨"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"얼굴이 인증되었습니다"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"얼굴이 인증되었습니다. 확인을 누르세요"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index f7852d8008c4..763afb169b85 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Биометрикалык жөндөөнү же экрандын кулпусун колдонуу"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Өзүңүздү ырастаңыз"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Улантуу үчүн биометрикалык жөндөөнү колдонуу"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Улантуу үчүн манжаңызды сканерге тийгизиңиз"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Улантуу үчүн жүзүңүздү көрсөтүңүз"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Улантуу үчүн биометрикалык маалыматты же экрандын кулпусун колдонуңуз"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Манжа изи таанылган жок"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Манжа изи таанылган жок"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Жүз таанылбай жатат. Манжа изин колдонуңуз."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Манжа изи текшерилди"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Жүздүн аныктыгы текшерилди"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 68f4acf9e839..c57d386c5943 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ໃຊ້ລະບົບຊີວະມິຕິ ຫຼື ການລັອກໜ້າຈໍ"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ຢັ້ງຢືນວ່າແມ່ນທ່ານ"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ໃຊ້ລະບົບຊີວະມິຕິຂອງທ່ານເພື່ອດຳເນີນການຕໍ່"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ໃຊ້ລາຍນິ້ວມືຂອງທ່ານເພື່ອສືບຕໍ່"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ໃຊ້ໃບໜ້າຂອງທ່ານເພື່ອສືບຕໍ່"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ໃຊ້ລະບົບຊີວະມິຕິ ຫຼື ການລັອກໜ້າຈໍຂອງທ່ານເພື່ອດຳເນີນການຕໍ່"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ຮາດແວຊີວະມິຕິບໍ່ສາມາດໃຊ້ໄດ້"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ຍົກເລີກການຮັບຮອງຄວາມຖືກຕ້ອງແລ້ວ"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ບໍ່ຮູ້ຈັກລາຍນິ້ວມື"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ບໍ່ຮູ້ຈັກລາຍນິ້ວມື"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"ບໍ່ສາມາດຈຳແນກໜ້າໄດ້. ກະລຸນາໃຊ້ລາຍນິ້ວມືແທນ."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ, ກະລຸນາກົດຢືນຢັນ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index fca59f850398..ed1205946ba8 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Naudoti biometrinius duomenis arba ekrano užraktą"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Patvirtinkite, kad tai jūs"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Norėdami tęsti, naudokite biometrinius duomenis"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Jei norite tęsti, naudokite piršto atspaudą"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Jei norite tęsti, naudokite veido atpažinimo funkciją"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Jei norite tęsti, naudokite biometrinius duomenis arba ekrano užraktą"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrinė aparatinė įranga nepasiekiama"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikavimas atšauktas"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Piršto atspaudas neatpažintas"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Piršto atspaudas neatpažintas"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Veidas neatpažintas. Naudokite kontrolinį kodą."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Piršto antspaudas autentifikuotas"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Veidas autentifikuotas"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Veidas autentifikuotas, paspauskite patvirtinimo mygtuką"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 563d20638fda..a2380d9af0ca 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrijas vai ekrāna bloķēšanas izmantošana"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Apstipriniet, ka tas esat jūs"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Lai turpinātu, izmantojiet biometriju"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Izmantojiet pirksta nospiedumu, lai turpinātu"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Izmantojiet autorizāciju pēc sejas, lai turpinātu"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Izmantojiet biometrijas datus vai ekrāna bloķēšanas opciju, lai turpinātu"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisko datu aparatūra nav pieejama"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikācija ir atcelta"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Pirksta nospiedums netika atpazīts"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Pirksta nospiedums netika atpazīts"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Nevar atpazīt seju. Lietojiet pirksta nospiedumu."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Pirksta nospiedums tika autentificēts."</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Seja autentificēta"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Seja ir autentificēta. Nospiediet pogu Apstiprināt."</string> @@ -2332,13 +2331,13 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Ļauj palīglietotnei sākt priekšplāna pakalpojumus no fona."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofons ir pieejams."</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofons ir bloķēts."</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Divu ekrānu režīms"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Ieslēgts divu ekrānu režīms"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen režīms"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Ieslēgts Dual Screen režīms"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> satura rādīšanai izmanto abus displejus."</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Ierīce ir pārāk sakarsusi"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Divu ekrānu režīms nav pieejams, jo tālrunis sāk pārāk sakarst."</string> - <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Divu ekrānu režīms nav pieejams"</string> - <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Divu ekrānu režīms nav pieejams, jo ir ieslēgts akumulatora enerģijas taupīšanas režīms. To var izslēgt iestatījumos."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen režīms nav pieejams, jo tālrunis sāk pārāk sakarst."</string> + <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen režīms nav pieejams"</string> + <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen režīms nav pieejams, jo ir ieslēgts akumulatora enerģijas taupīšanas režīms. To var izslēgt iestatījumos."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Atvērt iestatījumus"</string> <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"Izslēgt"</string> <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ir konfigurēta"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 678d167a7208..0eb3927233db 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Користи биометрика или заклучен екран"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Потврдете дека сте вие"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Користете ја вашата биометрика за да продолжите"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Користете го отпечатокот за да продолжите"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Користете го вашиот лик за да продолжите"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Користете ја вашата биометрика или заклучување екран за да продолжите"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрискиот хардвер е недостапен"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Проверката е откажана"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отпечатокот не е препознаен"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Отпечатокот не е препознаен"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Не се препознава ликот. Користете отпечаток."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатокот е проверен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е проверено"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е проверено, притиснете го копчето „Потврди“"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index c2bce63c463c..e550bdd7f239 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ബയോമെട്രിക്സ് അല്ലെങ്കിൽ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ഇത് നിങ്ങളാണെന്ന് പരിശോധിച്ചുറപ്പിക്കുക"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"തുടരാൻ ബയോമെട്രിക് ഉപയോഗിക്കുക"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"തുടരാൻ നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"തുടരാൻ നിങ്ങളുടെ മുഖം ഉപയോഗിക്കുക"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"തുടരാൻ നിങ്ങളുടെ ബയോമെട്രിക് അല്ലെങ്കിൽ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ബയോമെട്രിക് ഹാർഡ്വെയർ ലഭ്യമല്ല"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കി"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ഫിംഗർപ്രിന്റ് തിരിച്ചറിഞ്ഞില്ല"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ഫിംഗർപ്രിന്റ് തിരിച്ചറിഞ്ഞില്ല"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"മുഖം തിരിച്ചറിയാനായില്ല. പകരം ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കൂ."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു, സ്ഥിരീകരിക്കുക അമർത്തുക"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 0ea1b080bc4a..5205d1e0fc40 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Биометр эсвэл дэлгэцийн түгжээ ашиглах"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Өөрийгөө мөн гэдгийг баталгаажуулаарай"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Үргэлжлүүлэхийн тулд биометрээ ашиглана уу"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Үргэлжлүүлэхийн тулд хурууныхаа хээг ашиглана уу"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Үргэлжлүүлэхийн тулд царайгаа ашиглана уу"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Үргэлжлүүлэхийн тулд биометр эсвэл дэлгэцийн түгжээгээ ашиглана уу"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрийн техник хангамж боломжгүй байна"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Нотолгоог цуцаллаа"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Хурууны хээг таньсангүй"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Хурууны хээг таньсангүй"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Царай таних боломжгүй. Оронд нь хурууны хээ ашигла"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Хурууны хээг нотолсон"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Царайг баталгаажууллаа"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Царайг баталгаажууллаа. Баталгаажуулах товчлуурыг дарна уу"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index ff2e089805f1..c469e362c57a 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"बायोमेट्रिक किंवा स्क्रीन लॉक वापरा"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"हे तुम्हीच आहात याची पडताळणी करा"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"पुढे सुरू ठेवण्यासाठी तुमचे बायोमेट्रिक वापरा"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"पुढे सुरू ठेवण्यासाठी तुमची फिंगरप्रिंट वापरा"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"पुढे सुरू ठेवण्यासाठी तुमचा चेहरा वापरा"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"पुढे सुरू ठेवण्यासाठी तुमचे बायोमेट्रिक किंवा स्क्रीन लॉक वापरा"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेअर उपलब्ध नाही"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ऑथेंटिकेशन रद्द केले"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"फिंगरप्रिंट ओळखली नाही"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"फिंगरप्रिंट ओळखली नाही"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"चेहरा ओळखू शकत नाही. त्याऐवजी फिंगरप्रिंट वापरा."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरा ऑथेंटिकेशन केलेला आहे"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कंफर्म प्रेस करा"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index e98caa6d20e7..6ee7721aa2b2 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gunakan biometrik atau kunci skrin"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Sahkan itu anda"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gunakan biometrik anda untuk meneruskan"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Gunakan cap jari anda untuk teruskan"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Gunakan muka anda untuk teruskan"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Gunakan biometrik atau kunci skrin anda untuk meneruskan pengesahan"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Perkakasan biometrik tidak tersedia"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Pengesahan dibatalkan"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Cap jari tidak dikenali"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Cap jari tidak dikenali"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Tidak mengenali wajah. Gunakan cap jari."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Cap jari disahkan"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah disahkan"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah disahkan, sila tekan sahkan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index d67390420c66..51cf28505729 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ဇီဝမက်ထရစ်အချက်အလက်များ (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"သင်ဖြစ်ကြောင်း အတည်ပြုပါ"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ရှေ့ဆက်ရန် သင်၏ ဇီဝမက်ထရစ်အချက်အလက်ကို သုံးပါ"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ရှေ့ဆက်ရန် သင့်လက်ဗွေကို သုံးပါ"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ရှေ့ဆက်ရန် သင့်မျက်နှာကို သုံးပါ"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ရှေ့ဆက်ရန် သင်၏ ဇီဝမက်ထရစ်အချက်အလက် (သို့) ဖန်သားပြင်လော့ခ်ကို သုံးပါ"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ဇီဝအချက်အလက်သုံး ကွန်ပျူတာစက်ပစ္စည်း မရရှိနိုင်ပါ"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"လက်ဗွေကို မသိရှိပါ"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"လက်ဗွေကို မသိရှိပါ"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"မျက်နှာကို မမှတ်မိပါ။ လက်ဗွေကို အစားထိုးသုံးပါ။"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 50bdebf39b57..d48e831ed21c 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Bruk biometri eller skjermlås"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Bekreft at det er deg"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Bruk biometri for å fortsette"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Bruk fingeravtrykket for å fortsette"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Bruk ansiktet for å fortsette"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Bruk biometri eller skjermlåsen for å fortsette"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk maskinvare er utilgjengelig"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentiseringen er avbrutt"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Gjenkjenner ikke fingeravtrykket"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Gjenkjenner ikke fingeravtrykket"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Ansiktet gjenkjennes ikke. Bruk fingeravtrykk."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrykket er godkjent"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet er autentisert"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet er autentisert. Trykk på Bekreft"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index d95d750ce1ce..65ea4702277b 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"बायोमेट्रिक्स वा स्क्रिन लक प्रयोग गर्नुहोस्"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"यो व्यक्ति तपाईं नै हो भन्ने प्रमाणित गर्नुहोस्"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"जारी राख्न आफ्नो बायोमेट्रिक प्रयोग गर्नुहोस्"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"जारी राख्न आफ्नो फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"जारी राख्न आफ्नो अनुहार प्रयोग गर्नुहोस्"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"जारी राख्न आफ्नो बायोमेट्रिक वा स्क्रिन लक प्रयोग गरी पुष्टि गर्नुहोस्"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"प्रमाणीकरण रद्द गरियो"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"फिंगरप्रिन्ट पहिचान गर्न सकिएन"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"फिंगरप्रिन्ट पहिचान गर्न सकिएन"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"अनुहार पहिचान गर्न सकिएन। बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्।"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"अनुहार प्रमाणीकरण गरियो"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 9b1e982ed052..9a00d68a4362 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrische gegevens of schermvergrendeling gebruiken"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Je identiteit verifiëren"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gebruik je biometrische gegevens om door te gaan"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Gebruik je vingerafdruk om door te gaan"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Gebruik je gezicht om door te gaan"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Gebruik je biometrische gegevens of schermvergrendeling om door te gaan"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrische hardware niet beschikbaar"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Verificatie geannuleerd"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Vingerafdruk niet herkend"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Vingerafdruk niet herkend"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Gezicht niet herkend. Gebruik je vingerafdruk."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk geverifieerd"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gezicht geverifieerd"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gezicht geverifieerd. Druk op Bevestigen."</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Hiermee kan een bijbehorende app services op de voorgrond vanuit de achtergrond starten."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Microfoon is beschikbaar"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Microfoon is geblokkeerd"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dubbel scherm"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dubbel scherm staat aan"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen staat aan"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruikt beide schermen om content te tonen"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Het apparaat is te warm"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dubbel scherm is niet beschikbaar, omdat je telefoon te warm wordt"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen is niet beschikbaar, omdat je telefoon te warm wordt"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen is niet beschikbaar"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen is niet beschikbaar omdat Batterijbesparing aanstaat. Je kunt dit uitzetten via Instellingen."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Naar Instellingen"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 3e8f9d4abb4e..5668e2582337 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ବାୟୋମେଟ୍ରିକ୍ସ ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ଏହା ଆପଣ ବୋଲି ଯାଞ୍ଚ କରନ୍ତୁ"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ବାୟୋମେଟ୍ରିକ୍ସ ବ୍ୟବହାର କରନ୍ତୁ"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଫେସ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ବାୟୋମେଟ୍ରିକ୍ କିମ୍ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ବାୟୋମେଟ୍ରିକ୍ ହାର୍ଡୱେର୍ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ପ୍ରାମାଣିକତାକୁ ବାତିଲ୍ କରାଯାଇଛି"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ଟିପଚିହ୍ନ ଚିହ୍ନଟ ହେଲା ନାହିଁ"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ଟିପଚିହ୍ନ ଚିହ୍ନଟ ହେଲା ନାହିଁ"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"ଫେସ୍ ଚିହ୍ନଟ କରିହେବ ନାହିଁ। ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ।"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ଟିପଚିହ୍ନ ପ୍ରମାଣିତ ହେଲା"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି, ଦୟାକରି ସୁନିଶ୍ଚିତ ଦବାନ୍ତୁ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index c287bf6eb66c..043c296c14da 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ਆਪਣੀ ਪਛਾਣ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣੇ ਬਾਇਓਮੈਟ੍ਰਿਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣੇ ਚਿਹਰੇ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਬਾਇਓਮੈਟ੍ਰਿਕ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ਪ੍ਰਮਾਣੀਕਰਨ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"ਚਿਹਰਾ ਨਹੀਂ ਪਛਾਣ ਸਕਦੇ। ਇਸਦੀ ਬਜਾਏ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ।"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ਸੰਬੰਧੀ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਤੋਂ ਫੋਰਗ੍ਰਾਊਂਡ ਸੇਵਾਵਾਂ ਸ਼ੁਰੂ ਕਰਨ ਦੀ ਆਗਿਆ ਮਿਲਦੀ ਹੈ।"</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਉਪਲਬਧ ਹੈ"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"ਦੋਹਰੀ ਸਕ੍ਰੀਨ"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ਦੋਹਰੀ ਸਕ੍ਰੀਨ ਚਾਲੂ ਹੈ"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen ਚਾਲੂ ਹੈ"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਸਮੱਗਰੀ ਨੂੰ ਦਿਖਾਉਣ ਲਈ ਦੋਵੇਂ ਡਿਸਪਲੇਆਂ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"ਡੀਵਾਈਸ ਬਹੁਤ ਗਰਮ ਹੈ"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"ਦੋਹਰੀ ਸਕ੍ਰੀਨ ਵਿਸ਼ੇਸ਼ਤਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਤੁਹਾਡਾ ਫ਼ੋਨ ਬਹੁਤ ਗਰਮ ਹੋ ਰਿਹਾ ਹੈ"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen ਵਿਸ਼ੇਸ਼ਤਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਤੁਹਾਡਾ ਫ਼ੋਨ ਬਹੁਤ ਗਰਮ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen ਵਿਸ਼ੇਸ਼ਤਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen ਵਿਸ਼ੇਸ਼ਤਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਬੰਦ ਕਰ ਸਕਦੇ ਹੋ।"</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index cf2571be3629..709b067caa4e 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Używaj biometrii lub blokady ekranu"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potwierdź, że to Ty"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Użyj biometrii, by kontynuować"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Aby kontynuować, użyj odcisku palca"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Aby kontynuować, użyj rozpoznawania twarzy"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Aby kontynuować, użyj biometrii lub blokady ekranu"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Sprzęt biometryczny niedostępny"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Anulowano uwierzytelnianie"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Nie rozpoznano odcisku palca"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Nie rozpoznano odcisku palca"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Nie rozpoznaję twarzy. Użyj odcisku palca."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Uwierzytelniono odciskiem palca"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Twarz rozpoznana"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Twarz rozpoznana, kliknij Potwierdź"</string> @@ -2337,7 +2336,7 @@ <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Włączono podwójny ekran"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> korzysta z obu wyświetlaczy, aby pokazać treści"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Urządzenie jest za ciepłe"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Podwójny ekran jest niedostępny, ponieważ telefon za bardzo się nagrzewa"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Funkcja Dual Screen jest niedostępna, ponieważ telefon za bardzo się nagrzewa"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Funkcja Dual Screen jest niedostępna"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Funkcja Dual Screen jest niedostępna, ponieważ włączono Oszczędzanie baterii. Możesz to wyłączyć w Ustawieniach."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Otwórz ustawienia"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 2ca6ec765326..eb6aaeb78cc5 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar biometria ou bloqueio de tela"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirme que é você"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use seus dados biométricos para continuar"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use sua impressão digital para continuar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use seu rosto para continuar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Use sua autenticação biométrica ou o bloqueio de tela para continuar"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Impressão digital não reconhecida"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Impressão digital não reconhecida"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Não foi possível reconhecer o rosto Use a impressão digital."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 83da4b5ff8e9..25a816ce9378 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar a biometria ou o bloqueio de ecrã"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirme a sua identidade"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Utilize a biometria para continuar."</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use a impressão digital para continuar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use o rosto para continuar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Utilize a biometria ou o bloqueio de ecrã para continuar"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível."</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Impressão digital não reconhecida"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Impressão digital não reconhecida"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Impos. reconh. rosto. Utilize a impressão digital."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"A impressão digital foi autenticada."</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado."</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado. Prima Confirmar."</string> @@ -966,7 +965,7 @@ <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"Código PIN incorreto."</string> <string name="keyguard_label_text" msgid="3841953694564168384">"Para desbloquear, prima Menu e, em seguida, 0."</string> <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Número de emergência"</string> - <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Sem rede móvel"</string> + <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Sem dados"</string> <string name="lockscreen_screen_locked" msgid="7364905540516041817">"Ecrã bloqueado."</string> <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Prima Menu para desbloquear."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 2ca6ec765326..eb6aaeb78cc5 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Usar biometria ou bloqueio de tela"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirme que é você"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Use seus dados biométricos para continuar"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Use sua impressão digital para continuar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Use seu rosto para continuar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Use sua autenticação biométrica ou o bloqueio de tela para continuar"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Impressão digital não reconhecida"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Impressão digital não reconhecida"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Não foi possível reconhecer o rosto Use a impressão digital."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 345f67fbe485..eca2eb3923f8 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Folosește sistemele biometrice sau blocarea ecranului"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmă-ți identitatea"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Folosește sistemele biometrice pentru a continua"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Folosește amprenta pentru a continua"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Folosește-ți chipul pentru a continua"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Folosește sistemele biometrice sau blocarea ecranului pentru a continua"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometric indisponibil"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentificarea a fost anulată"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Amprenta nu a fost recunoscută"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Amprenta nu a fost recunoscută"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Chipul nu a fost recunoscut. Folosește amprenta."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Chip autentificat"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apasă pe Confirmă"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index efebc8a11689..8326ba8dba8f 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Использовать биометрию или блокировку экрана"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Подтвердите, что это вы"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Чтобы продолжить, используйте биометрические данные."</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Чтобы продолжить, прикоснитесь пальцем к сканеру."</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Чтобы продолжить, используйте фейсконтроль."</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Чтобы продолжить, используйте биометрию или данные для разблокировки экрана."</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрическое оборудование недоступно"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аутентификация отменена"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отпечаток не распознан."</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Отпечаток не распознан."</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Не удалось распознать лицо. Используйте отпечаток."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечаток пальца проверен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицо распознано"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string> @@ -2333,11 +2332,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Сопутствующее приложение сможет запускать активные службы из фонового режима."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофон доступен."</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон заблокирован."</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Двойной экран"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Двойной экран включен"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Функция Dual Screen включена"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> использует оба экрана."</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Устройство перегрелось"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Двойной экран недоступен из-за перегрева телефона."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Функция Dual Screen недоступна из-за перегрева телефона."</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Функция Dual Screen недоступна"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Функция Dual Screen недоступна, так как включен режим энергосбережения. Вы можете отключить его в настройках."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Открыть настройки"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 4a0b7159fb99..f81ff6346327 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ජෛවමිතික හෝ තිර අගුල භාවිත කරන්න"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"එය ඔබ බව තහවුරු කරන්න"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ඉදිරියට යාමට ඔබගේ ජෛවමිතික භාවිත කරන්න"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ඉදිරියට යාමට ඔබේ ඇඟිලි සලකුණ භාවිත කරන්න"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ඉදිරියට යාමට ඔබේ මුහුණ භාවිත කරන්න"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ඉදිරියට යාමට ඔබගේ ජෛවමිතික හෝ තිර අගුල භාවිත කරන්න"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ජීවමිතික දෘඪාංග ලබා ගත නොහැකිය"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"සත්යාපනය අවලංගු කළා"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ඇඟිලි සලකුණ හඳුනා නොගන්නා ලදි"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ඇඟිලි සලකුණ හඳුනා නොගන්නා ලදි"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"මුහුණ හැඳිනිය නොහැක. ඒ වෙනුවට ඇඟිලි සලකුණ භාවිත ක."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"මුහුණ සත්යාපනය කරන ලදී"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"මුහුණ සත්යාපනය කරන ලදී, කරුණාකර තහවුරු කරන්න ඔබන්න"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 0a1b159324c8..7bcf11907627 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Použiť biometrické údaje alebo zámku obrazovky"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Overenie, že ste to vy"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Ak chcete pokračovať, použite biometrický údaj"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Pokračujte nasnímaním odtlačku prsta"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Pokračujte nasnímaním tváre"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Pokračujte použitím biometrických údajov alebo zámky obrazovky"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrický hardvér nie je k dispozícii"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Overenie bolo zrušené"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Odtlačok prsta nebol rozpoznaný"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Odtlačok prsta nebol rozpoznaný"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Tvár sa nedá rozpoznať. Použite odtlačok prsta."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Odtlačok prsta bol overený"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Tvár bola overená"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Tvár bola overená, stlačte tlačidlo potvrdenia"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 00cf50bc8db8..2035979681fc 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Uporaba biometrike ali odklepanja s poverilnico"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Preverite, da ste res vi"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Za nadaljevanje uporabite biometrični podatek."</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Za nadaljevanje uporabite prstni odtis"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Za nadaljevanje uporabite obraz"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Za nadaljevanje uporabite biometrični podatek ali odklepanje s poverilnico."</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Strojna oprema za biometrične podatke ni na voljo"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Preverjanje pristnosti je preklicano"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Prstni odtis ni prepoznan."</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Prstni odtis ni prepoznan."</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Obraza ni mogoče prepoznati. Uporabite prstni odtis."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Pristnost prstnega odtisa je preverjena"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Pristnost obraza je potrjena"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Pristnost obraza je preverjena. Pritisnite gumb »Potrdi«."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 426b25562e23..a2e851a47def 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Përdor sistemet biometrike ose kyçjen e ekranit"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifiko që je ti"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Përdor sistemet e tua biometrike për të vazhduar"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Përdor gjurmën e gishtit për të vazhduar"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Përdor fytyrën tënde për të vazhduar"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Përdor sistemet e tua biometrike ose kyçjen e ekranit për të vazhduar"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Nuk ofrohet harduer biometrik"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Vërtetimi u anulua"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Gjurma e gishtit nuk u njoh"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Gjurma e gishtit nuk u njoh"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Nuk mund ta dallojë fytyrën. Përdor më mirë gjurmën e gishtit."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Gjurma e gishtit u vërtetua"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Fytyra u vërtetua"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Fytyra u vërtetua, shtyp \"Konfirmo\""</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index a2aee9ca99ee..a13d56dc609e 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -619,8 +619,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Користите биометрију или закључавање екрана"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Потврдите свој идентитет"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Користите биометријски податак да бисте наставили"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Наставите помоћу отиска прста"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Потврдите идентитет лицем да бисте наставили"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Користите биометријски податак или закључавање екрана да бисте наставили"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометријски хардвер није доступан"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Потврда идентитета је отказана"</string> @@ -646,6 +644,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отисак прста није препознат"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Отисак прста није препознат"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Лице није препознато. Користите отисак прста."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отисак прста је потврђен"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лице је потврђено"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лице је потврђено. Притисните Потврди"</string> @@ -2332,11 +2331,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Дозвољава пратећој апликацији да покрене услуге у првом плану из позадине."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофон је доступан"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон је блокиран"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Двојни екран"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Двојни екран је укључен"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen је укључен"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи оба екрана за приказивање садржаја"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Уређај је превише загрејан"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Двојни екран је недоступан јер је телефон превише загрејан"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen је недоступан јер је телефон превише загрејан"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen није доступан"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen није доступан зато што је Уштеда батерије укључена. То можете да искључите у подешавањима."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Иди у Подешавања"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index af2e1ea2f058..0a43e463166a 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Använd biometrisk data eller skärmlåset"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifiera din identitet"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Fortsätt med hjälp av din biometriska data"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Fortsätt med hjälp av ditt fingeravtryck"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Fortsätt med hjälp av ditt ansikte"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Fortsätt med hjälp av din biometriska data eller skärmlåset"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk maskinvara är inte tillgänglig"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentiseringen avbröts"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingeravtrycket känns inte igen"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Fingeravtrycket känns inte igen"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Ansiktet kändes inte igen. Använd fingeravtryck."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet har autentiserats"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Tillåter att en tillhörande app startar förgrundstjänster i bakgrunden."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofonen är tillgänglig"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofonen är blockerad"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dubbel skärm"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dubbel skärm är på"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen är på"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> använder båda skärmarna för att visa innehåll"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Enheten är för varm"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dubbel skärm kan inte användas eftersom telefonen börjar bli för varm"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen kan inte användas eftersom telefonen börjar bli för varm"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen är inte tillgängligt"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen kan inte användas eftersom battersparläget är aktiverat. Du kan inaktivera detta i inställningarna."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Öppna inställningarna"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d77385c126e2..466df4fdffca 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Tumia bayometriki au mbinu ya kufunga skrini"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Thibitisha kuwa ni wewe"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Tumia bayometriki yako ili uendelee"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Tumia alama ya kidole chako ili uendelee"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Tumia uso wako ili uendelee"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Tumia bayometriki au mbinu yako ya kufunga skrini ili uendelee"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Maunzi ya bayometriki hayapatikani"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Imeghairi uthibitishaji"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Alama ya kidole haijatambuliwa"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Alama ya kidole haijatambuliwa"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Imeshindwa kutambua uso. Tumia alama ya kidole."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Imethibitisha alama ya kidole"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Uso umethibitishwa"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string> @@ -2332,12 +2331,12 @@ <string name="mic_access_on_toast" msgid="2666925317663845156">"Maikrofoni inapatikana"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Maikrofoni imezuiwa"</string> <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Umewasha kipengele cha hali ya skrini mbili"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen imewasha"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumia skrini zote kuonyesha maudhui"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Kifaa kina joto sana"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Kipengele cha Hali ya Skrini Mbili hakipatikani kwa sababu simu yako inapata joto sana"</string> - <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Kipengele cha Hali ya Skrini Mbili hakipatikani"</string> - <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Kipengele cha Hali ya Skrini Mbili hakipatikani kwa sababu kipengele cha Kiokoa Betri kimewashwa. Unaweza kuzima kipengele hiki katika Mipangilio."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Kipengele cha Dual Screen hakipatikani kwa sababu simu yako inapata joto sana"</string> + <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen haipatikani"</string> + <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Kipengele cha Dual Screen hakipatikani kwa sababu Kiokoa Betri kimewashwa. Unaweza kukizima katika Mipangilio."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Nenda kwenye Mipangilio"</string> <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"Zima"</string> <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> imewekewa mipangilio"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 50d056ae3aea..b2551e72df9a 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"பயோமெட்ரிக்ஸையோ திரைப் பூட்டையோ பயன்படுத்து"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"நீங்கள்தான் என உறுதிசெய்க"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"தொடர உங்கள் பயோமெட்ரிக்கைப் பயன்படுத்துங்கள்"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"தொடர்வதற்கு உங்கள் கைரேகையைப் பயன்படுத்துங்கள்"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"தொடர்வதற்கு உங்கள் முகத்தைப் பயன்படுத்துங்கள்"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"தொடர, உங்கள் பயோமெட்ரிக்கையோ திரைப் பூட்டையோ பயன்படுத்துங்கள்"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"பயோமெட்ரிக் வன்பொருள் இல்லை"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"அங்கீகரிப்பு ரத்தானது"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"கைரேகை அங்கீகரிக்கப்படவில்லை"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"கைரேகை அங்கீகரிக்கப்படவில்லை"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"முகத்தை அடையாளம் காண முடியவில்லை. கைரேகையைப் பயன்படுத்தவும்."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"கைரேகை அங்கீகரிக்கப்பட்டது"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"முகம் அங்கீகரிக்கப்பட்டது"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"முகம் அங்கீகரிக்கப்பட்டது. ’உறுதிப்படுத்துக’ என்பதை அழுத்துக"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 32ef5bd6d2a8..a90b2bf332ee 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"బయోమెట్రిక్స్ను లేదా స్క్రీన్ లాక్ను ఉపయోగించండి"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ఇది మీరేనని వెరిఫై చేసుకోండి"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"కొనసాగించడానికి, మీ బయోమెట్రిక్ను ఉపయోగించండి"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"కొనసాగించడానికి మీ వేలిముద్రను ఉపయోగించండి"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"కొనసాగించడానికి మీ ముఖాన్ని ఉపయోగించండి"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"కొనసాగించడానికి మీ బయోమెట్రిక్ లేదా స్క్రీన్ లాక్ను ఉపయోగించండి"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"బయోమెట్రిక్ హార్డ్వేర్ అందుబాటులో లేదు"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ప్రమాణీకరణ రద్దు చేయబడింది"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"వేలిముద్ర గుర్తించబడలేదు"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"వేలిముద్ర గుర్తించబడలేదు"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"ముఖం గుర్తించలేము. బదులుగా వేలిముద్ర ఉపయోగించండి."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"వేలిముద్ర ప్రమాణీకరించబడింది"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ముఖం ప్రమాణీకరించబడింది"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ముఖం ప్రమాణీకరించబడింది, దయచేసి ధృవీకరించును నొక్కండి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 1f650867d16d..74b744f0ffed 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ใช้ข้อมูลไบโอเมตริกหรือการล็อกหน้าจอ"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"ยืนยันว่าเป็นตัวคุณ"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ใช้ข้อมูลไบโอเมตริกเพื่อดำเนินการต่อ"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"ใช้ลายนิ้วมือของคุณเพื่อดำเนินการต่อ"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"ใช้ใบหน้าของคุณเพื่อดำเนินการต่อ"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ใช้ข้อมูลไบโอเมตริกหรือการล็อกหน้าจอเพื่อดำเนินการต่อ"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ฮาร์ดแวร์ไบโอเมตริกไม่พร้อมใช้งาน"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"ยกเลิกการตรวจสอบสิทธิ์แล้ว"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ไม่รู้จักลายนิ้วมือ"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ไม่รู้จักลายนิ้วมือ"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"ไม่รู้จักใบหน้า ใช้ลายนิ้วมือแทน"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ตรวจสอบสิทธิ์ใบหน้าแล้ว โปรดกดยืนยัน"</string> @@ -1404,7 +1403,7 @@ <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"แตะเพื่อเลือกภาษาและรูปแบบ"</string> <string name="fast_scroll_alphabet" msgid="8854435958703888376">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string> <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string> - <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"แสดงทับแอปอื่นๆ"</string> + <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"แสดงทับบนแอปอื่นๆ"</string> <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> แสดงทับแอปอื่นๆ"</string> <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> กำลังแสดงทับแอปอื่นๆ"</string> <string name="alert_windows_notification_message" msgid="6538171456970725333">"หากคุณไม่ต้องการให้ <xliff:g id="NAME">%s</xliff:g> ใช้ฟีเจอร์นี้ ให้แตะเพื่อเปิดการตั้งค่าแล้วปิดฟีเจอร์"</string> @@ -2335,7 +2334,7 @@ <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen เปิดอยู่"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังใช้จอแสดงผลทั้งสองจอเพื่อแสดงเนื้อหา"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"อุปกรณ์ร้อนเกินไป"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"หน้าจอคู่ไม่พร้อมให้ใช้งานเนื่องจากโทรศัพท์ของคุณร้อนเกินไป"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen ไม่พร้อมให้ใช้งานเนื่องจากโทรศัพท์ของคุณร้อนเกินไป"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen ใช้งานไม่ได้"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen ใช้งานไม่ได้เนื่องจากเปิดโหมดประหยัดแบตเตอรี่อยู่ คุณปิดโหมดนี้ได้ในการตั้งค่า"</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"ไปที่การตั้งค่า"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 567b67e2cba5..633282ee6a94 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gumamit ng biometrics o lock ng screen"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"I-verify na ikaw ito"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gamitin ang iyong biometric para magpatuloy"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Gamitin ang iyong fingerprint para magpatuloy"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Gamitin ang iyong mukha para magpatuloy"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Gamitin ang iyong biometric o lock ng screen para magpatuloy"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Walang biometric hardware"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Nakansela ang pag-authenticate"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Hindi nakilala ang fingerprint"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Hindi nakilala ang fingerprint"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Hindi makilala ang mukha. Gumamit ng fingerprint."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Na-authenticate ang fingerprint"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Na-authenticate ang mukha"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Na-authenticate ang mukha, pakipindot ang kumpirmahin"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 3bf35a5a526f..17db4f418e87 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biyometri veya ekran kilidi kullan"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Siz olduğunuzu doğrulayın"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Devam etmek için biyometri kullanın"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Devam etmek için parmak izinizi kullanın"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Devam etmek için yüzünüzü kullanın"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Devam etmek için biyometrik kimlik bilginizi veya ekran kilidinizi kullanın"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biyometrik donanım kullanılamıyor"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Kimlik doğrulama iptal edildi"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Parmak izi tanınmadı"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Parmak izi tanınmadı"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Yüz tanınamadı. Bunun yerine parmak izi kullanın."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Parmak izi kimlik doğrulaması yapıldı"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yüz kimliği doğrulandı"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string> @@ -2331,11 +2330,11 @@ <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Tamamlayıcı uygulamanın arka plandan ön plan hizmetlerini başlatmasına izin verir."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofon kullanılabilir"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon engellenmiş"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Çift ekran"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Çift ekran açık"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen açık"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g>, içeriği göstermek için her iki ekranı da kullanıyor"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Cihaz çok ısındı"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Telefonunuz çok ısındığı için Çift Ekran kullanılamıyor"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Telefonunuz çok ısındığı için Dual Screen kullanılamıyor"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen kullanılamıyor"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Pil Tasarrufu açık olduğundan Dual Screen kullanılamıyor. Bu özelliği Ayarlar\'dan kapatabilirsiniz."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Ayarlar\'a git"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 47e529c3de06..0606c7a2d462 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -620,8 +620,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Використовувати біометрію або дані для розблокування екрана"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Підтвердьте, що це ви"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Щоб продовжити, скористайтеся біометрією"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Щоб продовжити, скористайтеся відбитком пальця"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Щоб продовжити, скористайтеся фейс-контролем"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Щоб продовжити, скористайтеся біометрією або даними для розблокування екрана"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Біометричне апаратне забезпечення недоступне"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Автентифікацію скасовано"</string> @@ -647,6 +645,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Відбиток пальця не розпізнано"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Відбиток пальця не розпізнано"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Обличчя не розпізнано. Скористайтеся відбитком пальця."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Відбиток пальця автентифіковано"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Обличчя автентифіковано"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string> @@ -2337,7 +2336,7 @@ <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen увімкнено"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> використовує обидва екрани для показу контенту"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"Пристрій сильно нагрівається"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Подвійний екран недоступний, оскільки телефон сильно нагрівається"</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Функція Dual Screen недоступна, оскільки телефон сильно нагрівається"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Функція Dual Screen недоступна"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Функція Dual Screen недоступна, оскільки ввімкнено режим енергозбереження. Її можна вимкнути в налаштуваннях."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Перейти до налаштувань"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 907ebd95a862..dc4ea46875b2 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"بایو میٹرکس یا اسکرین لاک استعمال کریں"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"توثیق کریں کہ یہ آپ ہیں"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"جاری رکھنے کیلئے اپنا بایو میٹرک استعمال کریں"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"جاری رکھنے کے لیے اپنا فنگر پرنٹ استعمال کریں"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"جاری رکھنے کے لیے اپنے چہرے کا استعمال کریں"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"جاری رکھنے کے لیے اپنے بایو میٹرک اور اسکرین لاک کا استعمال کریں"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"تصدیق کا عمل منسوخ ہو گیا"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"فنگر پرنٹ کی شناخت نہیں ہو سکی"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"فنگر پرنٹ کی شناخت نہیں ہو سکی"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"چہرے کی شناخت نہیں ہو سکی۔ اس کے بجائے فنگر پرنٹ استعمال کریں۔"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"فنگر پرنٹ کی تصدیق ہو گئی"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چہرے کی تصدیق ہو گئی"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چہرے کی تصدیق ہو گئی، براہ کرم \'تصدیق کریں\' کو دبائيں"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index a65b4780f118..979c578f4cad 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biometrika yoki ekran qulfi"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Oʻzingizni taniting"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Davom etish uchun biometrik tasdiqlang"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Davom etish uchun barmoq izingizdan foydalaning"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Yuz tekshiruvi bilan davom eting"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Davom etish uchun biometrika yoki ekran qulfidan foydalaning"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrik sensor ishlamayapti"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikatsiya bekor qilindi"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Barmoq izi aniqlanmadi"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Barmoq izi aniqlanmadi"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Bu yuz notanish. Barmoq izi orqali urining."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmoq izi tekshirildi"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yuzingiz aniqlandi"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yuzingiz aniqlandi, tasdiqlash uchun bosing"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 9c772995ba6b..58cec01894e4 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Dùng dữ liệu sinh trắc học hoặc phương thức khóa màn hình"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh danh tính của bạn"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Dùng dữ liệu sinh trắc học của bạn để tiếp tục"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Hãy dùng vân tay để tiếp tục"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Hãy dùng khuôn mặt để tiếp tục"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Dùng dữ liệu sinh trắc học của bạn hoặc phương thức khóa màn hình để tiếp tục"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Không nhận dạng được vân tay"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Không nhận dạng được vân tay"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Không thể nhận dạng khuôn mặt. Hãy dùng vân tay."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Đã xác thực vân tay"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Đã xác thực khuôn mặt"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Đã xác thực khuôn mặt, vui lòng nhấn để xác nhận"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 955cbe793e36..e5535afeca38 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -274,7 +274,7 @@ <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"实体键盘"</string> <string name="notification_channel_security" msgid="8516754650348238057">"安全性"</string> <string name="notification_channel_car_mode" msgid="2123919247040988436">"车载模式"</string> - <string name="notification_channel_account" msgid="6436294521740148173">"帐号状态"</string> + <string name="notification_channel_account" msgid="6436294521740148173">"账号状态"</string> <string name="notification_channel_developer" msgid="1691059964407549150">"开发者消息"</string> <string name="notification_channel_developer_important" msgid="7197281908918789589">"重要开发者消息"</string> <string name="notification_channel_updates" msgid="7907863984825495278">"更新"</string> @@ -448,9 +448,9 @@ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"允许应用发送置顶广播,这类广播在广播结束后仍会继续存在。过度使用这项功能可能会导致 Android TV 设备使用过多内存,从而降低其运行速度或稳定性。"</string> <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"允许该应用发送持久广播消息,此类消息在广播结束后仍会保留。过度使用可能会导致手机使用过多内存,从而降低其速度或稳定性。"</string> <string name="permlab_readContacts" msgid="8776395111787429099">"读取联系人"</string> - <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"允许该应用读取您的平板电脑上存储的联系人相关数据。应用还将有权访问您的平板电脑上已创建联系人的帐号,其中可能包括您已安装的应用所创建的帐号。此权限允许应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string> - <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"允许该应用读取您的 Android TV 设备上存储的联系人相关数据。应用还将有权访问您的 Android TV 设备上已创建联系人的帐号,其中可能包括您已安装的应用所创建的帐号。此权限允许应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string> - <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"允许该应用读取您手机上存储的联系人相关数据。应用还将有权访问您的手机上已创建联系人的帐号,其中可能包括您已安装的应用所创建的帐号。此权限允许应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string> + <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"允许该应用读取您的平板电脑上存储的联系人相关数据。应用还将有权访问您的平板电脑上已创建联系人的账号,其中可能包括您已安装的应用所创建的账号。此权限允许应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string> + <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"允许该应用读取您的 Android TV 设备上存储的联系人相关数据。应用还将有权访问您的 Android TV 设备上已创建联系人的账号,其中可能包括您已安装的应用所创建的账号。此权限允许应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string> + <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"允许该应用读取您手机上存储的联系人相关数据。应用还将有权访问您的手机上已创建联系人的账号,其中可能包括您已安装的应用所创建的账号。此权限允许应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string> <string name="permlab_writeContacts" msgid="8919430536404830430">"修改您的通讯录"</string> <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"允许该应用修改您平板电脑上存储的联系人相关数据。此权限允许应用删除联系人数据。"</string> <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"允许该应用修改您的 Android TV 设备上存储的联系人相关数据。此权限允许应用删除联系人数据。"</string> @@ -542,10 +542,10 @@ <string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"允许应用更改平板电脑的时区。"</string> <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"允许应用更改 Android TV 设备的时区。"</string> <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"允许应用更改手机的时区。"</string> - <string name="permlab_getAccounts" msgid="5304317160463582791">"查找设备上的帐号"</string> - <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"允许该应用获取平板电脑已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string> - <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"允许应用获取 Android TV 设备已知的帐号列表,其中可能包括您已安装的应用所创建的任何帐号。"</string> - <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"允许该应用获取手机已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string> + <string name="permlab_getAccounts" msgid="5304317160463582791">"查找设备上的账号"</string> + <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"允许该应用获取平板电脑已知的账号列表,其中可能包括由已安装的应用创建的所有账号。"</string> + <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"允许应用获取 Android TV 设备已知的账号列表,其中可能包括您已安装的应用所创建的任何账号。"</string> + <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"允许该应用获取手机已知的账号列表,其中可能包括由已安装的应用创建的所有账号。"</string> <string name="permlab_accessNetworkState" msgid="2349126720783633918">"查看网络连接"</string> <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"允许该应用查看网络连接的相关信息,例如存在和连接的网络。"</string> <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"拥有完全的网络访问权限"</string> @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物识别或屏幕锁定凭据"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"验证是您本人在操作"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"使用生物识别验证身份才能继续"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"如需继续操作,请使用指纹验证身份"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"如需继续操作,请刷脸验证身份"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"使用生物识别或屏幕锁定凭据验证身份,才能继续操作"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"生物识别硬件无法使用"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"身份验证已取消"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"未能识别指纹"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"未能识别指纹"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"无法识别人脸。请改用指纹。"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"已验证指纹"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已验证"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已验证,请按确认按钮"</string> @@ -732,11 +731,11 @@ <string name="face_error_vendor_unknown" msgid="7387005932083302070">"出了点问题,请重试。"</string> <string name="face_icon_content_description" msgid="465030547475916280">"面孔图标"</string> <string name="permlab_readSyncSettings" msgid="6250532864893156277">"读取同步设置"</string> - <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"允许该应用读取某个帐号的同步设置。例如,此权限可确定“联系人”应用是否与某个帐号同步。"</string> + <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"允许该应用读取某个账号的同步设置。例如,此权限可确定“联系人”应用是否与某个账号同步。"</string> <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"启用和停用同步"</string> - <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"允许该应用修改某个帐号的同步设置。例如,此权限可用于在“联系人”应用与某个帐号之间启用同步。"</string> + <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"允许该应用修改某个账号的同步设置。例如,此权限可用于在“联系人”应用与某个账号之间启用同步。"</string> <string name="permlab_readSyncStats" msgid="3747407238320105332">"读取同步统计信息"</string> - <string name="permdesc_readSyncStats" msgid="3867809926567379434">"允许该应用读取某个帐号的同步统计信息,包括同步活动历史记录和同步数据量。"</string> + <string name="permdesc_readSyncStats" msgid="3867809926567379434">"允许该应用读取某个账号的同步统计信息,包括同步活动历史记录和同步数据量。"</string> <string name="permlab_sdcardRead" msgid="5791467020950064920">"读取您共享存储空间中的内容"</string> <string name="permdesc_sdcardRead" msgid="6872973242228240382">"允许该应用读取您共享存储空间中的内容。"</string> <string name="permlab_readMediaAudio" msgid="8723513075731763810">"从共享存储空间读取音频文件"</string> @@ -1002,7 +1001,7 @@ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string> <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"您已经<xliff:g id="NUMBER_0">%1$d</xliff:g>次输错了PIN码。\n\n请在<xliff:g id="NUMBER_1">%2$d</xliff:g>秒后重试。"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"您已画错解锁图案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您登录 Google 帐号来解锁 Android TV 设备。\n\n 请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"您已画错解锁图案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您登录 Google 账号来解锁 Android TV 设备。\n\n 请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的 Google 登录信息解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次错误地尝试解锁平板电脑。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,平板电脑将恢复为出厂默认设置,所有用户数据都会丢失。"</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"您尝试解锁 Android TV 设备失败的次数已达 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,您的 Android TV 设备就会恢复出厂设置,而且所有用户数据都会丢失。"</string> @@ -1012,9 +1011,9 @@ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁手机。手机现在将恢复为出厂默认设置。"</string> <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g>秒后重试。"</string> <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"忘记了图案?"</string> - <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"帐号解锁"</string> + <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"账号解锁"</string> <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"图案尝试次数过多"</string> - <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"要解除锁定,请使用您的 Google 帐号登录。"</string> + <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"要解除锁定,请使用您的 Google 账号登录。"</string> <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"用户名(电子邮件)"</string> <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"密码"</string> <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"登录"</string> @@ -1482,14 +1481,14 @@ <string name="ime_action_default" msgid="8265027027659800121">"执行"</string> <string name="dial_number_using" msgid="6060769078933953531">"拨打电话\n<xliff:g id="NUMBER">%s</xliff:g>"</string> <string name="create_contact_using" msgid="6200708808003692594">"创建电话号码为\n<xliff:g id="NUMBER">%s</xliff:g> 的联系人"</string> - <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"以下一个或多个应用请求获得相应权限,以便在当前和以后访问您的帐号。"</string> + <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"以下一个或多个应用请求获得相应权限,以便在当前和以后访问您的账号。"</string> <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"您是否同意此请求?"</string> <string name="grant_permissions_header_text" msgid="3420736827804657201">"访问权限请求"</string> <string name="allow" msgid="6195617008611933762">"允许"</string> <string name="deny" msgid="6632259981847676572">"拒绝"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"权限请求"</string> - <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"应用对帐号 <xliff:g id="ACCOUNT">%s</xliff:g>\n 提出权限请求。"</string> - <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"“<xliff:g id="APP">%1$s</xliff:g>”请求获得以下帐号的访问权限:\n<xliff:g id="ACCOUNT">%2$s</xliff:g>。"</string> + <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"应用对账号 <xliff:g id="ACCOUNT">%s</xliff:g>\n 提出权限请求。"</string> + <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"“<xliff:g id="APP">%1$s</xliff:g>”请求获得以下账号的访问权限:\n<xliff:g id="ACCOUNT">%2$s</xliff:g>。"</string> <string name="forward_intent_to_owner" msgid="4620359037192871015">"您目前是在工作资料之外使用此应用"</string> <string name="forward_intent_to_work" msgid="3620262405636021151">"您目前是在工作资料内使用此应用"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"输入法"</string> @@ -1535,13 +1534,13 @@ <string name="gpsVerifYes" msgid="3719843080744112940">"是"</string> <string name="gpsVerifNo" msgid="1671201856091564741">"否"</string> <string name="sync_too_many_deletes" msgid="6999440774578705300">"超出删除限制"</string> - <string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"帐号 <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> 在进行“<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>”同步时删除了 <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> 项内容。您要如何处理这些删除的内容?"</string> + <string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"账号 <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> 在进行“<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>”同步时删除了 <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> 项内容。您要如何处理这些删除的内容?"</string> <string name="sync_really_delete" msgid="5657871730315579051">"删除这些内容"</string> <string name="sync_undo_deletes" msgid="5786033331266418896">"撤消删除"</string> <string name="sync_do_nothing" msgid="4528734662446469646">"目前不进行任何操作"</string> - <string name="choose_account_label" msgid="5557833752759831548">"选择帐号"</string> - <string name="add_account_label" msgid="4067610644298737417">"添加帐号"</string> - <string name="add_account_button_label" msgid="322390749416414097">"添加帐号"</string> + <string name="choose_account_label" msgid="5557833752759831548">"选择账号"</string> + <string name="add_account_label" msgid="4067610644298737417">"添加账号"</string> + <string name="add_account_button_label" msgid="322390749416414097">"添加账号"</string> <string name="number_picker_increment_button" msgid="7621013714795186298">"增大"</string> <string name="number_picker_decrement_button" msgid="5116948444762708204">"减小"</string> <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> 触摸并按住。"</string> @@ -1665,13 +1664,13 @@ <string name="kg_invalid_puk" msgid="4809502818518963344">"请重新输入正确的PUK码。如果尝试错误次数过多,SIM卡将永久停用。"</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"PIN 码不匹配"</string> <string name="kg_login_too_many_attempts" msgid="699292728290654121">"图案尝试次数过多"</string> - <string name="kg_login_instructions" msgid="3619844310339066827">"要解锁,请登录您的 Google 帐号。"</string> + <string name="kg_login_instructions" msgid="3619844310339066827">"要解锁,请登录您的 Google 账号。"</string> <string name="kg_login_username_hint" msgid="1765453775467133251">"用户名(电子邮件地址)"</string> <string name="kg_login_password_hint" msgid="3330530727273164402">"密码"</string> <string name="kg_login_submit_button" msgid="893611277617096870">"登录"</string> <string name="kg_login_invalid_input" msgid="8292367491901220210">"用户名或密码无效。"</string> <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"忘记了用户名或密码?\n请访问 "<b>"google.com/accounts/recovery"</b>"。"</string> - <string name="kg_login_checking_password" msgid="4676010303243317253">"正在检查帐号…"</string> + <string name="kg_login_checking_password" msgid="4676010303243317253">"正在检查账号…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"您已经<xliff:g id="NUMBER_0">%1$d</xliff:g>次输错了PIN码。\n\n请在<xliff:g id="NUMBER_1">%2$d</xliff:g>秒后重试。"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string> @@ -1681,9 +1680,9 @@ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁平板电脑。平板电脑现在将恢复为出厂默认设置。"</string> <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"您尝试解锁 Android TV 设备失败的次数已达 <xliff:g id="NUMBER">%d</xliff:g> 次。您的 Android TV 设备现在将恢复出厂设置。"</string> <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁手机。手机现在将恢复为出厂默认设置。"</string> - <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"您已连续 <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="tv" msgid="4670840383567106114">"您已画错解锁图案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用电子邮件帐号解锁 Android TV 设备。\n\n 请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string> - <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"您已连续 <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="tablet" msgid="7086799295109717623">"您已连续 <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="tv" msgid="4670840383567106114">"您已画错解锁图案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用电子邮件账号解锁 Android TV 设备。\n\n 请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string> + <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"您已连续 <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_text_message_separator" product="default" msgid="4503708889934976866">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"删除"</string> <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要将音量调高到建议的音量以上吗?\n\n长时间保持高音量可能会损伤听力。"</string> @@ -1733,7 +1732,7 @@ <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"放大功能"</string> <string name="user_switched" msgid="7249833311585228097">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string> <string name="user_switching_message" msgid="1912993630661332336">"正在切换为<xliff:g id="NAME">%1$s</xliff:g>…"</string> - <string name="user_logging_out_message" msgid="7216437629179710359">"正在将<xliff:g id="NAME">%1$s</xliff:g>退出帐号…"</string> + <string name="user_logging_out_message" msgid="7216437629179710359">"正在将<xliff:g id="NAME">%1$s</xliff:g>退出账号…"</string> <string name="owner_name" msgid="8713560351570795743">"机主"</string> <string name="guest_name" msgid="8502103277839834324">"访客"</string> <string name="error_message_title" msgid="4082495589294631966">"错误"</string> @@ -1938,7 +1937,7 @@ <string name="importance_from_user" msgid="2782756722448800447">"这些通知的重要程度由您来设置。"</string> <string name="importance_from_person" msgid="4235804979664465383">"这条通知涉及特定的人,因此被归为重要通知。"</string> <string name="notification_history_title_placeholder" msgid="7748630986182249599">"自定义应用通知"</string> - <string name="user_creation_account_exists" msgid="2239146360099708035">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g>(目前已有用户使用此帐号)创建新用户吗?"</string> + <string name="user_creation_account_exists" msgid="2239146360099708035">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g>(目前已有用户使用此账号)创建新用户吗?"</string> <string name="user_creation_adding" msgid="7305185499667958364">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 创建新用户吗?"</string> <string name="supervised_user_creation_label" msgid="6884904353827427515">"添加受监管用户"</string> <string name="language_selection_title" msgid="52674936078683285">"添加语言"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 899c314e5f11..935e757c79ba 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物識別或螢幕鎖定"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"驗證是你本人"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用使用生物識別驗證身分"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"如要繼續操作,請使用你的指紋驗證身分"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"如要繼續操作,請使用你的面孔驗證身分"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"請使用生物識別或螢幕鎖定功能驗證身分,才能繼續操作"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物識別硬件"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"已取消驗證"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"無法辨識指紋"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"無法辨識指紋"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"無法辨識面孔,請改用指紋完成驗證。"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"驗證咗指紋"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已經驗證"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已經驗證,請㩒一下 [確認]"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 717e0460cd51..3f93ecd0eda2 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物特徵辨識或螢幕鎖定功能"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"驗證你的身分"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用生物特徵辨識功能驗證身分"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"如要繼續操作,請使用指紋驗證身分"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"如要繼續操作,請使用臉孔驗證身分"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"請使用生物特徵辨識或螢幕鎖定功能驗證身分,才能繼續操作"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物特徵辨識硬體"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"已取消驗證"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"指紋辨識失敗"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"指紋辨識失敗"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"無法辨識臉孔,請改用指紋完成驗證。"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋驗證成功"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"臉孔驗證成功"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"臉孔驗證成功,請按下 [確認] 按鈕"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 88a35479f490..929af2327b6e 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -618,8 +618,6 @@ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Sebenzisa i-biometrics noma ukukhiya isikrini"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"Qinisekisa ukuthi nguwe"</string> <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Sebenzisa i-biometric yakho ukuze uqhubeke"</string> - <string name="biometric_dialog_fingerprint_subtitle" msgid="2520227942533751342">"Sebenzisa isigxivizo sakho somunwe ukuze uqhubeke"</string> - <string name="biometric_dialog_face_subtitle" msgid="5269284162191087084">"Sebenzisa ubuso bakho ukuze uqhubeke"</string> <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Sebenzisa i-biometric noma ukukhiya isikrini ukuze uqhubeke"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"I-Biometric hardware ayitholakali"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ukufakazela ubuqiniso kukhanseliwe"</string> @@ -645,6 +643,7 @@ </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Isigxivizo somunwe asaziwa"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Isigxivizo somunwe asaziwa"</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"Ayibazi ubuso. Sebenzisa izigxivizo zeminwe kunalokho."</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Izigxivizo zeminwe zigunyaziwe"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ubuso bufakazelwe ubuqiniso"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ukuqinisekiswa kobuso, sicela ucindezele okuthi qinisekisa"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d80cfa340dcb..f45499a38312 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2465,7 +2465,7 @@ duration of the vector animation automatically. --> <attr name="windowSplashScreenAnimationDuration" format="integer"/> - <!-- Place an drawable image in the bottom of the starting window, it can be used to + <!-- Place a drawable image in the bottom of the starting window. The image can be used to represent the branding of the application. --> <attr name="windowSplashScreenBrandingImage" format="reference"/> <!-- Set a background behind the splash screen icon. This is useful if there is not enough @@ -3245,7 +3245,7 @@ <!-- Specifies the id of a view for which this view serves as a label for accessibility purposes. For example, a TextView before an EditText in - the UI usually specifies what infomation is contained in the EditText. + the UI usually specifies what information is contained in the EditText. Hence, the TextView is a label for the EditText. --> <attr name="labelFor" format="reference" /> @@ -6787,7 +6787,7 @@ edges of a bitmap when rotated. Default value is false. --> <attr name="antialias" format="boolean" /> <!-- Enables or disables bitmap filtering. Filtering is used when the bitmap is - shrunk or stretched to smooth its apperance. Default value is true. --> + shrunk or stretched to smooth its appearance. Default value is true. --> <attr name="filter" format="boolean" /> <!-- Enables or disables dithering of the bitmap if the bitmap does not have the same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index b7d088bd3d82..95f14931c6ef 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -634,7 +634,7 @@ able to return to it. --> <attr name="noHistory" format="boolean" /> - <!-- Specify whether an acitivty's task state should always be maintained + <!-- Specify whether an activity's task state should always be maintained by the system, or if it is allowed to reset the task to its initial state in certain situations. @@ -731,15 +731,17 @@ This is equivalent to calling {@link android.app.Activity#setVrModeEnabled} with the the given component name within the Activity that this attribute is set for. Declaring this will prevent the system from leaving VR mode during an Activity - transtion from one VR activity to another. --> + transition from one VR activity to another. --> <attr name="enableVrMode" format="string" /> - <!-- Flag allowing the activity to specify which screen rotation animation - it desires. Valid values are "rotate", "crossfade", and "jumpcut" - as described in {@link android.view.WindowManager.LayoutParams#rotationAnimation}. - Specifying your Rotation animation in the WindowManager.LayoutParams - may be racy with app startup and updattransitions occuring during application startup and thusly - the manifest attribute is preferred. + <!-- Flag that specifies the activity's preferred screen rotation animation. + Valid values are "rotate", "crossfade", "jumpcut", and "seamless" as + described in + {@link android.view.WindowManager.LayoutParams#rotationAnimation}. + Specifying your rotation animation in + <code>WindowManager.LayoutParams</code> may be racy with app startup + and update transitions that occur during application startup; and so, + specify the animation in the manifest attribute. --> <attr name="rotationAnimation"> <flag name="rotate" value= "0" /> @@ -830,7 +832,7 @@ <enum name="singleInstance" value="3" /> <!-- The activity can only be running as the root activity of the task, the first activity that created the task, and therefore there will only be one instance of this activity - in a task. In constrast to the {@code singleTask} launch mode, this activity can be + in a task. In contrast to the {@code singleTask} launch mode, this activity can be started in multiple instances in different tasks if the {@code FLAG_ACTIVITY_MULTIPLE_TASK} or {@code FLAG_ACTIVITY_NEW_DOCUMENT} is set.--> <enum name="singleInstancePerTask" value="4" /> @@ -1328,7 +1330,7 @@ <p>Such a document is any kind of item for which an application may want to maintain multiple simultaneous instances. Examples might be text files, web pages, spreadsheets, or emails. Each such document will be in a separate - task in the recent taskss list. + task in the recent tasks list. <p>This attribute is equivalent to adding the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch @@ -1771,7 +1773,7 @@ </attr> <!-- Enable hardware memory tagging (ARM MTE) in this process. - When enabled, heap memory bugs like use-after-free and buffer overlow + When enabled, heap memory bugs like use-after-free and buffer overflow are detected and result in an immediate ("sync" mode) or delayed ("async" mode) crash instead of a silent memory corruption. Sync mode, while slower, provides enhanced bug reports including stack traces at the time of allocation diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 72333fb5a878..e0b656565ce1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -45,9 +45,7 @@ <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item> <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> <item><xliff:g id="id">@string/status_bar_connected_display</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item> <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item> <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item> @@ -56,6 +54,8 @@ <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item> <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item> <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item> <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item> <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> @@ -542,6 +542,9 @@ <bool name="config_goToSleepOnButtonPressTheaterMode">true</bool> <!-- If this is true, long press on power button will be available from the non-interactive state --> <bool name="config_supportLongPressPowerWhenNonInteractive">false</bool> + <!-- If this is true, short press on power button will be available whenever the default display + is on even if the device is non-interactive (dreaming). --> + <bool name="config_supportShortPressPowerWhenDefaultDisplayOn">false</bool> <!-- If this is true, then keep dreaming when unplugging. This config was formerly known as config_keepDreamingWhenUndocking. @@ -3036,14 +3039,15 @@ on the headphone/microphone jack. When false use the older uevent framework. --> <bool name="config_useDevInputEventForAudioJack">false</bool> - <!-- Whether safe headphone volume is enabled or not (country specific). --> + <!-- Whether safe headphone hearing is enforced by any regulation (e.g. + EN50332-3, EN50332-2) or not (country specific). --> <bool name="config_safe_media_volume_enabled">true</bool> - <!-- Whether safe headphone sound dosage warning is enabled or not - (country specific). This value should only be overlaid to true - when a vendor supports offload and has the HAL sound dose - interfaces implemented. Otherwise, this can lead to a compliance - issue with the safe hearing standards EN50332-3 and IEC62368-1. + <!-- Whether safe headphone sound dosage warning is enabled or not. + This value should only be overlaid to true when a vendor supports + offload and has the HAL sound dose interfaces implemented. + Otherwise, this can lead to a compliance issue with the safe + hearing standards EN50332-3 and IEC62368-1. --> <bool name="config_safe_sound_dosage_enabled">false</bool> @@ -5040,6 +5044,21 @@ </array> <!-- See DisplayWhiteBalanceController. + A float array containing a list of ambient brightnesses, in Lux. This array, + together with config_displayWhiteBalanceLowLightAmbientBiasesStrong, is used to generate a + lookup table used in DisplayWhiteBalanceController. This lookup table is used to map + ambient brightness readings to a bias, where the bias is used to linearly interpolate + between ambient color temperature and + config_displayWhiteBalanceLowLightAmbientColorTemperatureIdle. + This table is optional. If used, this array must, + 1) Contain at least two entries + 2) Be the same length as config_displayWhiteBalanceLowLightAmbientBiasesStrong. --> + <array name ="config_displayWhiteBalanceLowLightAmbientBrightnessesStrong"> + <item>10.0</item> + <item>10.0</item> + </array> + + <!-- See DisplayWhiteBalanceController. An array containing a list of biases. See config_displayWhiteBalanceLowLightAmbientBrightnesses for additional details. This array must be in the range of [0.0, 1.0]. --> @@ -5049,12 +5068,28 @@ </array> <!-- See DisplayWhiteBalanceController. + An array containing a list of biases. See + config_displayWhiteBalanceLowLightAmbientBrightnessesStrong for additional details. + This array must be in the range of [0.0, 1.0]. --> + <array name ="config_displayWhiteBalanceLowLightAmbientBiasesStrong"> + <item>0.0</item> + <item>1.0</item> + </array> + + <!-- See DisplayWhiteBalanceController. The ambient color temperature (in cct) to which we interpolate towards using the the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnesses and config_displayWhiteBalanceLowLightAmbientBiases. --> <item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item> <!-- See DisplayWhiteBalanceController. + The ambient color temperature (in cct) to which we interpolate towards using the + the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnessesStrong + and config_displayWhiteBalanceLowLightAmbientBiasesStrong. Used when device is in Idle Screen + Brightness mode. --> + <item name="config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong" format="float" type="dimen">6500.0</item> + + <!-- See DisplayWhiteBalanceController. A float array containing a list of ambient brightnesses, in Lux. This array, together with config_displayWhiteBalanceHighLightAmbientBiases, is used to generate a lookup table used in DisplayWhiteBalanceController. This lookup table is used to map @@ -5068,6 +5103,19 @@ </array> <!-- See DisplayWhiteBalanceController. + A float array containing a list of ambient brightnesses, in Lux. This array, + together with config_displayWhiteBalanceHighLightAmbientBiasesStrong, is used to generate a + lookup table used in DisplayWhiteBalanceController. This lookup table is used to map + ambient brightness readings to a bias, where the bias is used to linearly interpolate + between ambient color temperature and + config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong. + This table is optional. If used, this array must, + 1) Contain at least two entries + 2) Be the same length as config_displayWhiteBalanceHighLightAmbientBiasesStrong. --> + <array name ="config_displayWhiteBalanceHighLightAmbientBrightnessesStrong"> + </array> + + <!-- See DisplayWhiteBalanceController. An array containing a list of biases. See config_displayWhiteBalanceHighLightAmbientBrightnesses for additional details. This array must be in the range of [0.0, 1.0]. --> @@ -5075,12 +5123,26 @@ </array> <!-- See DisplayWhiteBalanceController. + An array containing a list of biases. See + config_displayWhiteBalanceHighLightAmbientBrightnessesStrong for additional details. + This array must be in the range of [0.0, 1.0]. --> + <array name ="config_displayWhiteBalanceHighLightAmbientBiasesStrong"> + </array> + + <!-- See DisplayWhiteBalanceController. The ambient color temperature (in cct) to which we interpolate towards using the the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnesses and config_displayWhiteBalanceHighLightAmbientBiases. --> <item name="config_displayWhiteBalanceHighLightAmbientColorTemperature" format="float" type="dimen">8000.0</item> <!-- See DisplayWhiteBalanceController. + The ambient color temperature (in cct) to which we interpolate towards using the + the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnessesStrong + and config_displayWhiteBalanceHighLightAmbientBiasesStrong. Used when device is in Idle + Screen Brightness mode. --> + <item name="config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong" format="float" type="dimen">8000.0</item> + + <!-- See DisplayWhiteBalanceController. A float array containing a list of ambient color temperatures, in Kelvin. This array, together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a lookup table used in DisplayWhiteBalanceController. This lookup table is used to map @@ -6113,6 +6175,9 @@ <!-- Default value for Settings.ASSIST_TOUCH_GESTURE_ENABLED --> <bool name="config_assistTouchGestureEnabledDefault">true</bool> + <!-- Default value for Settings.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED --> + <bool name="config_searchPressHoldNavHandleEnabledDefault">true</bool> + <!-- The maximum byte size of the information contained in the bundle of HotwordDetectedResult. --> <integer translatable="false" name="config_hotwordDetectedResultMaxBundleSize">0</integer> diff --git a/core/res/res/values/config_battery_saver.xml b/core/res/res/values/config_battery_saver.xml index eb396df1ac79..e1b0ef4bd0a7 100644 --- a/core/res/res/values/config_battery_saver.xml +++ b/core/res/res/values/config_battery_saver.xml @@ -26,6 +26,10 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string> + <!-- Whether or not the device supports battery saver. If false, battery saver will be + disabled. --> + <bool name="config_batterySaverSupported">true</bool> + <!-- Whether or not battery saver should be "sticky" when manually enabled. --> <bool name="config_batterySaverStickyBehaviourDisabled">false</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index c5aa8b05b948..0dd6c749b5a9 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1895,22 +1895,22 @@ <!-- Content description which should be used for the fingerprint icon. --> <string name="fingerprint_icon_content_description">Fingerprint icon</string> + <!-- Notification name shown when the system requires the user to set up device unlock. [CHAR LIMIT=NONE] --> + <string name="device_unlock_notification_name">Device unlock</string> + <!-- Notification title shown when the system suggests the user to set up another way to unlock. [CHAR LIMIT=NONE] --> + <string name="alternative_unlock_setup_notification_title">Try another way to unlock</string> + <!-- Notification content shown when the system suggests the user to enroll their face. [CHAR LIMIT=NONE] --> + <string name="alternative_face_setup_notification_content">Use Face Unlock when your fingerprint isn\'t recognized, like when your fingers are wet</string> + <!-- Notification content shown when the system suggests the user to enroll their fingerprint. [CHAR LIMIT=NONE] --> + <string name="alternative_fp_setup_notification_content">Use Fingerprint Unlock when your face isn\'t recognized, like when there\'s not enough light</string> <!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] --> <string name="face_recalibrate_notification_name">Face Unlock</string> <!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] --> <string name="face_recalibrate_notification_title">Issue with Face Unlock</string> <!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] --> <string name="face_recalibrate_notification_content">Tap to delete your face model, then add your face again</string> - <!-- Title of a notification that directs the user to set up Face Unlock by enrolling their face. [CHAR LIMIT=NONE] --> - <string name="face_setup_notification_title">Set up Face Unlock</string> - <!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] --> - <string name="face_setup_notification_content">Unlock your phone by looking at it</string> <!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] --> <string name="face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string> - <!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] --> - <string name="fingerprint_setup_notification_title">Set up more ways to unlock</string> - <!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] --> - <string name="fingerprint_setup_notification_content">Tap to add a fingerprint</string> <!-- Notification name shown when the system requires the user to re-calibrate their fingerprint. [CHAR LIMIT=NONE] --> <string name="fingerprint_recalibrate_notification_name">Fingerprint Unlock</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0a0dc368d4a0..4918bbefa900 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1968,6 +1968,7 @@ <java-symbol type="integer" name="config_keyguardDrawnTimeout" /> <java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" /> <java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" /> + <java-symbol type="bool" name="config_supportShortPressPowerWhenDefaultDisplayOn" /> <java-symbol type="bool" name="config_wimaxEnabled" /> <java-symbol type="bool" name="show_ongoing_ime_switcher" /> <java-symbol type="color" name="config_defaultNotificationColor" /> @@ -2597,6 +2598,12 @@ <!-- Biometric FRR config --> <java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" /> + <!-- Biometric FRR notification messages --> + <java-symbol type="string" name="device_unlock_notification_name" /> + <java-symbol type="string" name="alternative_unlock_setup_notification_title" /> + <java-symbol type="string" name="alternative_face_setup_notification_content" /> + <java-symbol type="string" name="alternative_fp_setup_notification_content" /> + <!-- Device credential strings for BiometricManager --> <java-symbol type="string" name="screen_lock_app_setting_name" /> <java-symbol type="string" name="screen_lock_dialog_default_subtitle" /> @@ -4019,6 +4026,7 @@ <!-- Battery saver config --> <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" /> + <java-symbol type="bool" name="config_batterySaverSupported" /> <java-symbol type="string" name="config_batterySaverDeviceSpecificConfig" /> <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" /> <java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" /> @@ -4166,11 +4174,17 @@ <java-symbol type="array" name="config_displayWhiteBalanceIncreaseThresholds" /> <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" /> <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnesses" /> + <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnessesStrong" /> <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiases" /> + <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiasesStrong" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" /> + <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong" /> <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnesses" /> + <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnessesStrong" /> <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiases" /> + <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiasesStrong" /> <java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperature" /> + <java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong" /> <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" /> <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" /> <java-symbol type="array" name="config_displayWhiteBalanceStrongAmbientColorTemperatures" /> @@ -4894,6 +4908,8 @@ <java-symbol type="bool" name="config_assistLongPressHomeEnabledDefault" /> <java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" /> + <java-symbol type="bool" name="config_searchPressHoldNavHandleEnabledDefault" /> + <java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" /> <java-symbol type="dimen" name="config_wallpaperDimAmount" /> @@ -5184,6 +5200,7 @@ <java-symbol type="style" name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" /> <java-symbol type="drawable" name="focus_event_pressed_key_background" /> + <java-symbol type="drawable" name="focus_event_rotary_input_background" /> <java-symbol type="string" name="config_defaultShutdownVibrationFile" /> <java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" /> @@ -5193,4 +5210,6 @@ <!-- For ActivityManager PSS profiling configurability --> <java-symbol type="bool" name="config_am_disablePssProfiling" /> + + <java-symbol type="raw" name="default_ringtone_vibration_effect" /> </resources> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index c14da299c6ef..7f56eb70b153 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -88,7 +88,7 @@ android_test { resource_dirs: ["res"], resource_zips: [":FrameworksCoreTests_apks_as_resources"], - java_resources: [":ApkVerityTestCertDer"], + java_resources: [":FrameworksCoreTests_unit_test_cert_der"], data: [ ":BinderDeathRecipientHelperApp1", diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 31755efb88ed..a358c4f6f7e9 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1749,6 +1749,15 @@ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> </intent-filter> </activity> + + <activity android:name="android.view.ViewGroupTestActivity" + android:label="ViewGroup Test" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp index 8d4ecf4253c3..cefdc4dd60b1 100644 --- a/core/tests/coretests/certs/Android.bp +++ b/core/tests/coretests/certs/Android.bp @@ -13,3 +13,8 @@ android_app_certificate { name: "FrameworksCoreTests_unit_test_cert", certificate: "unit_test", } + +filegroup { + name: "FrameworksCoreTests_unit_test_cert_der", + srcs: ["unit_test.der"], +} diff --git a/core/tests/coretests/certs/README b/core/tests/coretests/certs/README index 00917a188934..b5c096e61a2e 100644 --- a/core/tests/coretests/certs/README +++ b/core/tests/coretests/certs/README @@ -2,3 +2,5 @@ Generate with: development/tools/make_key unit_test '/CN=unit_test' development/tools/make_key unit_test_diff '/CN=unit_test_diff' + +openssl x509 -in unit_test.x509.pem -out unit_test.der -outform der diff --git a/core/tests/coretests/certs/unit_test.der b/core/tests/coretests/certs/unit_test.der Binary files differnew file mode 100644 index 000000000000..4dbbc4946bda --- /dev/null +++ b/core/tests/coretests/certs/unit_test.der diff --git a/core/tests/coretests/res/layout/viewgroup_test.xml b/core/tests/coretests/res/layout/viewgroup_test.xml new file mode 100644 index 000000000000..04f4f5228b06 --- /dev/null +++ b/core/tests/coretests/res/layout/viewgroup_test.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- Demonstrates adding/removing views from ViewGroup. See corresponding Java code. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/linear_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <EditText + android:id="@+id/view" + android:layout_width="20dp" + android:layout_height="10dp" + android:text="Hello World!" + android:background="#2F00FF00" /> + <EditText + android:id="@+id/view_scale" + android:layout_width="20dp" + android:layout_height="10dp" + android:scaleX="0.5" + android:scaleY="2" + android:transformPivotX="0dp" + android:transformPivotY="0dp" + android:text="Hello World!" + android:background="#2F00FF00" /> + <EditText + android:id="@+id/view_translate" + android:layout_width="20dp" + android:layout_height="10dp" + android:translationX="10dp" + android:translationY="20dp" + android:text="Hello World!" + android:background="#2F00FF00" /> + <FrameLayout + android:layout_width="20dp" + android:layout_height="10dp"> + <EditText + android:id="@+id/view_overlap_bottom" + android:layout_width="20dp" + android:layout_height="10dp" + android:text="Hello World!"/> + <Button + android:id="@+id/view_overlap_top" + android:layout_width="10dp" + android:layout_height="10dp"/> + </FrameLayout> + + <FrameLayout + android:layout_width="20dp" + android:layout_height="10dp"> + <EditText + android:id="@+id/view_cover_bottom" + android:layout_width="10dp" + android:layout_height="10dp" + android:text="Hello World!"/> + <Button + android:id="@+id/view_cover_top" + android:layout_width="10dp" + android:layout_height="10dp"/> + </FrameLayout> + +</LinearLayout> diff --git a/core/tests/coretests/res/raw/fsverity_sig b/core/tests/coretests/res/raw/fsverity_sig Binary files differindex b2f335dc0342..2c28f0be3cba 100644 --- a/core/tests/coretests/res/raw/fsverity_sig +++ b/core/tests/coretests/res/raw/fsverity_sig diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index a936ceafe5f3..f9377fc480bd 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -19,6 +19,8 @@ package android.app; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_READ; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_REPLY; import static android.app.Notification.CarExtender.UnreadConversation.KEY_REMOTE_INPUT; +import static android.app.Notification.DEFAULT_SOUND; +import static android.app.Notification.DEFAULT_VIBRATE; import static android.app.Notification.EXTRA_ANSWER_INTENT; import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO; import static android.app.Notification.EXTRA_CALL_PERSON; @@ -35,6 +37,9 @@ import static android.app.Notification.EXTRA_PICTURE; import static android.app.Notification.EXTRA_PICTURE_ICON; import static android.app.Notification.EXTRA_SUMMARY_TEXT; import static android.app.Notification.EXTRA_TITLE; +import static android.app.Notification.GROUP_ALERT_CHILDREN; +import static android.app.Notification.GROUP_ALERT_SUMMARY; +import static android.app.Notification.GROUP_KEY_SILENT; import static android.app.Notification.MessagingStyle.Message.KEY_DATA_URI; import static android.app.Notification.MessagingStyle.Message.KEY_SENDER_PERSON; import static android.app.Notification.MessagingStyle.Message.KEY_TEXT; @@ -511,6 +516,75 @@ public class NotificationTest { } @Test + public void testBuilder_setSilent_summaryBehavior_groupAlertChildren() { + Notification summaryNotif = new Notification.Builder(mContext, "channelId") + .setGroupSummary(true) + .setGroup("groupKey") + .setSilent(true) + .build(); + assertEquals(GROUP_ALERT_CHILDREN, summaryNotif.getGroupAlertBehavior()); + } + + @Test + public void testBuilder_setSilent_childBehavior_groupAlertSummary() { + Notification childNotif = new Notification.Builder(mContext, "channelId") + .setGroupSummary(false) + .setGroup("groupKey") + .setSilent(true) + .build(); + assertEquals(GROUP_ALERT_SUMMARY, childNotif.getGroupAlertBehavior()); + } + + @Test + public void testBuilder_setSilent_emptyGroupKey_groupKeySilent() { + Notification emptyGroupKeyNotif = new Notification.Builder(mContext, "channelId") + .setGroup("") + .setSilent(true) + .build(); + assertEquals(GROUP_KEY_SILENT, emptyGroupKeyNotif.getGroup()); + } + + @Test + public void testBuilder_setSilent_vibrateNull() { + Notification silentNotif = new Notification.Builder(mContext, "channelId") + .setGroup("") + .setSilent(true) + .build(); + + assertNull(silentNotif.vibrate); + } + + @Test + public void testBuilder_setSilent_soundNull() { + Notification silentNotif = new Notification.Builder(mContext, "channelId") + .setGroup("") + .setSilent(true) + .build(); + + assertNull(silentNotif.sound); + } + + @Test + public void testBuilder_setSilent_noDefaultSound() { + Notification silentNotif = new Notification.Builder(mContext, "channelId") + .setGroup("") + .setSilent(true) + .build(); + + assertEquals(0, (silentNotif.defaults & DEFAULT_SOUND)); + } + + @Test + public void testBuilder_setSilent_noDefaultVibrate() { + Notification silentNotif = new Notification.Builder(mContext, "channelId") + .setGroup("") + .setSilent(true) + .build(); + + assertEquals(0, (silentNotif.defaults & DEFAULT_VIBRATE)); + } + + @Test public void testCallStyle_getSystemActions_forIncomingCall() { PendingIntent answerIntent = createPendingIntent("answer"); PendingIntent declineIntent = createPendingIntent("decline"); diff --git a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java index 2232e3a6ffa3..e1f9523f1b49 100644 --- a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java +++ b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java @@ -20,6 +20,7 @@ import static androidx.test.InstrumentationRegistry.getContext; import static com.google.common.truth.Truth.assertThat; +import android.os.ConditionVariable; import android.os.PowerMonitor; import android.os.PowerMonitorReadings; @@ -29,13 +30,16 @@ import java.util.ArrayList; import java.util.List; public class SystemHealthManagerTest { + private List<PowerMonitor> mPowerMonitorInfo; + private PowerMonitorReadings mReadings; + private RuntimeException mException; @Test public void getPowerMonitors() { SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class); - PowerMonitor[] powerMonitorInfo = shm.getSupportedPowerMonitors(); + List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors(); assertThat(powerMonitorInfo).isNotNull(); - if (powerMonitorInfo.length == 0) { + if (powerMonitorInfo.isEmpty()) { // This device does not support PowerStats HAL return; } @@ -50,20 +54,73 @@ public class SystemHealthManagerTest { } } - List<PowerMonitor> pmis = new ArrayList<>(); + List<PowerMonitor> selectedMonitors = new ArrayList<>(); if (consumerMonitor != null) { - pmis.add(consumerMonitor); + selectedMonitors.add(consumerMonitor); } if (measurementMonitor != null) { - pmis.add(measurementMonitor); + selectedMonitors.add(measurementMonitor); } - PowerMonitor[] selectedMonitors = pmis.toArray(new PowerMonitor[0]); PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors); for (PowerMonitor monitor : selectedMonitors) { assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0); - assertThat(readings.getTimestampMs(monitor)).isGreaterThan(0); + assertThat(readings.getTimestamp(monitor)).isGreaterThan(0); + } + } + + @Test + public void getPowerMonitorsAsync() { + SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class); + ConditionVariable done = new ConditionVariable(); + shm.getSupportedPowerMonitors(null, pms -> { + mPowerMonitorInfo = pms; + done.open(); + }); + done.block(); + assertThat(mPowerMonitorInfo).isNotNull(); + if (mPowerMonitorInfo.isEmpty()) { + // This device does not support PowerStats HAL + return; + } + + PowerMonitor consumerMonitor = null; + PowerMonitor measurementMonitor = null; + for (PowerMonitor pmi : mPowerMonitorInfo) { + if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) { + measurementMonitor = pmi; + } else { + consumerMonitor = pmi; + } + } + + List<PowerMonitor> selectedMonitors = new ArrayList<>(); + if (consumerMonitor != null) { + selectedMonitors.add(consumerMonitor); + } + if (measurementMonitor != null) { + selectedMonitors.add(measurementMonitor); + } + + done.close(); + shm.getPowerMonitorReadings(selectedMonitors, null, + readings -> { + mReadings = readings; + done.open(); + }, + exception -> { + mException = exception; + done.open(); + } + ); + done.block(); + + assertThat(mException).isNull(); + + for (PowerMonitor monitor : selectedMonitors) { + assertThat(mReadings.getConsumedEnergyUws(monitor)).isAtLeast(0); + assertThat(mReadings.getTimestamp(monitor)).isGreaterThan(0); } } } diff --git a/core/tests/coretests/src/android/security/keystore/OWNERS b/core/tests/coretests/src/android/security/keystore/OWNERS new file mode 100644 index 000000000000..d9e01161ce6d --- /dev/null +++ b/core/tests/coretests/src/android/security/keystore/OWNERS @@ -0,0 +1 @@ +include /keystore/OWNERS diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java index 5fe17eeaac2d..4d4469011c06 100644 --- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java +++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -36,6 +37,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.os.Binder; import android.os.Bundle; +import android.os.DeadObjectException; import android.os.IBinder; import android.os.RemoteException; import android.service.controls.actions.CommandAction; @@ -53,6 +55,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -307,6 +310,18 @@ public class ControlProviderServiceTest { intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL))); } + @Test + public void testOnNextDoesntRethrowDeadObjectException() throws RemoteException { + doAnswer(invocation -> { + throw new DeadObjectException(); + }).when(mSubscriber).onNext(ArgumentMatchers.any(), ArgumentMatchers.any()); + Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build(); + + sendControlGetControl(control); + + assertTrue(mControlsProviderService.mSubscription.mIsCancelled); + } + /** * Sends the control through the publisher in {@code mControlsProviderService}, returning * the control obtained by the subscriber @@ -359,6 +374,7 @@ public class ControlProviderServiceTest { } private List<Control> mControls; + private FakeSubscription mSubscription; public void setControls(List<Control> controls) { mControls = controls; @@ -398,17 +414,35 @@ public class ControlProviderServiceTest { } private Subscription createSubscription(Subscriber s, List<Control> controls) { - return new Subscription() { - public void request(long n) { - int i = 0; - for (Control c : mControls) { - if (i++ < n) s.onNext(c); - else break; - } - s.onComplete(); - } - public void cancel() {} - }; + FakeSubscription subscription = new FakeSubscription(s, controls); + mSubscription = subscription; + return subscription; + } + } + + private static final class FakeSubscription implements Subscription { + + private final Subscriber mSubscriber; + private final List<Control> mControls; + + private boolean mIsCancelled = false; + + FakeSubscription(Subscriber s, List<Control> controls) { + mSubscriber = s; + mControls = controls; + } + + public void request(long n) { + int i = 0; + for (Control c : mControls) { + if (i++ < n) mSubscriber.onNext(c); + else break; + } + mSubscriber.onComplete(); + } + + public void cancel() { + mIsCancelled = true; } } } diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java index a84ac55f0d5a..55ded9c8813d 100644 --- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java +++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java @@ -136,7 +136,11 @@ public class NotificationRankingUpdateTest { NotificationListenerService.RankingMap retrievedRankings = retrievedRankingUpdate.getRankingMap(); assertNotNull(retrievedRankings); - assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed()); + // The rankingUpdate file descriptor is only non-null in the new path. + if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( + SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { + assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed()); + } NotificationListenerService.Ranking retrievedRanking = new NotificationListenerService.Ranking(); assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); diff --git a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java new file mode 100644 index 000000000000..60a0a2adbbbe --- /dev/null +++ b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java @@ -0,0 +1,216 @@ +/* + * 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 android.view; + +import static com.google.common.truth.Truth.assertThat; + + +import android.graphics.Matrix; +import android.graphics.Region; +import android.platform.test.annotations.Presubmit; +import android.widget.LinearLayout; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.filters.SmallTest; + +import com.android.frameworks.coretests.R; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +/** + * Test basic functions of ViewGroup. + * + * Build/Install/Run: + * atest FrameworksCoreTests:ViewGroupTest + */ +@Presubmit +@SmallTest +public class ViewGroupGetChildLocalHitRegionTest { + @Rule + public ActivityScenarioRule<ViewGroupTestActivity> mScenarioRule = + new ActivityScenarioRule<>(ViewGroupTestActivity.class); + + private LinearLayout mRoot; + private final int[] mRootLocation = new int[2]; + + @Before + public void setup() { + mScenarioRule.getScenario().onActivity(activity -> { + mRoot = activity.findViewById(R.id.linear_layout); + mRoot.getLocationInWindow(mRootLocation); + }); + } + + @Test + public void testGetChildLocalHitRegion() { + assertGetChildLocalHitRegion(R.id.view); + } + + @Test + public void testGetChildLocalHitRegion_withScale() { + assertGetChildLocalHitRegion(R.id.view_scale); + } + + @Test + public void testGetChildLocalHitRegion_withTranslate() { + assertGetChildLocalHitRegion(R.id.view_translate); + } + + @Test + public void testGetChildLocalHitRegion_overlap_noMotionEvent() { + assertGetChildLocalHitRegion(R.id.view_overlap_bottom); + } + @Test + public void testGetChildLocalHitRegion_overlap_withMotionEvent() { + // In this case, view_cover_bottom is partially covered by the view_cover_top. + // The returned region is the bounds of the bottom view subtract the bounds of the top view. + assertGetChildLocalHitRegion(R.id.view_overlap_top, R.id.view_overlap_bottom); + } + + @Test + public void testGetChildLocalHitRegion_cover_withMotionEvent() { + // In this case, view_cover_bottom is completely covered by the view_cover_top. + // The returned region is expected to be empty. + assertGetChildLocalHitRegionEmpty(R.id.view_cover_top, R.id.view_cover_bottom); + } + + private void injectMotionEvent(View view, boolean isHover) { + int[] location = new int[2]; + view.getLocationInWindow(location); + + float x = location[0] + view.getWidth() / 2f; + float y = location[1] + view.getHeight() / 2f; + + int action = isHover ? MotionEvent.ACTION_HOVER_ENTER : MotionEvent.ACTION_DOWN; + MotionEvent motionEvent = MotionEvent.obtain(/* downtime= */ 0, /* eventTime= */ 0, action, + x, y, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0, + /* xPrecision= */ 1, /* yPrecision= */ 1, /* deviceId= */0, /* edgeFlags= */0); + + View rootView = view.getRootView(); + rootView.dispatchPointerEvent(motionEvent); + } + + private void assertGetChildLocalHitRegion(int viewId) { + assertGetChildLocalHitRegion(viewId, /* isHover= */ true); + assertGetChildLocalHitRegion(viewId, /* isHover= */ false); + } + + /** + * Assert ViewParent#getChildLocalHitRegion for a single view. + * @param viewId the viewId of the tested view. + * @param isHover if true, check the hit region of the hover events. Otherwise, check the hit + * region of the touch events. + */ + private void assertGetChildLocalHitRegion(int viewId, boolean isHover) { + mScenarioRule.getScenario().onActivity(activity -> { + View view = activity.findViewById(viewId); + + Matrix actualMatrix = new Matrix(); + Region actualRegion = new Region(0, 0, view.getWidth(), view.getHeight()); + boolean actualNotEmpty = view.getParent() + .getChildLocalHitRegion(view, actualRegion, actualMatrix, isHover); + + int[] windowLocation = new int[2]; + view.getLocationInWindow(windowLocation); + Matrix expectMatrix = new Matrix(); + expectMatrix.preScale(1 / view.getScaleX(), 1 / view.getScaleY()); + expectMatrix.preTranslate(-windowLocation[0], -windowLocation[1]); + + Region expectRegion = new Region(0, 0, view.getWidth(), view.getHeight()); + + assertThat(actualNotEmpty).isTrue(); + assertThat(actualMatrix).isEqualTo(expectMatrix); + assertThat(actualRegion).isEqualTo(expectRegion); + }); + } + + private void assertGetChildLocalHitRegion(int viewIdTop, int viewIdBottom) { + assertGetChildLocalHitRegion(viewIdTop, viewIdBottom, /* isHover= */ true); + assertGetChildLocalHitRegion(viewIdTop, viewIdBottom, /* isHover= */ false); + } + + /** + * Assert ViewParent#getChildLocalHitRegion of a view that is covered by another view. It will + * inject {@link MotionEvent}s to the view on top first and then get the hit region of the + * bottom view. + * + * @param viewIdTop the view id of the test view on top. + * @param viewIdBottom the view id of the test view at the bottom. + * @param isHover if true, check the hit region of the hover events. Otherwise, check the hit + * region of the touch events. + */ + private void assertGetChildLocalHitRegion(int viewIdTop, int viewIdBottom, boolean isHover) { + mScenarioRule.getScenario().onActivity(activity -> { + View viewTop = activity.findViewById(viewIdTop); + View viewBottom = activity.findViewById(viewIdBottom); + + injectMotionEvent(viewTop, isHover); + + Matrix actualMatrix = new Matrix(); + Region actualRegion = new Region(0, 0, viewBottom.getWidth(), viewBottom.getHeight()); + boolean actualNotEmpty = viewBottom.getParent() + .getChildLocalHitRegion(viewBottom, actualRegion, actualMatrix, isHover); + + int[] windowLocation = new int[2]; + viewBottom.getLocationInWindow(windowLocation); + Matrix expectMatrix = new Matrix(); + expectMatrix.preTranslate(-windowLocation[0], -windowLocation[1]); + + Region expectRegion = new Region(0, 0, viewBottom.getWidth(), viewBottom.getHeight()); + expectRegion.op(0, 0, viewTop.getWidth(), viewTop.getHeight(), Region.Op.DIFFERENCE); + + assertThat(actualNotEmpty).isTrue(); + assertThat(actualMatrix).isEqualTo(expectMatrix); + assertThat(actualRegion).isEqualTo(expectRegion); + }); + } + + private void assertGetChildLocalHitRegionEmpty(int viewIdTop, int viewIdBottom) { + assertGetChildLocalHitRegionEmpty(viewIdTop, viewIdBottom, /* isHover= */ true); + assertGetChildLocalHitRegionEmpty(viewIdTop, viewIdBottom, /* isHover= */ false); + } + + /** + * Assert ViewParent#getChildLocalHitRegion returns an empty region for a view that is + * completely covered by another view. It will inject {@link MotionEvent}s to the view on top + * first and then get the hit region of the + * bottom view. + * + * @param viewIdTop the view id of the test view on top. + * @param viewIdBottom the view id of the test view at the bottom. + * @param isHover if true, check the hit region of the hover events. Otherwise, check the hit + * region of the touch events. + */ + private void assertGetChildLocalHitRegionEmpty(int viewIdTop, int viewIdBottom, + boolean isHover) { + mScenarioRule.getScenario().onActivity(activity -> { + View viewTop = activity.findViewById(viewIdTop); + View viewBottom = activity.findViewById(viewIdBottom); + + injectMotionEvent(viewTop, isHover); + + Region actualRegion = new Region(0, 0, viewBottom.getWidth(), viewBottom.getHeight()); + boolean actualNotEmpty = viewBottom.getParent() + .getChildLocalHitRegion(viewBottom, actualRegion, new Matrix(), isHover); + + assertThat(actualNotEmpty).isFalse(); + assertThat(actualRegion.isEmpty()).isTrue(); + }); + } +} diff --git a/core/tests/coretests/src/android/view/ViewGroupTestActivity.java b/core/tests/coretests/src/android/view/ViewGroupTestActivity.java new file mode 100644 index 000000000000..b94bda5efba4 --- /dev/null +++ b/core/tests/coretests/src/android/view/ViewGroupTestActivity.java @@ -0,0 +1,31 @@ +/* + * 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 android.view; + +import android.annotation.Nullable; +import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; + +public class ViewGroupTestActivity extends Activity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.viewgroup_test); + } +} diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java index 388a996d8b1c..b4c72ca3226b 100644 --- a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java +++ b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java @@ -21,7 +21,9 @@ import static org.mockito.Mockito.when; import android.app.Instrumentation; import android.content.Context; +import android.graphics.Matrix; import android.graphics.Rect; +import android.graphics.Region; import android.view.View; import android.view.ViewGroup; @@ -45,7 +47,7 @@ public class HandwritingTestUtil { float handwritingBoundsOffsetRight, float handwritingBoundsOffsetBottom) { final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); final Context context = instrumentation.getTargetContext(); - // mock a parent so that HandwritingInitiator can get + // mock a parent so that HandwritingInitiator can get visible rect and hit region. final ViewGroup parent = new ViewGroup(context) { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { @@ -56,6 +58,14 @@ public class HandwritingTestUtil { r.set(handwritingArea); return true; } + + @Override + public boolean getChildLocalHitRegion(View child, Region region, Matrix matrix, + boolean isHover) { + matrix.reset(); + region.set(handwritingArea); + return true; + } }; View view = spy(new View(context)); diff --git a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java index 151365481f20..a978e3b9e739 100644 --- a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java @@ -229,11 +229,12 @@ public class VerityUtilsTest { @Test public void testSignatureGeneratedExternally() throws Exception { var context = InstrumentationRegistry.getInstrumentation().getContext(); - byte[] cert = getClass().getClassLoader().getResourceAsStream("ApkVerityTestCert.der") + byte[] cert = getClass().getClassLoader().getResourceAsStream("unit_test.der") .readAllBytes(); // The signature is generated by: - // fsverity sign <(echo -n fs-verity) fsverity_sig --key=ApkVerityTestKey.pem \ - // --cert=ApkVerityTestCert.pem + // openssl pkcs8 -topk8 -nocrypt -in certs/unit_test.pk8 -out certs/unit_test.key.pem + // fsverity sign <(echo -n fs-verity) fsverity_sig --key=certs/unit_test.key.pem \ + // --cert=certs/unit_test.x509.pem byte[] sig = context.getResources().openRawResource(R.raw.fsverity_sig).readAllBytes(); // The fs-verity digest is generated by: // fsverity digest --compact <(echo -n fs-verity) diff --git a/core/tests/packagemonitortests/Android.bp b/core/tests/packagemonitortests/Android.bp index 453b476edbc0..7b5d7dff0a85 100644 --- a/core/tests/packagemonitortests/Android.bp +++ b/core/tests/packagemonitortests/Android.bp @@ -24,6 +24,9 @@ package { android_test { name: "FrameworksCorePackageMonitorTests", srcs: ["src/**/*.java"], + exclude_srcs: [ + "src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java", + ], static_libs: [ "androidx.test.runner", "compatibility-device-util-axt", @@ -39,3 +42,19 @@ android_test { ":TestVisibilityApp", ], } + +android_test { + name: "FrameworksCorePackageMonitorWithoutPermissionTests", + srcs: ["src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java"], + manifest: "AndroidManifestNoPermission.xml", + static_libs: [ + "androidx.test.runner", + "compatibility-device-util-axt", + "frameworks-base-testutils", + ], + libs: ["android.test.runner"], + platform_apis: true, + certificate: "platform", + test_suites: ["device-tests"], + test_config: "AndroidTestWithoutPermission.xml", +} diff --git a/core/tests/packagemonitortests/AndroidManifestNoPermission.xml b/core/tests/packagemonitortests/AndroidManifestNoPermission.xml new file mode 100644 index 000000000000..5d6308accf00 --- /dev/null +++ b/core/tests/packagemonitortests/AndroidManifestNoPermission.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.packagemonitor.withoutpermission"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.packagemonitor.withoutpermission" + android:label="Frameworks PackageMonitor Core without Permission Tests" /> +</manifest> diff --git a/core/tests/packagemonitortests/AndroidTestWithoutPermission.xml b/core/tests/packagemonitortests/AndroidTestWithoutPermission.xml new file mode 100644 index 000000000000..37a6a2f537d7 --- /dev/null +++ b/core/tests/packagemonitortests/AndroidTestWithoutPermission.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Runs Frameworks Core Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="FrameworksCorePackageMonitorWithoutPermissionTests.apk" /> + </target_preparer> + + <option name="test-tag" value="FrameworksCorePackageMonitorWithoutPermissionTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.packagemonitor.withoutpermission" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java index a310edceb3b3..da8e4cc5aecc 100644 --- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java +++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java @@ -20,9 +20,12 @@ import static com.android.compatibility.common.util.ShellUtils.runShellCommand; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + import android.content.Context; import android.content.pm.PackageManager; import android.os.Handler; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.UserHandle; @@ -45,6 +48,25 @@ public class PackageMonitorVisibilityTest { TEST_DATA_PATH + "TestVisibilityApp.apk"; private static final String TEAT_APK_PACKAGE_NAME = "com.example.android.testvisibilityapp"; private static final int WAIT_CALLBACK_CALLED_IN_SECONDS = 1; + + @Test + public void testPackageMonitorCallbackMultipleRegisterThrowsException() throws Exception { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + final IRemoteCallback callback = new IRemoteCallback.Stub() { + @Override + public void sendResult(android.os.Bundle bundle) { + // do-nothing + } + }; + try { + context.getPackageManager().registerPackageMonitorCallback(callback, 0); + assertThrows(IllegalStateException.class, + () -> context.getPackageManager().registerPackageMonitorCallback(callback, 0)); + } finally { + context.getPackageManager().unregisterPackageMonitorCallback(callback); + } + } + @Test public void testPackageMonitorPackageVisible() throws Exception { TestVisibilityPackageMonitor testPackageMonitor = new TestVisibilityPackageMonitor(); diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java new file mode 100644 index 000000000000..659c0c293043 --- /dev/null +++ b/core/tests/packagemonitortests/src/com/android/internal/content/withoutpermission/PackageMonitorPermissionTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content.withoutpermission; + +import static org.junit.Assert.assertThrows; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.content.PackageMonitor; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * A test to verify PackageMonitor implementation without INTERACT_ACROSS_USERS_FULL permission. + */ +@RunWith(AndroidJUnit4.class) +public class PackageMonitorPermissionTest { + + @Test + public void testPackageMonitorNoCrossUserPermission() throws Exception { + TestVisibilityPackageMonitor testPackageMonitor = new TestVisibilityPackageMonitor(); + + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + assertThrows(SecurityException.class, + () -> testPackageMonitor.register(context, UserHandle.ALL, + new Handler(Looper.getMainLooper()))); + } + + private static class TestVisibilityPackageMonitor extends PackageMonitor { + } +} diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java index 8be489ecd140..e8758754f059 100644 --- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java +++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java @@ -41,8 +41,6 @@ import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinit import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; -import androidx.test.InstrumentationRegistry; - import com.android.internal.R; import org.jetbrains.annotations.NotNull; @@ -838,31 +836,29 @@ public class VibrationEffectTest { @Test public void testAreVibrationFeaturesSupported_allSegmentsSupported() { - Vibrator vibrator = - createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) - .build()); + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build(); assertTrue(VibrationEffect.createWaveform( /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(VibrationEffect.createWaveform( /* timings= */ new long[] {1, 2, 3}, /* amplitudes= */ new int[] {10, 20, 40}, /* repeatIndex= */ 2) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue( VibrationEffect.startComposition() .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) .repeatEffectIndefinitely(TEST_ONE_SHOT) .compose() - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testAreVibrationFeaturesSupported_withUnsupportedSegments() { - Vibrator vibrator = - createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1).build()); + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build(); assertFalse( VibrationEffect.startComposition() @@ -872,7 +868,7 @@ public class VibrationEffectTest { /* amplitudes= */ new int[] {10, 20, 40}, /* repeatIndex= */ -1)) .compose() - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test @@ -996,13 +992,4 @@ public class VibrationEffectTest { return context; } - - private Vibrator createVibratorWithCustomInfo(VibratorInfo info) { - return new SystemVibrator(InstrumentationRegistry.getContext()) { - @Override - public VibratorInfo getInfo() { - return info; - } - }; - } } diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java index ff917aacba38..73cd4647415d 100644 --- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java +++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java @@ -57,6 +57,17 @@ public class VibratorInfoTest { } @Test + public void testHasFrequencyControl() { + VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); + assertFalse(noCapabilities.hasFrequencyControl()); + VibratorInfo composeAndFrequencyControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID) + .setCapabilities( + IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS) + .build(); + assertTrue(composeAndFrequencyControl.hasFrequencyControl()); + } + + @Test public void testHasCapabilities() { VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) @@ -246,8 +257,13 @@ public class VibratorInfoTest { @Test public void testEquals() { - VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID); + // Create a builder with a different ID, but same properties the same as the first one. + VibratorInfo.Builder completeBuilder2 = new VibratorInfo.Builder(TEST_VIBRATOR_ID + 2); + + for (VibratorInfo.Builder builder : + new VibratorInfo.Builder[] {completeBuilder, completeBuilder2}) { + builder.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20) .setPrimitiveDelayMax(100) @@ -257,31 +273,43 @@ public class VibratorInfoTest { .setPwleSizeMax(20) .setQFactor(2f) .setFrequencyProfile(TEST_FREQUENCY_PROFILE); + } VibratorInfo complete = completeBuilder.build(); assertEquals(complete, complete); + assertTrue(complete.equalContent(complete)); assertEquals(complete, completeBuilder.build()); + assertTrue(complete.equalContent(completeBuilder.build())); assertEquals(complete.hashCode(), completeBuilder.build().hashCode()); + // The infos from the two builders should have equal content, but should not be equal due to + // their different IDs. + assertNotEquals(complete, completeBuilder2.build()); + assertTrue(complete.equalContent(completeBuilder2.build())); + VibratorInfo completeWithComposeControl = completeBuilder .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .build(); assertNotEquals(complete, completeWithComposeControl); + assertFalse(complete.equalContent(completeWithComposeControl)); VibratorInfo completeWithNoEffects = completeBuilder .setSupportedEffects(new int[0]) .build(); assertNotEquals(complete, completeWithNoEffects); + assertFalse(complete.equalContent(completeWithNoEffects)); VibratorInfo completeWithUnknownEffects = completeBuilder .setSupportedEffects(null) .build(); assertNotEquals(complete, completeWithUnknownEffects); + assertFalse(complete.equalContent(completeWithUnknownEffects)); VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) .build(); assertNotEquals(complete, completeWithDifferentPrimitiveDuration); + assertFalse(complete.equalContent(completeWithDifferentPrimitiveDuration)); VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder .setFrequencyProfile(new VibratorInfo.FrequencyProfile( @@ -291,31 +319,37 @@ public class VibratorInfoTest { TEST_AMPLITUDE_MAP)) .build(); assertNotEquals(complete, completeWithDifferentFrequencyProfile); + assertFalse(complete.equalContent(completeWithDifferentFrequencyProfile)); VibratorInfo completeWithEmptyFrequencyProfile = completeBuilder .setFrequencyProfile(EMPTY_FREQUENCY_PROFILE) .build(); assertNotEquals(complete, completeWithEmptyFrequencyProfile); + assertFalse(complete.equalContent(completeWithEmptyFrequencyProfile)); VibratorInfo completeWithUnknownQFactor = completeBuilder.setQFactor(Float.NaN).build(); assertNotEquals(complete, completeWithUnknownQFactor); + assertFalse(complete.equalContent(completeWithUnknownQFactor)); VibratorInfo completeWithDifferentQFactor = completeBuilder .setQFactor(complete.getQFactor() + 3f) .build(); assertNotEquals(complete, completeWithDifferentQFactor); + assertFalse(complete.equalContent(completeWithDifferentQFactor)); VibratorInfo unknownEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); VibratorInfo knownEmptyEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setSupportedEffects(new int[0]) .build(); assertNotEquals(unknownEffectSupport, knownEmptyEffectSupport); + assertFalse(unknownEffectSupport.equalContent(knownEmptyEffectSupport)); VibratorInfo unknownBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); VibratorInfo knownEmptyBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setSupportedBraking(new int[0]) .build(); assertNotEquals(unknownBrakingSupport, knownEmptyBrakingSupport); + assertFalse(unknownBrakingSupport.equalContent(knownEmptyBrakingSupport)); } @Test @@ -335,4 +369,186 @@ public class VibratorInfoTest { assertEquals(original, restored); assertEquals(original.hashCode(), restored.hashCode()); } + + @Test + public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudeUnsupported() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build(); + + // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT). + assertFalse(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30))); + assertFalse(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255))); + assertFalse(info.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 40))); + } + + @Test + public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudeSupported() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build(); + + // All amplitudes are min, max, or default. Requires no amplitude control. + assertTrue(info.areVibrationFeaturesSupported( + waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255))); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1))); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE))); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 255))); + } + + @Test + public void areVibrationFeaturesSupported_withAmplitudeControl_allWaveformsSupported() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build(); + + // All forms of amplitudes are valid when amplitude control is available. + assertTrue(info.areVibrationFeaturesSupported( + waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255))); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1))); + assertTrue(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50))); + assertTrue(info.areVibrationFeaturesSupported( + waveformWithAmplitudes(7, 255, 0, 0, 60))); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE))); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 255))); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 40))); + } + + @Test + public void areVibrationFeaturesSupported_compositionsWithSupportedPrimitivesSupported() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build(); + + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose())); + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + /* scale= */ 0.2f, + /* delay= */ 200) + .compose())); + } + + @Test + public void areVibrationFeaturesSupported_compositionsWithUnupportedPrimitivesUnsupported() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build(); + + assertFalse(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .compose())); + assertFalse(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK) + .compose())); + } + + @Test + public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP) + .build(); + + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) + .compose())); + + info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10) + .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK) + .build(); + + assertTrue(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .addEffect(VibrationEffect.createWaveform( + // These timings are given either 0 or default amplitudes, which + // do not require vibrator's amplitude control. + /* timings= */ new long[] {1, 2, 3}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) + .compose())); + } + + @Test + public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP) + .build(); + + // Not supported due to the TICK primitive, which the vibrator has no support for. + assertFalse(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .compose())); + // Not supported due to the THUD effect, which the vibrator has no support for. + assertFalse(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD)) + .compose())); + + info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10) + .setSupportedEffects(VibrationEffect.EFFECT_POP) + .build(); + + // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or + // DEFAULT), because the vibrator has no amplitude control. + assertFalse(info.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) + .compose())); + } + + private static VibrationEffect waveformWithAmplitudes(int...amplitudes) { + long[] timings = new long[amplitudes.length]; + for (int i = 0; i < timings.length; i++) { + timings[i] = i * 2; // Arbitrary timings. + } + return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1); + } } diff --git a/core/tests/vibrator/src/android/os/VibratorTest.java b/core/tests/vibrator/src/android/os/VibratorTest.java index c559e34d92a3..cfa12bb5b504 100644 --- a/core/tests/vibrator/src/android/os/VibratorTest.java +++ b/core/tests/vibrator/src/android/os/VibratorTest.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; -import android.hardware.vibrator.IVibrator; import android.media.AudioAttributes; import android.os.test.TestLooper; @@ -60,8 +59,6 @@ public class VibratorTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); - private static final float TEST_TOLERANCE = 1e-5f; - private Context mContextSpy; private Vibrator mVibratorSpy; private TestLooper mTestLooper; @@ -79,9 +76,6 @@ public class VibratorTest { @Test public void getId_returnsDefaultId() { assertEquals(-1, mVibratorSpy.getId()); - assertEquals(-1, new SystemVibrator.NoVibratorInfo().getId()); - assertEquals(-1, new SystemVibrator.MultiVibratorInfo(new VibratorInfo[] { - VibratorInfo.EMPTY_VIBRATOR_INFO, VibratorInfo.EMPTY_VIBRATOR_INFO }).getId()); } @Test @@ -95,53 +89,6 @@ public class VibratorTest { } @Test - public void areEffectsSupported_noVibrator_returnsAlwaysNo() { - VibratorInfo info = new SystemVibrator.NoVibratorInfo(); - assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO, - info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); - } - - @Test - public void areEffectsSupported_unsupportedInOneVibrator_returnsNo() { - VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setSupportedEffects(VibrationEffect.EFFECT_CLICK) - .build(); - VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setSupportedEffects(new int[0]) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{supportedVibrator, unsupportedVibrator}); - assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO, - info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); - } - - @Test - public void areEffectsSupported_unknownInOneVibrator_returnsUnknown() { - VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setSupportedEffects(VibrationEffect.EFFECT_CLICK) - .build(); - VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO; - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{supportedVibrator, unknownSupportVibrator}); - assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN, - info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); - } - - @Test - public void arePrimitivesSupported_supportedInAllVibrators_returnsYes() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setSupportedEffects(VibrationEffect.EFFECT_CLICK) - .build(); - VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setSupportedEffects(VibrationEffect.EFFECT_CLICK) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, secondVibrator}); - assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES, - info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); - } - - @Test public void arePrimitivesSupported_returnsArrayOfSameSize() { assertEquals(0, mVibratorSpy.arePrimitivesSupported(new int[0]).length); assertEquals(1, mVibratorSpy.arePrimitivesSupported( @@ -152,39 +99,6 @@ public class VibratorTest { } @Test - public void arePrimitivesSupported_noVibrator_returnsAlwaysFalse() { - VibratorInfo info = new SystemVibrator.NoVibratorInfo(); - assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); - } - - @Test - public void arePrimitivesSupported_unsupportedInOneVibrator_returnsFalse() { - VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) - .build(); - VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO; - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{supportedVibrator, unsupportedVibrator}); - assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); - } - - @Test - public void arePrimitivesSupported_supportedInAllVibrators_returnsTrue() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) - .build(); - VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, secondVibrator}); - assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); - } - - @Test public void getPrimitivesDurations_returnsArrayOfSameSize() { assertEquals(0, mVibratorSpy.getPrimitiveDurations(new int[0]).length); assertEquals(1, mVibratorSpy.getPrimitiveDurations( @@ -195,245 +109,6 @@ public class VibratorTest { } @Test - public void getPrimitivesDurations_noVibrator_returnsAlwaysZero() { - VibratorInfo info = new SystemVibrator.NoVibratorInfo(); - assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK)); - } - - @Test - public void getPrimitivesDurations_unsupportedInOneVibrator_returnsZero() { - VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) - .build(); - VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO; - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{supportedVibrator, unsupportedVibrator}); - assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK)); - } - - @Test - public void getPrimitivesDurations_supportedInAllVibrators_returnsMaxDuration() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) - .build(); - VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, secondVibrator}); - assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK)); - } - - @Test - public void getQFactorAndResonantFrequency_noVibrator_returnsNaN() { - VibratorInfo info = new SystemVibrator.NoVibratorInfo(); - - assertTrue(Float.isNaN(info.getQFactor())); - assertTrue(Float.isNaN(info.getResonantFrequencyHz())); - } - - @Test - public void getQFactorAndResonantFrequency_differentValues_returnsNaN() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setQFactor(1f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null)) - .build(); - VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setQFactor(2f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null)) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, secondVibrator}); - - assertTrue(Float.isNaN(info.getQFactor())); - assertTrue(Float.isNaN(info.getResonantFrequencyHz())); - assertEmptyFrequencyProfileAndControl(info); - - // One vibrator with values undefined. - VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build(); - info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, thirdVibrator}); - - assertTrue(Float.isNaN(info.getQFactor())); - assertTrue(Float.isNaN(info.getResonantFrequencyHz())); - assertEmptyFrequencyProfileAndControl(info); - } - - @Test - public void getQFactorAndResonantFrequency_sameValues_returnsValue() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setQFactor(10f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile( - /* resonantFrequencyHz= */ 11, 10, 0.5f, null)) - .build(); - VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setQFactor(10f) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile( - /* resonantFrequencyHz= */ 11, 5, 1, null)) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, secondVibrator}); - - assertEquals(10f, info.getQFactor(), TEST_TOLERANCE); - assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE); - - // No frequency range defined. - assertTrue(info.getFrequencyProfile().isEmpty()); - assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); - } - - @Test - public void getFrequencyProfile_noVibrator_returnsEmpty() { - VibratorInfo info = new SystemVibrator.NoVibratorInfo(); - - assertEmptyFrequencyProfileAndControl(info); - } - - @Test - public void getFrequencyProfile_differentResonantFrequencyOrResolutionValues_returnsEmpty() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, - new float[] { 0, 1 })) - .build(); - VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1, - new float[] { 0, 1 })) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, differentResonantFrequency}); - - assertEmptyFrequencyProfileAndControl(info); - - VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2, - new float[] { 0, 1 })) - .build(); - info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, differentFrequencyResolution}); - - assertEmptyFrequencyProfileAndControl(info); - } - - @Test - public void getFrequencyProfile_missingValues_returnsEmpty() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, - new float[] { 0, 1 })) - .build(); - VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1, - new float[] { 0, 1 })) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, missingResonantFrequency}); - - assertEmptyFrequencyProfileAndControl(info); - - VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1, - new float[] { 0, 1 })) - .build(); - info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, missingMinFrequency}); - - assertEmptyFrequencyProfileAndControl(info); - - VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN, - new float[] { 0, 1 })) - .build(); - info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, missingFrequencyResolution}); - - assertEmptyFrequencyProfileAndControl(info); - - VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null)) - .build(); - info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, missingMaxAmplitudes}); - - assertEmptyFrequencyProfileAndControl(info); - } - - @Test - public void getFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f, - new float[] { 0, 1, 1, 0 })) - .build(); - VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f, - new float[] { 0, 1, 1, 0 })) - .build(); - VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, - new float[] { 0, 1, 1, 0 })) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, unalignedMinFrequency, thirdVibrator}); - - assertEmptyFrequencyProfileAndControl(info); - } - - @Test - public void getFrequencyProfile_alignedProfiles_returnsIntersection() { - VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f, - new float[] { 0.5f, 1, 1, 0.5f })) - .build(); - VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, - new float[] { 1, 1, 1 })) - .build(); - VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3) - .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, - new float[] { 0.8f, 1, 0.8f, 0.5f })) - .build(); - VibratorInfo info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator}); - - assertEquals( - new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }), - info.getFrequencyProfile()); - assertEquals(true, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); - - // Third vibrator without frequency control capability. - thirdVibrator = new VibratorInfo.Builder(/* id= */ 3) - .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, - new float[] { 0.8f, 1, 0.8f, 0.5f })) - .build(); - info = new SystemVibrator.MultiVibratorInfo( - new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator}); - - assertEquals( - new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }), - info.getFrequencyProfile()); - assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); - } - - @Test public void onVibratorStateChanged_noVibrator_registersNoListenerToVibratorManager() { VibratorManager mockVibratorManager = mock(VibratorManager.class); when(mockVibratorManager.getVibratorIds()).thenReturn(new int[0]); @@ -577,212 +252,4 @@ public class VibratorTest { VibrationAttributes vibrationAttributes = captor.getValue(); assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes); } - - @Test - public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudes() { - Vibrator vibrator = - createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedEffects(VibrationEffect.EFFECT_THUD) - .build()); - - // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT). - assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30))); - assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255))); - assertFalse(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createOneShot(20, /* amplitude= */ 40))); - } - - @Test - public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudes() { - Vibrator vibrator = - createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedEffects(VibrationEffect.EFFECT_THUD) - .build()); - - // All amplitudes are min, max, or default. Requires no amplitude control. - assertTrue(vibrator.areVibrationFeaturesSupported( - waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255))); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createWaveform( - /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1))); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE))); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createOneShot(20, /* amplitude= */ 255))); - } - - @Test - public void areVibrationFeaturesSupported_withAmplitudeControl() { - Vibrator vibrator = - createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) - .build()); - - // All forms of amplitudes are valid when amplitude control is available. - assertTrue(vibrator.areVibrationFeaturesSupported( - waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255))); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createWaveform( - /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1))); - assertTrue(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50))); - assertTrue(vibrator.areVibrationFeaturesSupported( - waveformWithAmplitudes(7, 255, 0, 0, 60))); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE))); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createOneShot(20, /* amplitude= */ 255))); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.createOneShot(20, /* amplitude= */ 40))); - } - - @Test - public void areVibrationFeaturesSupported_primitiveCompositionsWithSupportedPrimitives() { - Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) - .build()); - - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) - .compose())); - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive( - VibrationEffect.Composition.PRIMITIVE_CLICK, - /* scale= */ 0.2f, - /* delay= */ 200) - .compose())); - } - - @Test - public void areVibrationFeaturesSupported_primitiveCompositionsWithUnupportedPrimitives() { - Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) - .build()); - - assertFalse(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) - .compose())); - assertFalse(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK) - .compose())); - } - - @Test - public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() { - Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) - .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP) - .build()); - - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) - .addEffect(VibrationEffect.createWaveform( - /* timings= */ new long[] {1, 2, 3}, - /* amplitudes= */ new int[] {10, 20, 255}, - /* repeatIndex= */ -1)) - .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) - .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) - .compose())); - - vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10) - .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK) - .build()); - - assertTrue(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) - .addEffect(VibrationEffect.createWaveform( - // These timings are given either 0 or default amplitudes, which - // do not require vibrator's amplitude control. - /* timings= */ new long[] {1, 2, 3}, - /* repeatIndex= */ -1)) - .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) - .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) - .compose())); - } - - @Test - public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() { - Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) - .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP) - .build()); - - // Not supported due to the TICK primitive, which the vibrator has no support for. - assertFalse(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK) - .addEffect(VibrationEffect.createWaveform( - /* timings= */ new long[] {1, 2, 3}, - /* amplitudes= */ new int[] {10, 20, 255}, - /* repeatIndex= */ -1)) - .compose())); - // Not supported due to the THUD effect, which the vibrator has no support for. - assertFalse(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) - .addEffect(VibrationEffect.createWaveform( - /* timings= */ new long[] {1, 2, 3}, - /* amplitudes= */ new int[] {10, 20, 255}, - /* repeatIndex= */ -1)) - .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD)) - .compose())); - - vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10) - .setSupportedEffects(VibrationEffect.EFFECT_POP) - .build()); - - // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or - // DEFAULT), because the vibrator has no amplitude control. - assertFalse(vibrator.areVibrationFeaturesSupported( - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) - .addEffect(VibrationEffect.createWaveform( - /* timings= */ new long[] {1, 2, 3}, - /* amplitudes= */ new int[] {10, 20, 255}, - /* repeatIndex= */ -1)) - .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) - .compose())); - } - - /** - * Asserts that the frequency profile is empty, and therefore frequency control isn't supported. - */ - void assertEmptyFrequencyProfileAndControl(VibratorInfo info) { - assertTrue(info.getFrequencyProfile().isEmpty()); - assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); - } - - private Vibrator createVibratorWithCustomInfo(VibratorInfo info) { - return new SystemVibrator(mContextSpy) { - @Override - public VibratorInfo getInfo() { - return info; - } - }; - } - - private static VibrationEffect waveformWithAmplitudes(int...amplitudes) { - long[] timings = new long[amplitudes.length]; - for (int i = 0; i < timings.length; i++) { - timings[i] = i * 2; // Arbitrary timings. - } - return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1); - } } diff --git a/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java new file mode 100644 index 000000000000..fc31ac44b362 --- /dev/null +++ b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java @@ -0,0 +1,361 @@ +/* + * Copyright 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 android.os.vibrator; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; + +import android.hardware.vibrator.IVibrator; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.VibratorInfo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class MultiVibratorInfoTest { + private static final float TEST_TOLERANCE = 1e-5f; + + @Test + public void testGetId() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .build(); + VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) + .setSupportedEffects(new int[0]) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 3, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertEquals(3, info.getId()); + } + + @Test + public void testIsEffectSupported_supportedInAllVibrators_returnsYes() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .build(); + VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES, + info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); + } + + @Test + public void testIsEffectSupported_unsupportedInOneVibrator_returnsNo() { + VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .build(); + VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2) + .setSupportedEffects(new int[0]) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{supportedVibrator, unsupportedVibrator}); + + assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO, + info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); + } + + @Test + public void testIsEffectSupported_unknownInOneVibrator_returnsUnknown() { + VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .build(); + VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO; + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{supportedVibrator, unknownSupportVibrator}); + assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN, + info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); + } + + @Test + public void testIsPrimitiveSupported_unsupportedInOneVibrator_returnsFalse() { + VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build(); + VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO; + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{supportedVibrator, unsupportedVibrator}); + + assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); + } + + @Test + public void testIsPrimitiveSupported_supportedInAllVibrators_returnsTrue() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) + .build(); + VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); + } + + @Test + public void testGetPrimitiveDuration_unsupportedInOneVibrator_returnsZero() { + VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build(); + VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO; + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{supportedVibrator, unsupportedVibrator}); + + assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK)); + } + + @Test + public void testGetPrimitiveDuration_supportedInAllVibrators_returnsMaxDuration() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build(); + VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK)); + } + + @Test + public void testGetQFactorAndResonantFrequency_differentValues_returnsNaN() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setQFactor(1f) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null)) + .build(); + VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setQFactor(2f) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null)) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertTrue(Float.isNaN(info.getQFactor())); + assertTrue(Float.isNaN(info.getResonantFrequencyHz())); + assertEmptyFrequencyProfileAndControl(info); + + // One vibrator with values undefined. + VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build(); + info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, thirdVibrator}); + + assertTrue(Float.isNaN(info.getQFactor())); + assertTrue(Float.isNaN(info.getResonantFrequencyHz())); + assertEmptyFrequencyProfileAndControl(info); + } + + @Test + public void testGetQFactorAndResonantFrequency_sameValues_returnsValue() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setQFactor(10f) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile( + /* resonantFrequencyHz= */ 11, 10, 0.5f, null)) + .build(); + VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setQFactor(10f) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile( + /* resonantFrequencyHz= */ 11, 5, 1, null)) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo}); + + assertEquals(10f, info.getQFactor(), TEST_TOLERANCE); + assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE); + // No frequency range defined. + assertTrue(info.getFrequencyProfile().isEmpty()); + assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); + } + + @Test + public void testGetFrequencyProfile_differentResonantFrequencyOrResolutions_returnsEmpty() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, + new float[] { 0, 1 })) + .build(); + VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1, + new float[] { 0, 1 })) + .build(); + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, differentResonantFrequency}); + + assertEmptyFrequencyProfileAndControl(info); + + VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2, + new float[] { 0, 1 })) + .build(); + info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, differentFrequencyResolution}); + + assertEmptyFrequencyProfileAndControl(info); + } + + @Test + public void testGetFrequencyProfile_missingValues_returnsEmpty() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, + new float[] { 0, 1 })) + .build(); + VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1, + new float[] { 0, 1 })) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, missingResonantFrequency}); + + assertEmptyFrequencyProfileAndControl(info); + + VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1, + new float[] { 0, 1 })) + .build(); + info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, missingMinFrequency}); + + assertEmptyFrequencyProfileAndControl(info); + + VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN, + new float[] { 0, 1 })) + .build(); + info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, missingFrequencyResolution}); + + assertEmptyFrequencyProfileAndControl(info); + + VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null)) + .build(); + info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, missingMaxAmplitudes}); + + assertEmptyFrequencyProfileAndControl(info); + } + + @Test + public void testGetFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f, + new float[] { 0, 1, 1, 0 })) + .build(); + VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f, + new float[] { 0, 1, 1, 0 })) + .build(); + VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + new float[] { 0, 1, 1, 0 })) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, unalignedMinFrequency, thirdVibrator}); + + assertEmptyFrequencyProfileAndControl(info); + } + + @Test + public void testGetFrequencyProfile_alignedProfiles_returnsIntersection() { + VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f, + new float[] { 0.5f, 1, 1, 0.5f })) + .build(); + VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + new float[] { 1, 1, 1 })) + .build(); + VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + new float[] { 0.8f, 1, 0.8f, 0.5f })) + .build(); + + VibratorInfo info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo, thirdVibrator}); + + assertEquals( + new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }), + info.getFrequencyProfile()); + assertEquals(true, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); + + // Third vibrator without frequency control capability. + thirdVibrator = new VibratorInfo.Builder(/* id= */ 3) + .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, + new float[] { 0.8f, 1, 0.8f, 0.5f })) + .build(); + info = new MultiVibratorInfo(/* id= */ 1, + new VibratorInfo[]{firstInfo, secondInfo, thirdVibrator}); + + assertEquals( + new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }), + info.getFrequencyProfile()); + assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); + } + + /** + * Asserts that the frequency profile is empty, and therefore frequency control isn't supported. + */ + private void assertEmptyFrequencyProfileAndControl(VibratorInfo info) { + assertTrue(info.getFrequencyProfile().isEmpty()); + assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); + } +} diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java index 8268077e72e9..4f5f3c0ddeaf 100644 --- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java @@ -25,18 +25,14 @@ import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertThrows; import android.os.Parcel; -import android.os.SystemVibrator; import android.os.VibrationEffect; -import android.os.Vibrator; import android.os.VibratorInfo; -import androidx.test.InstrumentationRegistry; - import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.junit.runners.JUnit4; -@RunWith(MockitoJUnitRunner.class) +@RunWith(JUnit4.class) public class PrebakedSegmentTest { @Test @@ -149,121 +145,121 @@ public class PrebakedSegmentTest { @Test public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_vibratorSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects( + VibratorInfo info = createVibratorInfoWithSupportedEffects( VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_noVibratorSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_vibratorSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects( + VibratorInfo info = createVibratorInfoWithSupportedEffects( VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK); assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_noVibratorSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]); assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_vibratorSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects( + VibratorInfo info = createVibratorInfoWithSupportedEffects( VibrationEffect.EFFECT_THUD, VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_TEXTURE_TICK); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_POP) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_noVibratorSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]); assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_THUD) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_POP) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_vibratorSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects( + VibratorInfo info = createVibratorInfoWithSupportedEffects( VibrationEffect.EFFECT_THUD, VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_TEXTURE_TICK); assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_noVibSupport() { - Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]); assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK) - .areVibrationFeaturesSupported(vibrator)); + .areVibrationFeaturesSupported(info)); } @Test @@ -283,14 +279,9 @@ public class PrebakedSegmentTest { return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); } - private static Vibrator createVibratorWithSupportedEffects(int... supportedEffects) { - return new SystemVibrator(InstrumentationRegistry.getContext()) { - @Override - public VibratorInfo getInfo() { - return new VibratorInfo.Builder(/* id= */ 1) - .setSupportedEffects(supportedEffects) - .build(); - } - }; + private static VibratorInfo createVibratorInfoWithSupportedEffects(int... supportedEffects) { + return new VibratorInfo.Builder(/* id= */ 1) + .setSupportedEffects(supportedEffects) + .build(); } } diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java index 6f5adcd26ba5..ec5a084c2ddb 100644 --- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java @@ -25,18 +25,14 @@ import static org.testng.Assert.assertThrows; import android.hardware.vibrator.IVibrator; import android.os.Parcel; -import android.os.SystemVibrator; import android.os.VibrationEffect; -import android.os.Vibrator; import android.os.VibratorInfo; -import androidx.test.InstrumentationRegistry; - import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.junit.runners.JUnit4; -@RunWith(MockitoJUnitRunner.class) +@RunWith(JUnit4.class) public class PrimitiveSegmentTest { private static final float TOLERANCE = 1e-2f; @@ -146,15 +142,15 @@ public class PrimitiveSegmentTest { public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() { assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK) .areVibrationFeaturesSupported( - createVibratorWithSupportedPrimitive( + createVibratorInfoWithSupportedPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK))); assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD) .areVibrationFeaturesSupported( - createVibratorWithSupportedPrimitive( + createVibratorInfoWithSupportedPrimitive( VibrationEffect.Composition.PRIMITIVE_THUD))); assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE) .areVibrationFeaturesSupported( - createVibratorWithSupportedPrimitive( + createVibratorInfoWithSupportedPrimitive( VibrationEffect.Composition.PRIMITIVE_QUICK_RISE))); } @@ -162,15 +158,15 @@ public class PrimitiveSegmentTest { public void testVibrationFeaturesSupport_primitiveNotSupportedByVibrator() { assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK) .areVibrationFeaturesSupported( - createVibratorWithSupportedPrimitive( + createVibratorInfoWithSupportedPrimitive( VibrationEffect.Composition.PRIMITIVE_THUD))); assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD) .areVibrationFeaturesSupported( - createVibratorWithSupportedPrimitive( + createVibratorInfoWithSupportedPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK))); assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD) .areVibrationFeaturesSupported( - createVibratorWithSupportedPrimitive( + createVibratorInfoWithSupportedPrimitive( VibrationEffect.Composition.PRIMITIVE_QUICK_RISE))); } @@ -193,15 +189,10 @@ public class PrimitiveSegmentTest { return new PrimitiveSegment(primitiveId, 0.2f, 10); } - private static Vibrator createVibratorWithSupportedPrimitive(int primitiveId) { - return new SystemVibrator(InstrumentationRegistry.getContext()) { - @Override - public VibratorInfo getInfo() { - return new VibratorInfo.Builder(/* id= */ 1) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(primitiveId, 10) - .build(); - } - }; + private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) { + return new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(primitiveId, 10) + .build(); } } diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java index 68870e5a2979..5caa86bb9fb5 100644 --- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java @@ -23,31 +23,21 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; -import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; +import android.hardware.vibrator.IVibrator; import android.os.Parcel; import android.os.VibrationEffect; -import android.os.Vibrator; +import android.os.VibratorInfo; -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.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; +import org.junit.runners.JUnit4; -@RunWith(MockitoJUnitRunner.class) +@RunWith(JUnit4.class) public class RampSegmentTest { private static final float TOLERANCE = 1e-2f; - @Rule - public MockitoRule mMockitoRule = MockitoJUnit.rule(); - - @Mock - private Vibrator mVibrator; - @Test public void testCreation() { RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0, @@ -147,71 +137,71 @@ public class RampSegmentTest { @Test public void testVibrationFeaturesSupport_amplitudeAndFrequencyControls_supported() { - when(mVibrator.hasAmplitudeControl()).thenReturn(true); - when(mVibrator.hasFrequencyControl()).thenReturn(true); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true); // Increasing amplitude - assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(info)); // Increasing frequency - assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(mVibrator)); + assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(info)); // Decreasing amplitude - assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(info)); // Decreasing frequency - assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(info)); // Zero duration - assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(mVibrator)); + assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_noAmplitudeControl_unsupportedForChangingAmplitude() { - when(mVibrator.hasAmplitudeControl()).thenReturn(false); - when(mVibrator.hasFrequencyControl()).thenReturn(true); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true); // Test with increasing/decreasing amplitudes. - assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); - assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(info)); + assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_noAmplitudeControl_fractionalAmplitudeUnsupported() { - when(mVibrator.hasAmplitudeControl()).thenReturn(false); - when(mVibrator.hasFrequencyControl()).thenReturn(true); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true); - assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); - assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); - assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(info)); + assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(info)); + assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_unchangingZeroAmplitude_supported() { RampSegment amplitudeZeroWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10); RampSegment amplitudeZeroWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10); - when(mVibrator.hasFrequencyControl()).thenReturn(true); - when(mVibrator.hasAmplitudeControl()).thenReturn(false); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true); - assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); - assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(info)); + assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(info)); - when(mVibrator.hasAmplitudeControl()).thenReturn(true); + info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true); - assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); - assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(info)); + assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_unchangingOneAmplitude_supported() { RampSegment amplitudeOneWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10); RampSegment amplitudeOneWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10); - when(mVibrator.hasFrequencyControl()).thenReturn(true); - when(mVibrator.hasAmplitudeControl()).thenReturn(false); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true); - assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); - assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(info)); + assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(info)); - when(mVibrator.hasAmplitudeControl()).thenReturn(true); + info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true); - assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); - assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(info)); + assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(info)); } @Test @@ -220,52 +210,52 @@ public class RampSegmentTest { new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.5f, 0.8f, 10); RampSegment defaultAmplitudeDecreasingFrequency = new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.8f, 0.5f, 10); - when(mVibrator.hasFrequencyControl()).thenReturn(true); - when(mVibrator.hasAmplitudeControl()).thenReturn(false); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true); - assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); - assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(info)); + assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(info)); - when(mVibrator.hasAmplitudeControl()).thenReturn(true); + info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true); - assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); - assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(info)); + assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_noFrequencyControl_unsupportedForChangingFrequency() { - when(mVibrator.hasAmplitudeControl()).thenReturn(true); - when(mVibrator.hasFrequencyControl()).thenReturn(false); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false); // Test with increasing/decreasing frequencies. - assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(mVibrator)); - assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(info)); + assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_noFrequencyControl_fractionalFrequencyUnsupported() { - when(mVibrator.hasAmplitudeControl()).thenReturn(true); - when(mVibrator.hasFrequencyControl()).thenReturn(false); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false); - assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator)); - assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(mVibrator)); - assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(info)); + assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(info)); + assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_unchangingZeroFrequency_supported() { RampSegment frequencyZeroWithIncreasingAmplitude = new RampSegment(0.1f, 1, 0, 0, 10); RampSegment frequencyZeroWithDecreasingAmplitude = new RampSegment(1, 0.1f, 0, 0, 10); - when(mVibrator.hasAmplitudeControl()).thenReturn(true); - when(mVibrator.hasFrequencyControl()).thenReturn(false); + VibratorInfo info = + createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false); - assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); - assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); + assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(info)); + assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(info)); - when(mVibrator.hasFrequencyControl()).thenReturn(true); + info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true); - assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); - assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); + assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(info)); + assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(info)); } @Test @@ -274,4 +264,17 @@ public class RampSegmentTest { // duration checked in VibrationEffect implementations. assertTrue(new RampSegment(0.5f, 1, 0, 0, 5_000).isHapticFeedbackCandidate()); } + + private static VibratorInfo createVibInfo( + boolean hasAmplitudeControl, boolean hasFrequencyControl) { + VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1); + long capabilities = 0; + if (hasAmplitudeControl) { + capabilities |= IVibrator.CAP_AMPLITUDE_CONTROL; + } + if (hasFrequencyControl) { + capabilities |= (IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); + } + return builder.setCapabilities(capabilities).build(); + } } diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java index 34bb892b07d3..44db30603089 100644 --- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java @@ -21,31 +21,20 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; -import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; +import android.hardware.vibrator.IVibrator; import android.os.Parcel; import android.os.VibrationEffect; -import android.os.Vibrator; +import android.os.VibratorInfo; -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.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; +import org.junit.runners.JUnit4; -@RunWith(MockitoJUnitRunner.class) +@RunWith(JUnit4.class) public class StepSegmentTest { private static final float TOLERANCE = 1e-2f; - - @Rule - public MockitoRule mMockitoRule = MockitoJUnit.rule(); - - @Mock - private Vibrator mVibrator; - @Test public void testCreation() { StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f, @@ -160,26 +149,26 @@ public class StepSegmentTest { public void testVibrationFeaturesSupport_zeroAmplitude_supported() { StepSegment segment = new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 0); - when(mVibrator.hasAmplitudeControl()).thenReturn(true); + VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); - when(mVibrator.hasAmplitudeControl()).thenReturn(false); + info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_maxAmplitude_supported() { StepSegment segment = new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 0); - when(mVibrator.hasAmplitudeControl()).thenReturn(true); + VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); - when(mVibrator.hasAmplitudeControl()).thenReturn(false); + info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); } @Test @@ -189,60 +178,60 @@ public class StepSegmentTest { /* amplitude= */ VibrationEffect.DEFAULT_AMPLITUDE, /* frequencyHz= */ 0, /* duration= */ 0); - when(mVibrator.hasAmplitudeControl()).thenReturn(true); + VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); - when(mVibrator.hasAmplitudeControl()).thenReturn(false); + info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_fractionalAmplitude_hasAmplitudeCtrl_supported() { - when(mVibrator.hasAmplitudeControl()).thenReturn(true); + VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true); assertTrue(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0) - .areVibrationFeaturesSupported(mVibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_fractionalAmplitude_hasNoAmplitudeCtrl_notSupported() { - when(mVibrator.hasAmplitudeControl()).thenReturn(false); + VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false); assertFalse(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0) - .areVibrationFeaturesSupported(mVibrator)); + .areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_zeroFrequency_supported() { StepSegment segment = new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0, /* duration= */ 0); - when(mVibrator.hasFrequencyControl()).thenReturn(false); + VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ false); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); - when(mVibrator.hasFrequencyControl()).thenReturn(true); + info = createVibInfoForFrequency(/* hasFrequencyControl= */ true); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_nonZeroFrequency_hasFrequencyCtrl_supported() { StepSegment segment = new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0); - when(mVibrator.hasFrequencyControl()).thenReturn(true); + VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ true); - assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + assertTrue(segment.areVibrationFeaturesSupported(info)); } @Test public void testVibrationFeaturesSupport_nonZeroFrequency_hasNoFrequencyCtrl_notSupported() { StepSegment segment = new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0); - when(mVibrator.hasFrequencyControl()).thenReturn(false); + VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ false); - assertFalse(segment.areVibrationFeaturesSupported(mVibrator)); + assertFalse(segment.areVibrationFeaturesSupported(info)); } @Test @@ -251,4 +240,21 @@ public class StepSegmentTest { // duration checked in VibrationEffect implementations. assertTrue(new StepSegment(0, 0, 5_000).isHapticFeedbackCandidate()); } + + private static VibratorInfo createVibInfoForAmplitude(boolean hasAmplitudeControl) { + VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1); + if (hasAmplitudeControl) { + builder.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + } + return builder.build(); + } + + private static VibratorInfo createVibInfoForFrequency(boolean hasFrequencyControl) { + VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1); + if (hasFrequencyControl) { + builder.setCapabilities( + IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); + } + return builder.build(); + } } diff --git a/core/tests/vibrator/src/android/os/vibrator/VibratorInfoFactoryTest.java b/core/tests/vibrator/src/android/os/vibrator/VibratorInfoFactoryTest.java new file mode 100644 index 000000000000..df4822fc8b04 --- /dev/null +++ b/core/tests/vibrator/src/android/os/vibrator/VibratorInfoFactoryTest.java @@ -0,0 +1,130 @@ +/* + * Copyright 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 android.os.vibrator; + +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; + +import android.hardware.vibrator.IVibrator; +import android.os.VibrationEffect; +import android.os.VibratorInfo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class VibratorInfoFactoryTest { + + @Test + public void testCreatedInfo_hasTheRequestedId() { + // Empty info list. + VibratorInfo infoFromEmptyInfos = + VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {}); + VibratorInfo info1 = new VibratorInfo.Builder(/* id= */ 1) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .build(); + VibratorInfo info2 = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build(); + VibratorInfo infoFromOneInfo = + VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info1}); + VibratorInfo infoFromTwoInfos = + VibratorInfoFactory.create(/* id= */ -3, new VibratorInfo[] {info1, info2}); + + assertEquals(3, infoFromEmptyInfos.getId()); + assertEquals(-1, infoFromOneInfo.getId()); + assertEquals(-3, infoFromTwoInfos.getId()); + } + + @Test + public void testCreatedInfo_fromEmptyVibratorInfos_returnsEmptyVibratorInfo() { + VibratorInfo info = VibratorInfoFactory.create(/* id= */ 2, new VibratorInfo[] {}); + + assertEqualContent(VibratorInfo.EMPTY_VIBRATOR_INFO, info); + } + + @Test + public void testCreatedInfo_fromSingleVibratorInfo_hasEqualContent() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_FREQUENCY_CONTROL) + .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_THUD) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 30) + .build(); + + VibratorInfo createdInfo = + VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info}); + + assertEqualContent(info, createdInfo); + } + + @Test + public void testCreatedInfo_hasEqualContentRegardlessOfSourceInfoOrder() { + VibratorInfo info1 = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .build(); + VibratorInfo info2 = new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build(); + + assertEqualContent( + VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info1, info2}), + VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info2, info1})); + } + + @Test + public void testCreatedInfoContents() { + VibratorInfo info1 = new VibratorInfo.Builder(/* id= */ -1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_FREQUENCY_CONTROL) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_POP) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 5) + .build(); + VibratorInfo info2 = new VibratorInfo.Builder(/* id= */ -2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) + .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_THUD) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 20) + .build(); + VibratorInfo info3 = new VibratorInfo.Builder(/* id= */ -3) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build(); + + assertEquals( + new VibratorInfo.Builder(/* id= */ 3) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedEffects(VibrationEffect.EFFECT_POP) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 20) + .build(), + VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {info1, info2})); + assertEquals( + new VibratorInfo.Builder(/* id= */ 3) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build(), + VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {info2, info3})); + assertEquals( + new VibratorInfo.Builder(/* id= */ 3).build(), + VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {info1, info3})); + } + + private static void assertEqualContent(VibratorInfo info1, VibratorInfo info2) { + assertTrue(info1.equalContent(info2)); + } +} diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java index f639521ff250..b5fb13db4ae4 100644 --- a/graphics/java/android/graphics/Gainmap.java +++ b/graphics/java/android/graphics/Gainmap.java @@ -124,8 +124,6 @@ public final class Gainmap implements Parcelable { /** * Creates a new gainmap using the provided gainmap as the metadata source and the provided * bitmap as the replacement for the gainmapContents - * TODO: Make public, it's useful - * @hide */ public Gainmap(@NonNull Gainmap gainmap, @NonNull Bitmap gainmapContents) { this(gainmapContents, nCreateCopy(gainmap.mNativePtr)); diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 98629a24696a..36bfb98e726b 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -235,6 +235,22 @@ public final class SystemFonts { } /** + * Get the updated FontConfig. + * + * @param updatableFontMap a font mapping of updated font files. + * @hide + */ + public static @NonNull FontConfig getSystemFontConfigForTesting( + @NonNull String fontsXml, + @Nullable Map<String, File> updatableFontMap, + long lastModifiedDate, + int configVersion + ) { + return getSystemFontConfigInternal(fontsXml, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, + updatableFontMap, lastModifiedDate, configVersion); + } + + /** * Get the system preinstalled FontConfig. * @hide */ diff --git a/keystore/OWNERS b/keystore/OWNERS index 7ab9d761e236..913f65586cd6 100644 --- a/keystore/OWNERS +++ b/keystore/OWNERS @@ -1,4 +1,4 @@ +# Bug component: 189335 eranm@google.com jbires@google.com -jdanis@google.com swillden@google.com diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 25f5dec9de40..b4d8defd4f90 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -36,6 +36,7 @@ import android.security.keystore.KeyProtection; import android.security.keystore.SecureKeyImportUnavailableException; import android.security.keystore.WrappedKeyEntry; import android.system.keystore2.AuthenticatorSpec; +import android.system.keystore2.Authorization; import android.system.keystore2.Domain; import android.system.keystore2.IKeystoreSecurityLevel; import android.system.keystore2.KeyDescriptor; @@ -966,6 +967,32 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { authenticatorSpecs.add(authSpec); } + if (parts.length > 2) { + @KeyProperties.EncryptionPaddingEnum int padding = + KeyProperties.EncryptionPadding.toKeymaster(parts[2]); + if (padding == KeymasterDefs.KM_PAD_RSA_OAEP + && response.metadata != null + && response.metadata.authorizations != null) { + Authorization[] keyCharacteristics = response.metadata.authorizations; + + for (Authorization authorization : keyCharacteristics) { + // Add default MGF1 digest SHA-1 + // when wrapping key has KM_TAG_RSA_OAEP_MGF_DIGEST tag + if (authorization.keyParameter.tag + == KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST) { + // Default MGF1 digest is SHA-1 + // and KeyMint only supports default MGF1 digest crypto operations + // for importWrappedKey. + args.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, + KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) + )); + break; + } + } + } + } + try { securityLevel.importWrappedKey( wrappedKey, wrappingkey, diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java index 54955c6b7fab..1394bd443f03 100644 --- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java +++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java @@ -325,32 +325,25 @@ public abstract class KeyStore2ParameterUtils { args.add(KeyStore2ParameterUtils.makeBool( KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED)); } else { - if (spec.getUserAuthenticationValidityDurationSeconds() == 0) { - // Every use of this key needs to be authorized by the user. - addSids(args, spec); - args.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType() + addSids(args, spec); + args.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType() + )); + if (spec.getUserAuthenticationValidityDurationSeconds() != 0) { + args.add(KeyStore2ParameterUtils.makeInt( + KeymasterDefs.KM_TAG_AUTH_TIMEOUT, + spec.getUserAuthenticationValidityDurationSeconds() )); - - if (spec.isUserAuthenticationValidWhileOnBody()) { + } + if (spec.isUserAuthenticationValidWhileOnBody()) { + if (spec.getUserAuthenticationValidityDurationSeconds() == 0) { throw new ProviderException( "Key validity extension while device is on-body is not " + "supported for keys requiring fingerprint authentication"); } - } else { - addSids(args, spec); - args.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType() + args.add(KeyStore2ParameterUtils.makeBool( + KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY )); - args.add(KeyStore2ParameterUtils.makeInt( - KeymasterDefs.KM_TAG_AUTH_TIMEOUT, - spec.getUserAuthenticationValidityDurationSeconds() - )); - if (spec.isUserAuthenticationValidWhileOnBody()) { - args.add(KeyStore2ParameterUtils.makeBool( - KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY - )); - } } } } 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 079cfa3ba2ff..81384ca35d52 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -242,9 +242,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return false; } + // Abort if no space to split. + final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes( + task.getTaskProperties(), splitPinRule, + splitPinRule.getDefaultSplitAttributes(), + getActivitiesMinDimensionsPair(primaryContainer.getTopNonFinishingActivity(), + topContainer.getTopNonFinishingActivity())); + if (!SplitPresenter.shouldShowSplit(calculatedSplitAttributes)) { + Log.w(TAG, "No space to split, abort pinning top ActivityStack."); + return false; + } + // Registers a Split final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer, - topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes()); + topContainer, splitPinRule, calculatedSplitAttributes); task.addSplitContainer(splitPinContainer); // Updates the Split @@ -263,7 +274,33 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void unpinTopActivityStack(int taskId){ - // TODO + synchronized (mLock) { + final TaskContainer task = getTaskContainer(taskId); + if (task == null) { + Log.e(TAG, "Cannot find the task to unpin, id: " + taskId); + return; + } + + final SplitPinContainer splitPinContainer = task.getSplitPinContainer(); + if (splitPinContainer == null) { + Log.e(TAG, "No ActivityStack is pinned."); + return; + } + + // Remove the SplitPinContainer from the task. + final TaskFragmentContainer containerToUnpin = + splitPinContainer.getSecondaryContainer(); + task.removeSplitPinContainer(); + + // Resets the isolated navigation and updates the container. + final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); + mPresenter.setTaskFragmentIsolatedNavigation(wct, + containerToUnpin.getTaskFragmentToken(), false /* isolated */); + updateContainer(wct, containerToUnpin); + transactionRecord.apply(false /* shouldApplyIndependently */); + updateCallbackIfNecessary(); + } } @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 16d8cb49e5a6..9a0769a82d99 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -265,8 +265,25 @@ class TaskContainer { } void removeSplitPinContainer() { + if (mSplitPinContainer == null) { + return; + } + + final TaskFragmentContainer primaryContainer = mSplitPinContainer.getPrimaryContainer(); + final TaskFragmentContainer secondaryContainer = mSplitPinContainer.getSecondaryContainer(); mSplitContainers.remove(mSplitPinContainer); mSplitPinContainer = null; + + // Remove the other SplitContainers that contains the unpinned container (unless it + // is the current top-most split-pair), since the state are no longer valid. + final List<SplitContainer> splitsToRemove = new ArrayList<>(); + for (SplitContainer splitContainer : mSplitContainers) { + if (splitContainer.getSecondaryContainer().equals(secondaryContainer) + && !splitContainer.getPrimaryContainer().equals(primaryContainer)) { + splitsToRemove.add(splitContainer); + } + } + removeSplitContainers(splitsToRemove); } @Nullable diff --git a/libs/WindowManager/Shell/res/drawable/user_aspect_ratio_settings_button.xml b/libs/WindowManager/Shell/res/drawable/user_aspect_ratio_settings_button.xml new file mode 100644 index 000000000000..6e4752c9d27d --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/user_aspect_ratio_settings_button.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="48" + android:viewportHeight="48"> + <path + android:fillColor="@color/compat_controls_background" + android:strokeAlpha="0.8" + android:fillAlpha="0.8" + android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/> + <group + android:translateX="12" + android:translateY="12"> + <path + android:fillColor="@color/compat_controls_text" + android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3L10,7L5,7v5h2L7,9zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02z"/> + </group> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/user_aspect_ratio_settings_button_ripple.xml b/libs/WindowManager/Shell/res/drawable/user_aspect_ratio_settings_button_ripple.xml new file mode 100644 index 000000000000..141a1ce60b8e --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/user_aspect_ratio_settings_button_ripple.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/compat_background_ripple"> + <item android:drawable="@drawable/user_aspect_ratio_settings_button"/> +</ripple>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml index dfaeeeb81c07..257fe1544bbb 100644 --- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml +++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml @@ -55,7 +55,7 @@ <include android:id="@+id/size_compat_hint" android:visibility="gone" - android:layout_width="@dimen/size_compat_hint_width" + android:layout_width="@dimen/compat_hint_width" android:layout_height="wrap_content" layout="@layout/compat_mode_hint"/> diff --git a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml new file mode 100644 index 000000000000..433d8546ece0 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.wm.shell.compatui.UserAspectRatioSettingsLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="bottom|end"> + + <include android:id="@+id/user_aspect_ratio_settings_hint" + android:visibility="gone" + android:layout_width="@dimen/compat_hint_width" + android:layout_height="wrap_content" + layout="@layout/compat_mode_hint"/> + + <ImageButton + android:id="@+id/user_aspect_ratio_settings_button" + android:visibility="gone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/compat_button_margin" + android:layout_marginBottom="@dimen/compat_button_margin" + android:src="@drawable/user_aspect_ratio_settings_button_ripple" + android:background="@android:color/transparent" + android:contentDescription="@string/user_aspect_ratio_settings_button_description"/> + +</com.android.wm.shell.compatui.UserAspectRatioSettingsLayout> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index ac73e1d87ba2..597e899d098d 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -260,8 +260,8 @@ + compat_button_margin - compat_hint_corner_radius - compat_hint_point_width / 2). --> <dimen name="compat_hint_padding_end">7dp</dimen> - <!-- The width of the size compat hint. --> - <dimen name="size_compat_hint_width">188dp</dimen> + <!-- The width of the compat hint. --> + <dimen name="compat_hint_width">188dp</dimen> <!-- The width of the camera compat hint. --> <dimen name="camera_compat_hint_width">143dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index b192fdf245e2..8cbc3d016b01 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -173,7 +173,13 @@ <string name="accessibility_bubble_dismissed">Bubble dismissed.</string> <!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] --> - <string name="restart_button_description">Tap to restart this app for a better view.</string> + <string name="restart_button_description">Tap to restart this app for a better view</string> + + <!-- Tooltip text of the button for the user aspect ratio settings. [CHAR LIMIT=NONE] --> + <string name="user_aspect_ratio_settings_button_hint">Change this app\'s aspect ratio in Settings</string> + + <!-- Content description of the button for the user aspect ratio settings. [CHAR LIMIT=NONE] --> + <string name="user_aspect_ratio_settings_button_description">Change aspect ratio</string> <!-- Description of the camera compat button for applying stretched issues treatment in the hint for compatibility control. [CHAR LIMIT=NONE] --> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 66b9ade6aea0..8400ddec0af5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -25,7 +25,6 @@ import static android.view.View.VISIBLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER; -import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.wm.shell.bubbles.Bubbles.DISMISS_BLOCKED; @@ -37,6 +36,7 @@ import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_LONGER_BUBBLE; import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED; import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED; import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES; import android.annotation.BinderThread; @@ -85,6 +85,7 @@ import androidx.annotation.MainThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.statusbar.IStatusBarService; import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.R; @@ -1008,9 +1009,7 @@ public class BubbleController implements ConfigurationChangeListener, } private void onNotificationPanelExpandedChanged(boolean expanded) { - if (DEBUG_BUBBLE_GESTURE) { - Log.d(TAG, "onNotificationPanelExpandedChanged: expanded=" + expanded); - } + ProtoLog.d(WM_SHELL_BUBBLES, "onNotificationPanelExpandedChanged: expanded=%b", expanded); if (mStackView != null && mStackView.isExpanded()) { if (expanded) { mStackView.stopMonitoringSwipeUpGesture(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java index dce6b56261ff..250e010f4d69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java @@ -47,7 +47,6 @@ public class BubbleDebugConfig { static final boolean DEBUG_USER_EDUCATION = false; static final boolean DEBUG_POSITIONER = false; public static final boolean DEBUG_COLLAPSE_ANIMATOR = false; - static final boolean DEBUG_BUBBLE_GESTURE = false; public static boolean DEBUG_EXPANDED_VIEW_DRAGGING = false; private static final boolean FORCE_SHOW_USER_EDUCATION = false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index f58b121ae04f..da5974fe3dc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -21,11 +21,11 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.wm.shell.animation.Interpolators.ALPHA_IN; import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT; -import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -75,6 +75,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; @@ -2024,9 +2025,7 @@ public class BubbleStackView extends FrameLayout * Monitor for swipe up gesture that is used to collapse expanded view */ void startMonitoringSwipeUpGesture() { - if (DEBUG_BUBBLE_GESTURE) { - Log.d(TAG, "startMonitoringSwipeUpGesture"); - } + ProtoLog.d(WM_SHELL_BUBBLES, "startMonitoringSwipeUpGesture"); stopMonitoringSwipeUpGestureInternal(); if (isGestureNavEnabled()) { @@ -2046,9 +2045,7 @@ public class BubbleStackView extends FrameLayout * Stop monitoring for swipe up gesture */ void stopMonitoringSwipeUpGesture() { - if (DEBUG_BUBBLE_GESTURE) { - Log.d(TAG, "stopMonitoringSwipeUpGesture"); - } + ProtoLog.d(WM_SHELL_BUBBLES, "stopMonitoringSwipeUpGesture"); stopMonitoringSwipeUpGestureInternal(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java index 3a3a378e00d3..137568458e3c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java @@ -18,10 +18,10 @@ package com.android.wm.shell.bubbles; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; import android.content.Context; import android.hardware.input.InputManager; -import android.util.Log; import android.view.Choreographer; import android.view.InputChannel; import android.view.InputEventReceiver; @@ -29,6 +29,7 @@ import android.view.InputMonitor; import androidx.annotation.Nullable; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener; /** @@ -58,9 +59,7 @@ class BubblesNavBarGestureTracker { * @param listener listener that is notified of touch events */ void start(MotionEventListener listener) { - if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) { - Log.d(TAG, "start monitoring bubbles swipe up gesture"); - } + ProtoLog.d(WM_SHELL_BUBBLES, "start monitoring bubbles swipe up gesture"); stopInternal(); @@ -76,9 +75,7 @@ class BubblesNavBarGestureTracker { } void stop() { - if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) { - Log.d(TAG, "stop monitoring bubbles swipe up gesture"); - } + ProtoLog.d(WM_SHELL_BUBBLES, "stop monitoring bubbles swipe up gesture"); stopInternal(); } @@ -94,9 +91,7 @@ class BubblesNavBarGestureTracker { } private void onInterceptTouch() { - if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) { - Log.d(TAG, "intercept touch event"); - } + ProtoLog.d(WM_SHELL_BUBBLES, "intercept touch event"); if (mInputMonitor != null) { mInputMonitor.pilferPointers(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java index 844526ca0f35..b7107f09b17f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java @@ -16,19 +16,20 @@ package com.android.wm.shell.bubbles; -import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; import android.content.Context; import android.graphics.PointF; -import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; import androidx.annotation.Nullable; +import com.android.internal.protolog.common.ProtoLog; + /** * Handles {@link MotionEvent}s for bubbles that begin in the nav bar area */ @@ -112,10 +113,8 @@ class BubblesNavBarMotionEventHandler { private boolean isInGestureRegion(MotionEvent ev) { // Only handles touch events beginning in navigation bar system gesture zone if (mPositioner.getNavBarGestureZone().contains((int) ev.getX(), (int) ev.getY())) { - if (DEBUG_BUBBLE_GESTURE) { - Log.d(TAG, "handling touch y=" + ev.getY() - + " navBarGestureZone=" + mPositioner.getNavBarGestureZone()); - } + ProtoLog.d(WM_SHELL_BUBBLES, "handling touch x=%d y=%d navBarGestureZone=%s", + (int) ev.getX(), (int) ev.getY(), mPositioner.getNavBarGestureZone()); return true; } return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt new file mode 100644 index 000000000000..a141ff951684 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt @@ -0,0 +1,79 @@ +/* + * 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.wm.shell.common.pip + +import android.app.AppOpsManager +import android.content.Context +import android.content.pm.PackageManager +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.pip.PipUtils + +class PipAppOpsListener( + private val mContext: Context, + private val mCallback: Callback, + private val mMainExecutor: ShellExecutor +) { + private val mAppOpsManager: AppOpsManager = checkNotNull( + mContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) + private val mAppOpsChangedListener = AppOpsManager.OnOpChangedListener { _, packageName -> + try { + // Dismiss the PiP once the user disables the app ops setting for that package + val topPipActivityInfo = PipUtils.getTopPipActivity(mContext) + val componentName = topPipActivityInfo.first ?: return@OnOpChangedListener + val userId = topPipActivityInfo.second + val appInfo = mContext.packageManager + .getApplicationInfoAsUser(packageName, 0, userId) + if (appInfo.packageName == componentName.packageName && + mAppOpsManager.checkOpNoThrow( + AppOpsManager.OP_PICTURE_IN_PICTURE, appInfo.uid, + packageName + ) != AppOpsManager.MODE_ALLOWED + ) { + mMainExecutor.execute { mCallback.dismissPip() } + } + } catch (e: PackageManager.NameNotFoundException) { + // Unregister the listener if the package can't be found + unregisterAppOpsListener() + } + } + + fun onActivityPinned(packageName: String) { + // Register for changes to the app ops setting for this package while it is in PiP + registerAppOpsListener(packageName) + } + + fun onActivityUnpinned() { + // Unregister for changes to the previously PiP'ed package + unregisterAppOpsListener() + } + + private fun registerAppOpsListener(packageName: String) { + mAppOpsManager.startWatchingMode( + AppOpsManager.OP_PICTURE_IN_PICTURE, packageName, + mAppOpsChangedListener + ) + } + + private fun unregisterAppOpsListener() { + mAppOpsManager.stopWatchingMode(mAppOpsChangedListener) + } + + /** Callback for PipAppOpsListener to request changes to the PIP window. */ + interface Callback { + /** Dismisses the PIP window. */ + fun dismissPip() + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java index ef93a336305f..be1b9b1227de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java @@ -16,6 +16,7 @@ package com.android.wm.shell.common.split; 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.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -60,7 +61,8 @@ public class SplitScreenConstants { public static final int[] CONTROLLED_WINDOWING_MODES = {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}; public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE = - {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW}; + {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW, + WINDOWING_MODE_FREEFORM}; /** Flag applied to a transition change to identify it as a divider bar for animation. */ public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 62b0799618ac..54f89846ac85 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -16,12 +16,17 @@ package com.android.wm.shell.compatui; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.app.TaskInfo.CameraCompatControlState; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.hardware.display.DisplayManager; +import android.provider.Settings; import android.util.ArraySet; import android.util.Log; import android.util.Pair; @@ -41,7 +46,6 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState; import com.android.wm.shell.sysui.KeyguardChangeListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -104,6 +108,13 @@ public class CompatUIController implements OnDisplaysChangedListener, private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>(); /** + * The active user aspect ratio settings button layout if there is one (there can be at most + * one active). + */ + @Nullable + private UserAspectRatioSettingsWindowManager mUserAspectRatioSettingsLayout; + + /** * The active Letterbox Education layout if there is one (there can be at most one active). * * <p>An active layout is a layout that is eligible to be shown for the associated task but @@ -121,38 +132,51 @@ public class CompatUIController implements OnDisplaysChangedListener, /** Avoid creating display context frequently for non-default display. */ private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0); + @NonNull private final Context mContext; + @NonNull private final ShellController mShellController; + @NonNull private final DisplayController mDisplayController; + @NonNull private final DisplayInsetsController mDisplayInsetsController; + @NonNull private final DisplayImeController mImeController; + @NonNull private final SyncTransactionQueue mSyncQueue; + @NonNull private final ShellExecutor mMainExecutor; + @NonNull private final Lazy<Transitions> mTransitionsLazy; + @NonNull private final DockStateReader mDockStateReader; + @NonNull private final CompatUIConfiguration mCompatUIConfiguration; // Only show each hint once automatically in the process life. + @NonNull private final CompatUIHintsState mCompatUIHintsState; + @NonNull private final CompatUIShellCommandHandler mCompatUIShellCommandHandler; - private CompatUICallback mCallback; + @Nullable + private CompatUICallback mCompatUICallback; // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't // be shown. private boolean mKeyguardShowing; - public CompatUIController(Context context, - ShellInit shellInit, - ShellController shellController, - DisplayController displayController, - DisplayInsetsController displayInsetsController, - DisplayImeController imeController, - SyncTransactionQueue syncQueue, - ShellExecutor mainExecutor, - Lazy<Transitions> transitionsLazy, - DockStateReader dockStateReader, - CompatUIConfiguration compatUIConfiguration, - CompatUIShellCommandHandler compatUIShellCommandHandler) { + public CompatUIController(@NonNull Context context, + @NonNull ShellInit shellInit, + @NonNull ShellController shellController, + @NonNull DisplayController displayController, + @NonNull DisplayInsetsController displayInsetsController, + @NonNull DisplayImeController imeController, + @NonNull SyncTransactionQueue syncQueue, + @NonNull ShellExecutor mainExecutor, + @NonNull Lazy<Transitions> transitionsLazy, + @NonNull DockStateReader dockStateReader, + @NonNull CompatUIConfiguration compatUIConfiguration, + @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler) { mContext = context; mShellController = shellController; mDisplayController = displayController; @@ -175,9 +199,9 @@ public class CompatUIController implements OnDisplaysChangedListener, mCompatUIShellCommandHandler.onInit(); } - /** Sets the callback for UI interactions. */ - public void setCompatUICallback(CompatUICallback callback) { - mCallback = callback; + /** Sets the callback for Compat UI interactions. */ + public void setCompatUICallback(@NonNull CompatUICallback compatUiCallback) { + mCompatUICallback = compatUiCallback; } /** @@ -187,7 +211,7 @@ public class CompatUIController implements OnDisplaysChangedListener, * @param taskInfo {@link TaskInfo} task the activity is in. * @param taskListener listener to handle the Task Surface placement. */ - public void onCompatInfoChanged(TaskInfo taskInfo, + public void onCompatInfoChanged(@NonNull TaskInfo taskInfo, @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (taskInfo != null && !taskInfo.topActivityInSizeCompat) { mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId); @@ -203,6 +227,16 @@ public class CompatUIController implements OnDisplaysChangedListener, createOrUpdateRestartDialogLayout(taskInfo, taskListener); if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) { createOrUpdateReachabilityEduLayout(taskInfo, taskListener); + // The user aspect ratio button should not be handled when a new TaskInfo is + // sent because of a double tap or when in multi-window mode. + if (taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + mUserAspectRatioSettingsLayout.release(); + mUserAspectRatioSettingsLayout = null; + return; + } + if (!taskInfo.isFromLetterboxDoubleTap) { + createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener); + } } } @@ -280,8 +314,8 @@ public class CompatUIController implements OnDisplaysChangedListener, return mDisplaysWithIme.contains(displayId); } - private void createOrUpdateCompatLayout(TaskInfo taskInfo, - ShellTaskOrganizer.TaskListener taskListener) { + private void createOrUpdateCompatLayout(@NonNull TaskInfo taskInfo, + @Nullable ShellTaskOrganizer.TaskListener taskListener) { CompatUIWindowManager layout = mActiveCompatLayouts.get(taskInfo.taskId); if (layout != null) { if (layout.needsToBeRecreated(taskInfo, taskListener)) { @@ -314,7 +348,7 @@ public class CompatUIController implements OnDisplaysChangedListener, CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { return new CompatUIWindowManager(context, - taskInfo, mSyncQueue, mCallback, taskListener, + taskInfo, mSyncQueue, mCompatUICallback, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState, mCompatUIConfiguration, this::onRestartButtonClicked); } @@ -328,12 +362,12 @@ public class CompatUIController implements OnDisplaysChangedListener, mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId); onCompatInfoChanged(taskInfoState.first, taskInfoState.second); } else { - mCallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId); + mCompatUICallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId); } } - private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo, - ShellTaskOrganizer.TaskListener taskListener) { + private void createOrUpdateLetterboxEduLayout(@NonNull TaskInfo taskInfo, + @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (mActiveLetterboxEduLayout != null) { if (mActiveLetterboxEduLayout.needsToBeRecreated(taskInfo, taskListener)) { mActiveLetterboxEduLayout.release(); @@ -342,6 +376,7 @@ public class CompatUIController implements OnDisplaysChangedListener, if (!mActiveLetterboxEduLayout.updateCompatInfo(taskInfo, taskListener, showOnDisplay(mActiveLetterboxEduLayout.getDisplayId()))) { // The layout is no longer eligible to be shown, clear active layout. + mActiveLetterboxEduLayout.release(); mActiveLetterboxEduLayout = null; } return; @@ -371,19 +406,13 @@ public class CompatUIController implements OnDisplaysChangedListener, ShellTaskOrganizer.TaskListener taskListener) { return new LetterboxEduWindowManager(context, taskInfo, mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), - mTransitionsLazy.get(), this::onLetterboxEduDismissed, mDockStateReader, - mCompatUIConfiguration); + mTransitionsLazy.get(), + stateInfo -> createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second), + mDockStateReader, mCompatUIConfiguration); } - private void onLetterboxEduDismissed( - Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) { - mActiveLetterboxEduLayout = null; - // We need to update the UI - createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second); - } - - private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo, - ShellTaskOrganizer.TaskListener taskListener) { + private void createOrUpdateRestartDialogLayout(@NonNull TaskInfo taskInfo, + @Nullable ShellTaskOrganizer.TaskListener taskListener) { RestartDialogWindowManager layout = mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId); if (layout != null) { @@ -428,7 +457,7 @@ public class CompatUIController implements OnDisplaysChangedListener, private void onRestartDialogCallback( Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) { mTaskIdToRestartDialogWindowManagerMap.remove(stateInfo.first.taskId); - mCallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId); + mCompatUICallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId); } private void onRestartDialogDismissCallback( @@ -437,8 +466,8 @@ public class CompatUIController implements OnDisplaysChangedListener, onCompatInfoChanged(stateInfo.first, stateInfo.second); } - private void createOrUpdateReachabilityEduLayout(TaskInfo taskInfo, - ShellTaskOrganizer.TaskListener taskListener) { + private void createOrUpdateReachabilityEduLayout(@NonNull TaskInfo taskInfo, + @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (mActiveReachabilityEduLayout != null) { if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener)) { mActiveReachabilityEduLayout.release(); @@ -448,6 +477,7 @@ public class CompatUIController implements OnDisplaysChangedListener, if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener, showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) { // The layout is no longer eligible to be shown, remove from active layouts. + mActiveReachabilityEduLayout.release(); mActiveReachabilityEduLayout = null; } return; @@ -478,14 +508,67 @@ public class CompatUIController implements OnDisplaysChangedListener, ShellTaskOrganizer.TaskListener taskListener) { return new ReachabilityEduWindowManager(context, taskInfo, mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), - mCompatUIConfiguration, mMainExecutor); + mCompatUIConfiguration, mMainExecutor, this::onInitialReachabilityEduDismissed); } + private void onInitialReachabilityEduDismissed(@NonNull TaskInfo taskInfo, + @NonNull ShellTaskOrganizer.TaskListener taskListener) { + // We need to update the UI otherwise it will not be shown until the user relaunches the app + createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener); + } + + private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo, + @Nullable ShellTaskOrganizer.TaskListener taskListener) { + if (mUserAspectRatioSettingsLayout != null) { + if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)) { + mUserAspectRatioSettingsLayout.release(); + mUserAspectRatioSettingsLayout = null; + } else { + // UI already exists, update the UI layout. + if (!mUserAspectRatioSettingsLayout.updateCompatInfo(taskInfo, taskListener, + showOnDisplay(mUserAspectRatioSettingsLayout.getDisplayId()))) { + mUserAspectRatioSettingsLayout.release(); + mUserAspectRatioSettingsLayout = null; + } + return; + } + } + + // Create a new UI layout. + final Context context = getOrCreateDisplayContext(taskInfo.displayId); + if (context == null) { + return; + } + final UserAspectRatioSettingsWindowManager newLayout = + createUserAspectRatioSettingsWindowManager(context, taskInfo, taskListener); + if (newLayout.createLayout(showOnDisplay(taskInfo.displayId))) { + // The new layout is eligible to be shown, add it the active layouts. + mUserAspectRatioSettingsLayout = newLayout; + } + } + + @VisibleForTesting + @NonNull + UserAspectRatioSettingsWindowManager createUserAspectRatioSettingsWindowManager( + @NonNull Context context, @NonNull TaskInfo taskInfo, + @Nullable ShellTaskOrganizer.TaskListener taskListener) { + return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue, + taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), + mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor); + } + + private void launchUserAspectRatioSettings( + @NonNull TaskInfo taskInfo, @NonNull ShellTaskOrganizer.TaskListener taskListener) { + final Intent intent = new Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); + } private void removeLayouts(int taskId) { - final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId); - if (layout != null) { - layout.release(); + final CompatUIWindowManager compatLayout = mActiveCompatLayouts.get(taskId); + if (compatLayout != null) { + compatLayout.release(); mActiveCompatLayouts.remove(taskId); } @@ -506,6 +589,12 @@ public class CompatUIController implements OnDisplaysChangedListener, mActiveReachabilityEduLayout.release(); mActiveReachabilityEduLayout = null; } + + if (mUserAspectRatioSettingsLayout != null + && mUserAspectRatioSettingsLayout.getTaskId() == taskId) { + mUserAspectRatioSettingsLayout.release(); + mUserAspectRatioSettingsLayout = null; + } } private Context getOrCreateDisplayContext(int displayId) { @@ -561,6 +650,10 @@ public class CompatUIController implements OnDisplaysChangedListener, if (mActiveReachabilityEduLayout != null && condition.test(mActiveReachabilityEduLayout)) { callback.accept(mActiveReachabilityEduLayout); } + if (mUserAspectRatioSettingsLayout != null && condition.test( + mUserAspectRatioSettingsLayout)) { + callback.accept(mUserAspectRatioSettingsLayout); + } } /** An implementation of {@link OnInsetsChangedListener} for a given display id. */ @@ -595,4 +688,14 @@ public class CompatUIController implements OnDisplaysChangedListener, insetsChanged(insetsState); } } + + /** + * A class holding the state of the compat UI hints, which is shared between all compat UI + * window managers. + */ + static class CompatUIHintsState { + boolean mHasShownSizeCompatHint; + boolean mHasShownCameraCompatHint; + boolean mHasShownUserAspectRatioSettingsButtonHint; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java index 065806df3dc8..ce3c5093fdd4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -38,6 +38,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.CompatUIController.CompatUICallback; +import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; import java.util.function.Consumer; @@ -235,15 +236,4 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED; } - - /** - * A class holding the state of the compat UI hints, which is shared between all compat UI - * window managers. - */ - static class CompatUIHintsState { - @VisibleForTesting - boolean mHasShownSizeCompatHint; - @VisibleForTesting - boolean mHasShownCameraCompatHint; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java index 95bb1fe1c986..9de3f9dec34e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java @@ -36,6 +36,8 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; +import java.util.function.BiConsumer; + /** * Window manager for the reachability education */ @@ -73,6 +75,8 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { // we need to animate them. private boolean mHasLetterboxSizeChanged; + private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnDismissCallback; + @Nullable @VisibleForTesting ReachabilityEduLayout mLayout; @@ -80,7 +84,8 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { ReachabilityEduWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, - CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor) { + CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor, + BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onDismissCallback) { super(context, taskInfo, syncQueue, taskListener, displayLayout); mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled; mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition; @@ -89,6 +94,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { mTopActivityLetterboxHeight = taskInfo.topActivityLetterboxHeight; mCompatUIConfiguration = compatUIConfiguration; mMainExecutor = mainExecutor; + mOnDismissCallback = onDismissCallback; } @Override @@ -217,13 +223,17 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { return; } final TaskInfo lastTaskInfo = getLastTaskInfo(); + final boolean hasSeenHorizontalReachabilityEdu = + mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(lastTaskInfo); + final boolean hasSeenVerticalReachabilityEdu = + mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(lastTaskInfo); final boolean eligibleForDisplayHorizontalEducation = mForceUpdate - || !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(lastTaskInfo) + || !hasSeenHorizontalReachabilityEdu || (mHasUserDoubleTapped && (mLetterboxHorizontalPosition == REACHABILITY_LEFT_OR_UP_POSITION || mLetterboxHorizontalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION)); final boolean eligibleForDisplayVerticalEducation = mForceUpdate - || !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(lastTaskInfo) + || !hasSeenVerticalReachabilityEdu || (mHasUserDoubleTapped && (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION || mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION)); @@ -239,6 +249,14 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { if (!mHasLetterboxSizeChanged) { updateHideTime(); mMainExecutor.executeDelayed(this::hideReachability, DISAPPEAR_DELAY_MS); + // If reachability education has been seen for the first time, trigger callback to + // display aspect ratio settings button once reachability education disappears + if (hasShownHorizontalReachabilityEduFirstTime(hasSeenHorizontalReachabilityEdu) + || hasShownVerticalReachabilityEduFirstTime( + hasSeenVerticalReachabilityEdu)) { + mMainExecutor.executeDelayed(this::triggerOnDismissCallback, + DISAPPEAR_DELAY_MS); + } } mHasUserDoubleTapped = false; } else { @@ -246,6 +264,38 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { } } + /** + * Compares the value of + * {@link CompatUIConfiguration#hasSeenHorizontalReachabilityEducation} before and after the + * layout is shown. Horizontal reachability education is considered seen for the first time if + * prior to viewing the layout, + * {@link CompatUIConfiguration#hasSeenHorizontalReachabilityEducation} is {@code false} + * but becomes {@code true} once the current layout is shown. + */ + private boolean hasShownHorizontalReachabilityEduFirstTime( + boolean previouslyShownHorizontalReachabilityEducation) { + return !previouslyShownHorizontalReachabilityEducation + && mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(getLastTaskInfo()); + } + + /** + * Compares the value of + * {@link CompatUIConfiguration#hasSeenVerticalReachabilityEducation} before and after the + * layout is shown. Horizontal reachability education is considered seen for the first time if + * prior to viewing the layout, + * {@link CompatUIConfiguration#hasSeenVerticalReachabilityEducation} is {@code false} + * but becomes {@code true} once the current layout is shown. + */ + private boolean hasShownVerticalReachabilityEduFirstTime( + boolean previouslyShownVerticalReachabilityEducation) { + return !previouslyShownVerticalReachabilityEducation + && mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(getLastTaskInfo()); + } + + private void triggerOnDismissCallback() { + mOnDismissCallback.accept(getLastTaskInfo(), getTaskListener()); + } + private void hideReachability() { if (mLayout == null || !shouldHideEducation()) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java new file mode 100644 index 000000000000..5eeb3b650074 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java @@ -0,0 +1,136 @@ +/* + * 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.wm.shell.compatui; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.annotation.IdRes; +import android.annotation.NonNull; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.wm.shell.R; + +/** + * Layout for the user aspect ratio button which opens the app list page in settings + * and allows users to change apps aspect ratio. + */ +public class UserAspectRatioSettingsLayout extends LinearLayout { + + private static final float ALPHA_FULL_TRANSPARENT = 0f; + + private static final float ALPHA_FULL_OPAQUE = 1f; + + private static final long VISIBILITY_ANIMATION_DURATION_MS = 50; + + private static final String ALPHA_PROPERTY_NAME = "alpha"; + + private UserAspectRatioSettingsWindowManager mWindowManager; + + public UserAspectRatioSettingsLayout(Context context) { + this(context, null); + } + + public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + void inject(@NonNull UserAspectRatioSettingsWindowManager windowManager) { + mWindowManager = windowManager; + } + + void setUserAspectRatioSettingsHintVisibility(boolean show) { + setViewVisibility(R.id.user_aspect_ratio_settings_hint, show); + } + + void setUserAspectRatioButtonVisibility(boolean show) { + setViewVisibility(R.id.user_aspect_ratio_settings_button, show); + // Hint should never be visible without button. + if (!show) { + setUserAspectRatioSettingsHintVisibility(/* show= */ false); + } + } + + private void setViewVisibility(@IdRes int resId, boolean show) { + final View view = findViewById(resId); + int visibility = show ? View.VISIBLE : View.GONE; + if (view.getVisibility() == visibility) { + return; + } + if (show) { + showItem(view); + } else { + view.setVisibility(visibility); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + // Need to relayout after changes like hiding / showing a hint since they affect size. + // Doing this directly in setUserAspectRatioButtonVisibility can result in flaky animation. + mWindowManager.relayout(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final ImageButton userAspectRatioButton = + findViewById(R.id.user_aspect_ratio_settings_button); + userAspectRatioButton.setOnClickListener( + view -> mWindowManager.onUserAspectRatioSettingsButtonClicked()); + userAspectRatioButton.setOnLongClickListener(view -> { + mWindowManager.onUserAspectRatioSettingsButtonLongClicked(); + return true; + }); + + final LinearLayout sizeCompatHint = findViewById(R.id.user_aspect_ratio_settings_hint); + ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text)) + .setText(R.string.user_aspect_ratio_settings_button_hint); + sizeCompatHint.setOnClickListener( + view -> setUserAspectRatioSettingsHintVisibility(/* show= */ false)); + } + + private void showItem(@NonNull View view) { + view.setVisibility(View.VISIBLE); + final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME, + ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE); + fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS); + fadeIn.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setVisibility(View.VISIBLE); + } + }); + fadeIn.start(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java new file mode 100644 index 000000000000..bd53dc7390c8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java @@ -0,0 +1,211 @@ +/* + * 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.wm.shell.compatui; + +import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.TaskInfo; +import android.content.Context; +import android.graphics.Rect; +import android.os.SystemClock; +import android.view.LayoutInflater; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; + +import java.util.function.BiConsumer; + +/** + * Window manager for the user aspect ratio settings button which allows users to go to + * app settings and change apps aspect ratio. + */ +class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract { + + private static final long SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 500L; + + private static final long HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 4000L; + + private long mNextButtonHideTimeMs = -1L; + + private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnButtonClicked; + + private final ShellExecutor mShellExecutor; + + @VisibleForTesting + @NonNull + final CompatUIHintsState mCompatUIHintsState; + + @Nullable + private UserAspectRatioSettingsLayout mLayout; + + // Remember the last reported states in case visibility changes due to keyguard or IME updates. + @VisibleForTesting + boolean mHasUserAspectRatioSettingsButton; + + UserAspectRatioSettingsWindowManager(@NonNull Context context, @NonNull TaskInfo taskInfo, + @NonNull SyncTransactionQueue syncQueue, + @Nullable ShellTaskOrganizer.TaskListener taskListener, + @NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState, + @NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked, + @NonNull ShellExecutor shellExecutor) { + super(context, taskInfo, syncQueue, taskListener, displayLayout); + mShellExecutor = shellExecutor; + mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo); + mCompatUIHintsState = compatUIHintsState; + mOnButtonClicked = onButtonClicked; + } + + @Override + protected int getZOrder() { + return TASK_CHILD_LAYER_COMPAT_UI + 1; + } + + @Override + protected @Nullable View getLayout() { + return mLayout; + } + + @Override + protected void removeLayout() { + mLayout = null; + } + + @Override + protected boolean eligibleToShowLayout() { + return mHasUserAspectRatioSettingsButton; + } + + @Override + protected View createLayout() { + mLayout = inflateLayout(); + mLayout.inject(this); + + updateVisibilityOfViews(); + + return mLayout; + } + + @VisibleForTesting + UserAspectRatioSettingsLayout inflateLayout() { + return (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate( + R.layout.user_aspect_ratio_settings_layout, null); + } + + @Override + public boolean updateCompatInfo(@NonNull TaskInfo taskInfo, + @NonNull ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { + final boolean prevHasUserAspectRatioSettingsButton = mHasUserAspectRatioSettingsButton; + mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo); + + if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) { + return false; + } + + if (prevHasUserAspectRatioSettingsButton != mHasUserAspectRatioSettingsButton) { + updateVisibilityOfViews(); + } + return true; + } + + /** Called when the user aspect ratio settings button is clicked. */ + void onUserAspectRatioSettingsButtonClicked() { + mOnButtonClicked.accept(getLastTaskInfo(), getTaskListener()); + } + + /** Called when the user aspect ratio settings button is long clicked. */ + void onUserAspectRatioSettingsButtonLongClicked() { + if (mLayout == null) { + return; + } + mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true); + mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS); + mShellExecutor.executeDelayed(this::hideUserAspectRatioButton, + HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS); + } + + @Override + @VisibleForTesting + public void updateSurfacePosition() { + if (mLayout == null) { + return; + } + // Position of the button in the container coordinate. + final Rect taskBounds = getTaskBounds(); + final Rect taskStableBounds = getTaskStableBounds(); + final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? taskStableBounds.left - taskBounds.left + : taskStableBounds.right - taskBounds.left - mLayout.getMeasuredWidth(); + final int positionY = taskStableBounds.bottom - taskBounds.top + - mLayout.getMeasuredHeight(); + updateSurfacePosition(positionX, positionY); + } + + @VisibleForTesting + void updateVisibilityOfViews() { + if (mHasUserAspectRatioSettingsButton) { + mShellExecutor.executeDelayed(this::showUserAspectRatioButton, + SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS); + mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS); + mShellExecutor.executeDelayed(this::hideUserAspectRatioButton, + HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS); + } else { + mShellExecutor.removeCallbacks(this::showUserAspectRatioButton); + mShellExecutor.execute(this::hideUserAspectRatioButton); + } + } + + private void showUserAspectRatioButton() { + if (mLayout == null) { + return; + } + mLayout.setUserAspectRatioButtonVisibility(true); + // Only show by default for the first time. + if (!mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint) { + mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true); + mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = true; + } + } + + private void hideUserAspectRatioButton() { + if (mLayout == null || !isHideDelayReached(mNextButtonHideTimeMs)) { + return; + } + mLayout.setUserAspectRatioButtonVisibility(false); + } + + private boolean isHideDelayReached(long nextHideTime) { + return SystemClock.uptimeMillis() >= nextHideTime; + } + + private long updateHideTime(long hideDelay) { + return SystemClock.uptimeMillis() + hideDelay; + } + + private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) { + return taskInfo.topActivityEligibleForUserAspectRatioButton + && (taskInfo.topActivityBoundsLetterboxed + || taskInfo.isUserFullscreenOverrideEnabled); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 430fa95694c3..c06b22cdb8a4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -45,6 +45,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DockStateReader; +import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -74,7 +75,6 @@ import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; -import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; @@ -123,6 +123,12 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides + static FloatingContentCoordinator provideFloatingContentCoordinator() { + return new FloatingContentCoordinator(); + } + + @WMSingleton + @Provides static DisplayController provideDisplayController(Context context, IWindowManager wmService, ShellInit shellInit, @@ -807,7 +813,6 @@ public abstract class WMShellBaseModule { ShellTaskOrganizer shellTaskOrganizer, Optional<BubbleController> bubblesOptional, Optional<SplitScreenController> splitScreenOptional, - Optional<Pip> pipOptional, FullscreenTaskListener fullscreenTaskListener, Optional<UnfoldAnimationController> unfoldAnimationController, Optional<UnfoldTransitionHandler> unfoldTransitionHandler, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java index 54be90197e47..9bf973f523bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java @@ -31,13 +31,13 @@ import com.android.wm.shell.common.TabletopModeController; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java index f29b3a35128d..e8fae2490bc5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java @@ -21,7 +21,6 @@ import android.content.pm.PackageManager; import android.os.Handler; import com.android.internal.logging.UiEventLogger; -import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.pip.PipMediaController; @@ -37,12 +36,6 @@ import dagger.Provides; */ @Module public abstract class Pip1SharedModule { - @WMSingleton - @Provides - static FloatingContentCoordinator provideFloatingContentCoordinator() { - return new FloatingContentCoordinator(); - } - // Needs handler for registering broadcast receivers @WMSingleton @Provides diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java index 52c6d2008a9d..80ffbb0968f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java @@ -29,12 +29,12 @@ import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.pip.LegacySizeSpecSource; +import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipDisplayLayoutState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; 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 1d46e755ec9d..633f627e8e71 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 @@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode import android.R import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM @@ -301,6 +302,24 @@ class DesktopTasksController( } } + /** Move a desktop app to split screen. */ + fun moveToSplit(task: RunningTaskInfo) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: moveToSplit taskId=%d", + task.taskId + ) + val wct = WindowContainerTransaction() + wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW) + wct.setBounds(task.token, null) + wct.setDensityDpi(task.token, getDefaultDensityDpi()) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + /** * The second part of the animated move to desktop transition, called after * {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java deleted file mode 100644 index 48a3fc2460a2..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.pip; - -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; - -import android.app.AppOpsManager; -import android.app.AppOpsManager.OnOpChangedListener; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.util.Pair; - -import com.android.wm.shell.common.ShellExecutor; - -public class PipAppOpsListener { - private static final String TAG = PipAppOpsListener.class.getSimpleName(); - - private Context mContext; - private ShellExecutor mMainExecutor; - private AppOpsManager mAppOpsManager; - private Callback mCallback; - - private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() { - @Override - public void onOpChanged(String op, String packageName) { - try { - // Dismiss the PiP once the user disables the app ops setting for that package - final Pair<ComponentName, Integer> topPipActivityInfo = - PipUtils.getTopPipActivity(mContext); - if (topPipActivityInfo.first != null) { - final ApplicationInfo appInfo = mContext.getPackageManager() - .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second); - if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) && - mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, - packageName) != MODE_ALLOWED) { - mMainExecutor.execute(() -> mCallback.dismissPip()); - } - } - } catch (NameNotFoundException e) { - // Unregister the listener if the package can't be found - unregisterAppOpsListener(); - } - } - }; - - public PipAppOpsListener(Context context, Callback callback, ShellExecutor mainExecutor) { - mContext = context; - mMainExecutor = mainExecutor; - mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mCallback = callback; - } - - public void onActivityPinned(String packageName) { - // Register for changes to the app ops setting for this package while it is in PiP - registerAppOpsListener(packageName); - } - - public void onActivityUnpinned() { - // Unregister for changes to the previously PiP'ed package - unregisterAppOpsListener(); - } - - private void registerAppOpsListener(String packageName) { - mAppOpsManager.startWatchingMode(OP_PICTURE_IN_PICTURE, packageName, - mAppOpsChangedListener); - } - - private void unregisterAppOpsListener() { - mAppOpsManager.stopWatchingMode(mAppOpsChangedListener); - } - - /** Callback for PipAppOpsListener to request changes to the PIP window. */ - public interface Callback { - /** Dismisses the PIP window. */ - void dismissPip(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 0d55018ba580..19c60c2a9117 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -63,7 +63,6 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; -import android.os.SystemProperties; import android.view.Choreographer; import android.view.Display; import android.view.Surface; @@ -1735,17 +1734,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // animation. // TODO(b/272819817): cleanup the null-check and extra logging. final boolean hasTopActivityInfo = mTaskInfo.topActivityInfo != null; - if (!hasTopActivityInfo) { - ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, - "%s: TaskInfo.topActivityInfo is null", TAG); - } - if (SystemProperties.getBoolean( - "persist.wm.debug.enable_pip_app_icon_overlay", true) - && hasTopActivityInfo) { + if (hasTopActivityInfo) { animator.setAppIconContentOverlay( mContext, currentBounds, mTaskInfo.topActivityInfo, mPipBoundsState.getLauncherState().getAppIconSizePx()); } else { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "%s: TaskInfo.topActivityInfo is null", TAG); animator.setColorContentOverlay(mContext); } } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 2563d984b793..db7e2c0c529f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -49,7 +49,6 @@ import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; -import android.os.SystemProperties; import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManager; @@ -902,17 +901,13 @@ public class PipTransition extends PipTransitionController { // animation. // TODO(b/272819817): cleanup the null-check and extra logging. final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null; - if (!hasTopActivityInfo) { - ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, - "%s: TaskInfo.topActivityInfo is null", TAG); - } - if (SystemProperties.getBoolean( - "persist.wm.debug.enable_pip_app_icon_overlay", true) - && hasTopActivityInfo) { + if (hasTopActivityInfo) { animator.setAppIconContentOverlay( mContext, currentBounds, taskInfo.topActivityInfo, mPipBoundsState.getLauncherState().getAppIconSizePx()); } else { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "%s: TaskInfo.topActivityInfo is null", TAG); animator.setColorContentOverlay(mContext); } } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java index f9332e4bdb2e..2d3403599484 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java @@ -40,6 +40,7 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac "persist.wm.debug.enable_pip_keep_clear_algorithm_gravity", false); protected int mKeepClearAreasPadding; + private int mImeOffset; public PhonePipKeepClearAlgorithm(Context context) { reloadResources(context); @@ -48,6 +49,7 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac private void reloadResources(Context context) { final Resources res = context.getResources(); mKeepClearAreasPadding = res.getDimensionPixelSize(R.dimen.pip_keep_clear_areas_padding); + mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); } /** @@ -61,7 +63,7 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac Rect insets = new Rect(); pipBoundsAlgorithm.getInsetBounds(insets); if (pipBoundsState.isImeShowing()) { - insets.bottom -= pipBoundsState.getImeHeight(); + insets.bottom -= (pipBoundsState.getImeHeight() + mImeOffset); } // if PiP is stashed we only adjust the vertical position if it's outside of insets and // ignore all keep clear areas, since it's already on the side diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index f396f3fedf64..f25110ac3257 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -75,6 +75,7 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener; import com.android.wm.shell.common.TabletopModeController; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.IPip; @@ -82,7 +83,6 @@ import com.android.wm.shell.pip.IPipAnimationListener; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; @@ -123,14 +123,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb private static final long PIP_KEEP_CLEAR_AREAS_DELAY = SystemProperties.getLong("persist.wm.debug.pip_keep_clear_areas_delay", 200); - private boolean mEnablePipKeepClearAlgorithm = - SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", true); - - @VisibleForTesting - void setEnablePipKeepClearAlgorithm(boolean value) { - mEnablePipKeepClearAlgorithm = value; - } - private Context mContext; protected ShellExecutor mMainExecutor; private DisplayController mDisplayController; @@ -166,10 +158,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb // early bail out if the change was caused by keyguard showing up return; } - if (!mEnablePipKeepClearAlgorithm) { - // early bail out if the keep clear areas feature is disabled - return; - } if (mPipBoundsState.isStashed()) { // don't move when stashed return; @@ -187,10 +175,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb } private void updatePipPositionForKeepClearAreas() { - if (!mEnablePipKeepClearAlgorithm) { - // early bail out if the keep clear areas feature is disabled - return; - } if (mIsKeyguardShowingOrAnimating) { // early bail out if the change was caused by keyguard showing up return; @@ -343,19 +327,17 @@ public class PipController implements PipTransitionController.PipTransitionCallb public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted, Set<Rect> unrestricted) { if (mPipDisplayLayoutState.getDisplayId() == displayId) { - if (mEnablePipKeepClearAlgorithm) { - mPipBoundsState.setKeepClearAreas(restricted, unrestricted); - - mMainExecutor.removeCallbacks( - mMovePipInResponseToKeepClearAreasChangeCallback); - mMainExecutor.executeDelayed( - mMovePipInResponseToKeepClearAreasChangeCallback, - PIP_KEEP_CLEAR_AREAS_DELAY); - - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "onKeepClearAreasChanged: restricted=%s, unrestricted=%s", - restricted, unrestricted); - } + mPipBoundsState.setKeepClearAreas(restricted, unrestricted); + + mMainExecutor.removeCallbacks( + mMovePipInResponseToKeepClearAreasChangeCallback); + mMainExecutor.executeDelayed( + mMovePipInResponseToKeepClearAreasChangeCallback, + PIP_KEEP_CLEAR_AREAS_DELAY); + + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onKeepClearAreasChanged: restricted=%s, unrestricted=%s", + restricted, unrestricted); } } }; @@ -660,25 +642,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb // there's a keyguard present return; } - int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom; onDisplayChangedUncheck(mDisplayController .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()), false /* saveRestoreSnapFraction */); - int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom; - if (!mEnablePipKeepClearAlgorithm) { - // offset PiP to adjust for bottom inset change - int pipTop = mPipBoundsState.getBounds().top; - int diff = newMaxMovementBound - oldMaxMovementBound; - if (diff < 0 && pipTop > newMaxMovementBound) { - // bottom inset has increased, move PiP up if it is too low - mPipMotionHelper.animateToOffset(mPipBoundsState.getBounds(), - newMaxMovementBound - pipTop); - } - if (diff > 0 && oldMaxMovementBound == pipTop) { - // bottom inset has decreased, move PiP down if it was by the edge - mPipMotionHelper.animateToOffset(mPipBoundsState.getBounds(), diff); - } - } } }); @@ -947,14 +913,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb * Sets both shelf visibility and its height. */ private void setShelfHeight(boolean visible, int height) { - if (mEnablePipKeepClearAlgorithm) { - // turn this into Launcher keep clear area registration instead - setLauncherKeepClearAreaHeight(visible, height); - return; - } - if (!mIsKeyguardShowingOrAnimating) { - setShelfHeightLocked(visible, height); - } + // turn this into Launcher keep clear area registration instead + setLauncherKeepClearAreaHeight(visible, height); } private void setLauncherKeepClearAreaHeight(boolean visible, int height) { @@ -1015,16 +975,10 @@ public class PipController implements PipTransitionController.PipTransitionCallb private Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams, int launcherRotation, Rect hotseatKeepClearArea) { - - if (mEnablePipKeepClearAlgorithm) { - // preemptively add the keep clear area for Hotseat, so that it is taken into account - // when calculating the entry destination bounds of PiP window - mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, - hotseatKeepClearArea); - } else { - int shelfHeight = hotseatKeepClearArea.height(); - setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); - } + // preemptively add the keep clear area for Hotseat, so that it is taken into account + // when calculating the entry destination bounds of PiP window + mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, + hotseatKeepClearArea); onDisplayRotationChangedNotInPip(mContext, launcherRotation); final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, pictureInPictureParams); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 43d3f36f1fe5..b251f6f55794 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -41,7 +41,7 @@ import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; -import com.android.wm.shell.pip.PipAppOpsListener; +import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 3e95498a832b..9f7dee74d06e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -34,7 +34,6 @@ import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; -import android.os.SystemProperties; import android.provider.DeviceConfig; import android.util.Size; import android.view.DisplayCutout; @@ -73,14 +72,6 @@ public class PipTouchHandler { private static final String TAG = "PipTouchHandler"; private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f; - private boolean mEnablePipKeepClearAlgorithm = - SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", true); - - @VisibleForTesting - void setEnablePipKeepClearAlgorithm(boolean value) { - mEnablePipKeepClearAlgorithm = value; - } - // Allow PIP to resize to a slightly bigger state upon touch private boolean mEnableResize; private final Context mContext; @@ -430,48 +421,6 @@ public class PipTouchHandler { mIsImeShowing ? mImeOffset : 0, !mIsImeShowing && mIsShelfShowing ? mShelfHeight : 0); - // If this is from an IME or shelf adjustment, then we should move the PiP so that it is not - // occluded by the IME or shelf. - if (fromImeAdjustment || fromShelfAdjustment) { - if (mTouchState.isUserInteracting() && mTouchState.isDragging()) { - // Defer the update of the current movement bounds until after the user finishes - // touching the screen - } else if (mEnablePipKeepClearAlgorithm) { - // Ignore moving PiP if keep clear algorithm is enabled, since IME and shelf height - // now are accounted for in the keep clear algorithm calculations - } else { - final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu(); - final Rect toMovementBounds = new Rect(); - mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds, - toMovementBounds, mIsImeShowing ? mImeHeight : 0); - final int prevBottom = mPipBoundsState.getMovementBounds().bottom - - mMovementBoundsExtraOffsets; - // This is to handle landscape fullscreen IMEs, don't apply the extra offset in this - // case - final int toBottom = toMovementBounds.bottom < toMovementBounds.top - ? toMovementBounds.bottom - : toMovementBounds.bottom - extraOffset; - - if (isExpanded) { - curBounds.set(mPipBoundsState.getExpandedBounds()); - mPipBoundsAlgorithm.getSnapAlgorithm().applySnapFraction(curBounds, - toMovementBounds, mSavedSnapFraction); - } - - if (prevBottom < toBottom) { - // The movement bounds are expanding - if (curBounds.top > prevBottom - mBottomOffsetBufferPx) { - mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top); - } - } else if (prevBottom > toBottom) { - // The movement bounds are shrinking - if (curBounds.top > toBottom - mBottomOffsetBufferPx) { - mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top); - } - } - } - } - // Update the movement bounds after doing the calculations based on the old movement bounds // above mPipBoundsState.setNormalMovementBounds(normalMovementBounds); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 2482acf60267..e3544c63dd6e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -47,10 +47,10 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipDisplayLayoutState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index b2a189b45d6c..ee55211a73a9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -62,13 +62,16 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis private SurfaceControl mLeash; private TvPipMenuView mPipMenuView; private TvPipBackgroundView mPipBackgroundView; - private boolean mMenuIsFocused; @TvPipMenuMode private int mCurrentMenuMode = MODE_NO_MENU; @TvPipMenuMode private int mPrevMenuMode = MODE_NO_MENU; + /** When the window gains focus, enter this menu mode */ + @TvPipMenuMode + private int mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU; + @IntDef(prefix = { "MODE_" }, value = { MODE_NO_MENU, MODE_MOVE_MENU, @@ -170,6 +173,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mPipMenuView = createTvPipMenuView(); setUpViewSurfaceZOrder(mPipMenuView, 1); addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE); + mPipMenuView.getViewTreeObserver().addOnWindowFocusChangeListener(hasFocus -> { + onPipWindowFocusChanged(hasFocus); + }); } @VisibleForTesting @@ -224,13 +230,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void showMovementMenu() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMovementMenu()", TAG); - switchToMenuMode(MODE_MOVE_MENU); + requestMenuMode(MODE_MOVE_MENU); } @Override public void showMenu() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG); - switchToMenuMode(MODE_ALL_ACTIONS_MENU, true); + mPipMenuView.resetMenu(); + requestMenuMode(MODE_ALL_ACTIONS_MENU); } void onPipTransitionToTargetBoundsStarted(Rect targetBounds) { @@ -250,7 +257,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void closeMenu() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: closeMenu()", TAG); - switchToMenuMode(MODE_NO_MENU); + requestMenuMode(MODE_NO_MENU); } @Override @@ -392,11 +399,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } } - // Start methods handling {@link TvPipMenuMode} + // Beginning of convenience methods for {@link TvPipMenuMode} @VisibleForTesting boolean isMenuOpen() { - return mCurrentMenuMode != MODE_NO_MENU; + return isMenuOpen(mCurrentMenuMode); + } + + private static boolean isMenuOpen(@TvPipMenuMode int menuMode) { + return menuMode != MODE_NO_MENU; } @VisibleForTesting @@ -409,31 +420,93 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis return mCurrentMenuMode == MODE_ALL_ACTIONS_MENU; } - private void switchToMenuMode(@TvPipMenuMode int menuMode) { - switchToMenuMode(menuMode, false); + @VisibleForTesting + String getMenuModeString() { + return getMenuModeString(mCurrentMenuMode); } - private void switchToMenuMode(@TvPipMenuMode int menuMode, boolean resetMenu) { - // Note: we intentionally don't return early here, because the TvPipMenuView needs to - // refresh the Ui even if there is no menu mode change. - mPrevMenuMode = mCurrentMenuMode; - mCurrentMenuMode = menuMode; + static String getMenuModeString(@TvPipMenuMode int menuMode) { + switch(menuMode) { + case MODE_NO_MENU: + return "MODE_NO_MENU"; + case MODE_MOVE_MENU: + return "MODE_MOVE_MENU"; + case MODE_ALL_ACTIONS_MENU: + return "MODE_ALL_ACTIONS_MENU"; + default: + return "Unknown"; + } + } + + // Beginning of methods handling switching between menu modes + + private void requestMenuMode(@TvPipMenuMode int menuMode) { + if (isMenuOpen() == isMenuOpen(menuMode)) { + // No need to request a focus change. We can directly switch to the new mode. + switchToMenuMode(menuMode); + } else { + if (isMenuOpen(menuMode)) { + mMenuModeOnFocus = menuMode; + } + + // Send a request to gain window focus if the menu is open, or lose window focus + // otherwise. Once the focus change happens, we will request the new mode in the + // callback {@link #onPipWindowFocusChanged}. + requestPipMenuFocus(isMenuOpen(menuMode)); + } + // Note: we don't handle cases where there is a focus change currently in flight, because + // this is very unlikely to happen in practice and would complicate the logic. + } + + private void requestPipMenuFocus(boolean focus) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: requestPipMenuFocus(%b)", TAG, focus); + + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + mSystemWindows.getFocusGrantToken(mPipMenuView), focus); + } catch (Exception e) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Unable to update focus, %s", TAG, e); + } + } + + /** + * Called when the menu window gains or loses focus. + */ + @VisibleForTesting + void onPipWindowFocusChanged(boolean focused) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: onPipWindowFocusChanged - focused=%b", TAG, focused); + switchToMenuMode(focused ? mMenuModeOnFocus : MODE_NO_MENU); + + // Reset the default menu mode for focused state. + mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU; + } + + /** + * Immediately switches to the menu mode in the given request. Updates the mDelegate and the UI. + * Doesn't handle any focus changes. + */ + private void switchToMenuMode(@TvPipMenuMode int menuMode) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: switchToMenuMode: from=%s, to=%s", TAG, getMenuModeString(), + getMenuModeString(menuMode)); - ProtoLog.i(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: switchToMenuMode: setting mCurrentMenuMode=%s, mPrevMenuMode=%s", TAG, - getMenuModeString(), getMenuModeString(mPrevMenuMode)); + if (mCurrentMenuMode == menuMode) return; - updateUiOnNewMenuModeRequest(resetMenu); + mPrevMenuMode = mCurrentMenuMode; + mCurrentMenuMode = menuMode; + updateUiOnNewMenuModeRequest(); updateDelegateOnNewMenuModeRequest(); } - private void updateUiOnNewMenuModeRequest(boolean resetMenu) { + private void updateUiOnNewMenuModeRequest() { if (mPipMenuView == null || mPipBackgroundView == null) return; mPipMenuView.setPipGravity(mTvPipBoundsState.getTvPipGravity()); - mPipMenuView.transitionToMenuMode(mCurrentMenuMode, resetMenu); + mPipMenuView.transitionToMenuMode(mCurrentMenuMode); mPipBackgroundView.transitionToMenuMode(mCurrentMenuMode); - grantPipMenuFocus(mCurrentMenuMode != MODE_NO_MENU); } private void updateDelegateOnNewMenuModeRequest() { @@ -444,29 +517,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mDelegate.onInMoveModeChanged(); } - if (mCurrentMenuMode == MODE_NO_MENU) { + if (!isMenuOpen()) { mDelegate.onMenuClosed(); } } - @VisibleForTesting - String getMenuModeString() { - return getMenuModeString(mCurrentMenuMode); - } - - static String getMenuModeString(@TvPipMenuMode int menuMode) { - switch(menuMode) { - case MODE_NO_MENU: - return "MODE_NO_MENU"; - case MODE_MOVE_MENU: - return "MODE_MOVE_MENU"; - case MODE_ALL_ACTIONS_MENU: - return "MODE_ALL_ACTIONS_MENU"; - default: - return "Unknown"; - } - } - // Start {@link TvPipMenuView.Delegate} methods @Override @@ -476,42 +531,19 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } @Override - public void onBackPress() { - if (!onExitMoveMode()) { - closeMenu(); - } - } - - @Override - public boolean onExitMoveMode() { + public void onExitCurrentMenuMode() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: onExitMoveMode - mCurrentMenuMode=%s", TAG, getMenuModeString()); - - final int saveMenuMode = mCurrentMenuMode; - if (isInMoveMode()) { - switchToMenuMode(mPrevMenuMode); - } - return saveMenuMode == MODE_MOVE_MENU; + "%s: onExitCurrentMenuMode - mCurrentMenuMode=%s", TAG, getMenuModeString()); + requestMenuMode(isInMoveMode() ? mPrevMenuMode : MODE_NO_MENU); } @Override - public boolean onPipMovement(int keycode) { + public void onPipMovement(int keycode) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: onPipMovement - mCurrentMenuMode=%s", TAG, getMenuModeString()); if (isInMoveMode()) { mDelegate.movePip(keycode); } - return isInMoveMode(); - } - - @Override - public void onPipWindowFocusChanged(boolean focused) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: onPipWindowFocusChanged - focused=%b", TAG, focused); - mMenuIsFocused = focused; - if (!focused && isMenuOpen()) { - closeMenu(); - } } interface Delegate { @@ -524,21 +556,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void closeEduText(); } - private void grantPipMenuFocus(boolean grantFocus) { - if (mMenuIsFocused == grantFocus) return; - - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: grantWindowFocus(%b)", TAG, grantFocus); - - try { - WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus); - } catch (Exception e) { - ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: Unable to update focus, %s", TAG, e); - } - } - private class PipMenuSurfaceChangedCallback implements ViewRootImpl.SurfaceChangedCallback { private final View mView; private final int mZOrder; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 613791ccc062..7c1563787a12 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -328,7 +328,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L return menuUiBounds; } - void transitionToMenuMode(int menuMode, boolean resetMenu) { + void transitionToMenuMode(int menuMode) { switch (menuMode) { case MODE_NO_MENU: hideAllUserControls(); @@ -337,7 +337,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L showMoveMenu(); break; case MODE_ALL_ACTIONS_MENU: - showAllActionsMenu(resetMenu); + showAllActionsMenu(); break; default: throw new IllegalArgumentException( @@ -362,13 +362,13 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L mEduTextDrawer.closeIfNeeded(); } - private void showAllActionsMenu(boolean resetMenu) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: showAllActionsMenu(), resetMenu %b", TAG, resetMenu); + void resetMenu() { + scrollToFirstAction(); + } - if (resetMenu) { - scrollToFirstAction(); - } + private void showAllActionsMenu() { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: showAllActionsMenu()", TAG); if (mCurrentMenuMode == MODE_ALL_ACTIONS_MENU) return; @@ -431,12 +431,6 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L } } - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - mListener.onPipWindowFocusChanged(hasWindowFocus); - } - private void animateAlphaTo(float alpha, View view) { if (view.getAlpha() == alpha) { return; @@ -483,28 +477,28 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L if (event.getAction() == ACTION_UP) { if (event.getKeyCode() == KEYCODE_BACK) { - mListener.onBackPress(); + mListener.onExitCurrentMenuMode(); return true; } - if (mA11yManager.isEnabled()) { - return super.dispatchKeyEvent(event); - } - - switch (event.getKeyCode()) { - case KEYCODE_DPAD_UP: - case KEYCODE_DPAD_DOWN: - case KEYCODE_DPAD_LEFT: - case KEYCODE_DPAD_RIGHT: - return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent( - event); - case KEYCODE_ENTER: - case KEYCODE_DPAD_CENTER: - return mListener.onExitMoveMode() || super.dispatchKeyEvent(event); - default: - break; + if (mCurrentMenuMode == MODE_MOVE_MENU && !mA11yManager.isEnabled()) { + switch (event.getKeyCode()) { + case KEYCODE_DPAD_UP: + case KEYCODE_DPAD_DOWN: + case KEYCODE_DPAD_LEFT: + case KEYCODE_DPAD_RIGHT: + mListener.onPipMovement(event.getKeyCode()); + return true; + case KEYCODE_ENTER: + case KEYCODE_DPAD_CENTER: + mListener.onExitCurrentMenuMode(); + return true; + default: + // Dispatch key event as normal below + } } } + return super.dispatchKeyEvent(event); } @@ -529,7 +523,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L if (a11yEnabled) { mA11yDoneButton.setVisibility(VISIBLE); mA11yDoneButton.setOnClickListener(v -> { - mListener.onExitMoveMode(); + mListener.onExitCurrentMenuMode(); }); mA11yDoneButton.requestFocus(); mA11yDoneButton.requestAccessibilityFocus(); @@ -626,26 +620,15 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L interface Listener { - void onBackPress(); - - /** - * Called when a button for exiting move mode was pressed. - * - * @return true if the event was handled or false if the key event should be handled by the - * next receiver. - */ - boolean onExitMoveMode(); - /** - * @return whether pip movement was handled. + * Called when a button for exiting the current menu mode was pressed. */ - boolean onPipMovement(int keycode); + void onExitCurrentMenuMode(); /** - * Called when the TvPipMenuView loses focus. This also means that the TV PiP menu window - * has lost focus. + * Called when a button to move the PiP in a certain direction, indicated by keycode. */ - void onPipWindowFocusChanged(boolean focused); + void onPipMovement(int keycode); /** * The edu text closing impacts the size of the Picture-in-Picture window and influences diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index 3af1b75f33b6..05e4af3302af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -55,6 +55,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM_SHELL), WM_SHELL_FOLDABLE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), + WM_SHELL_BUBBLES(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + "Bubbles"), TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); private final boolean mEnabled; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 84dcd4db7bb2..0c6adc942385 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -376,8 +376,8 @@ public class SplashscreenContentDrawer { private static int estimateWindowBGColor(Drawable themeBGDrawable) { final DrawableColorTester themeBGTester = new DrawableColorTester( themeBGDrawable, DrawableColorTester.TRANSLUCENT_FILTER /* filterType */); - if (themeBGTester.passFilterRatio() != 1) { - // the window background is translucent, unable to draw + if (themeBGTester.passFilterRatio() < 0.5f) { + // more than half pixels of the window background is translucent, unable to draw Slog.w(TAG, "Window background is translucent, fill background with black color"); return getSystemBGColor(); } else { 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 2be7a491fdf2..29fff03500a5 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 @@ -71,6 +71,7 @@ import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; +import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.KeyguardChangeListener; import com.android.wm.shell.sysui.ShellController; @@ -200,6 +201,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void setSplitScreenController(SplitScreenController splitScreenController) { mSplitScreenController = splitScreenController; + mSplitScreenController.registerSplitScreenListener(new SplitScreen.SplitScreenListener() { + @Override + public void onTaskStageChanged(int taskId, int stage, boolean visible) { + if (visible) { + DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId); + if (decor != null && DesktopModeStatus.isActive(mContext) + && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); + mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo)); + } + } + } + }); } @Override diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt index 610cedefe594..fa723e3110e0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt @@ -23,6 +23,7 @@ import android.platform.test.rule.UnlockScreenRule import android.tools.common.NavBar import android.tools.common.Rotation import android.tools.device.apphelpers.MessagingAppHelper +import android.tools.device.flicker.rules.ArtifactSaverRule import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.flicker.rules.LaunchAppRule import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule @@ -33,9 +34,10 @@ object Utils { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain { - return RuleChain.outerRule(UnlockScreenRule()) + return RuleChain.outerRule(ArtifactSaverRule()) + .around(UnlockScreenRule()) .around( - NavigationModeRule(navigationMode.value, /* changeNavigationModeAfterTest */ false) + NavigationModeRule(navigationMode.value, false) ) .around( LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index a6501f05475f..efc69ebd395c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -58,6 +58,8 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import dagger.Lazy; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -66,8 +68,6 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import dagger.Lazy; - /** * Tests for {@link CompatUIController}. * @@ -82,21 +82,36 @@ public class CompatUIControllerTest extends ShellTestCase { private CompatUIController mController; private ShellInit mShellInit; - private @Mock ShellController mMockShellController; - private @Mock DisplayController mMockDisplayController; - private @Mock DisplayInsetsController mMockDisplayInsetsController; - private @Mock DisplayLayout mMockDisplayLayout; - private @Mock DisplayImeController mMockImeController; - private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener; - private @Mock SyncTransactionQueue mMockSyncQueue; - private @Mock ShellExecutor mMockExecutor; - private @Mock Lazy<Transitions> mMockTransitionsLazy; - private @Mock CompatUIWindowManager mMockCompatLayout; - private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout; - private @Mock RestartDialogWindowManager mMockRestartDialogLayout; - private @Mock DockStateReader mDockStateReader; - private @Mock CompatUIConfiguration mCompatUIConfiguration; - private @Mock CompatUIShellCommandHandler mCompatUIShellCommandHandler; + @Mock + private ShellController mMockShellController; + @Mock + private DisplayController mMockDisplayController; + @Mock + private DisplayInsetsController mMockDisplayInsetsController; + @Mock + private DisplayLayout mMockDisplayLayout; + @Mock + private DisplayImeController mMockImeController; + @Mock + private ShellTaskOrganizer.TaskListener mMockTaskListener; + @Mock + private SyncTransactionQueue mMockSyncQueue; + @Mock + private ShellExecutor mMockExecutor; + @Mock + private Lazy<Transitions> mMockTransitionsLazy; + @Mock + private CompatUIWindowManager mMockCompatLayout; + @Mock + private LetterboxEduWindowManager mMockLetterboxEduLayout; + @Mock + private RestartDialogWindowManager mMockRestartDialogLayout; + @Mock + private DockStateReader mDockStateReader; + @Mock + private CompatUIConfiguration mCompatUIConfiguration; + @Mock + private CompatUIShellCommandHandler mCompatUIShellCommandHandler; @Captor ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java index 5f294d53b662..3bce2b824e28 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java @@ -44,7 +44,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState; +import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; import junit.framework.Assert; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java index 78c3cbdaace6..4c837e635939 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java @@ -53,7 +53,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState; +import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; import junit.framework.Assert; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java index 973a99c269ea..a802f15a0a41 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java @@ -40,6 +40,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.function.BiConsumer; + /** * Tests for {@link ReachabilityEduWindowManager}. * @@ -57,6 +59,8 @@ public class ReachabilityEduWindowManagerTest extends ShellTestCase { private CompatUIConfiguration mCompatUIConfiguration; @Mock private DisplayLayout mDisplayLayout; + @Mock + private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnDismissCallback; private TestShellExecutor mExecutor; private TaskInfo mTaskInfo; private ReachabilityEduWindowManager mWindowManager; @@ -104,6 +108,7 @@ public class ReachabilityEduWindowManagerTest extends ShellTestCase { private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) { return new ReachabilityEduWindowManager(mContext, taskInfo, mSyncTransactionQueue, - mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor); + mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor, + mOnDismissCallback); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java new file mode 100644 index 000000000000..1fee153877b5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java @@ -0,0 +1,154 @@ +/* + * 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.wm.shell.compatui; + +import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.app.TaskInfo; +import android.app.TaskInfo.CameraCompatControlState; +import android.content.ComponentName; +import android.testing.AndroidTestingRunner; +import android.util.Pair; +import android.view.LayoutInflater; +import android.view.SurfaceControlViewHost; +import android.widget.ImageButton; +import android.widget.LinearLayout; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.BiConsumer; + +/** + * Tests for {@link UserAspectRatioSettingsLayout}. + * + * Build/Install/Run: + * atest WMShellUnitTests:UserAspectRatioSettingsLayoutTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class UserAspectRatioSettingsLayoutTest extends ShellTestCase { + + private static final int TASK_ID = 1; + + @Mock + private SyncTransactionQueue mSyncTransactionQueue; + @Mock + private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> + mOnUserAspectRatioSettingsButtonClicked; + @Mock + private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock + private SurfaceControlViewHost mViewHost; + @Captor + private ArgumentCaptor<ShellTaskOrganizer.TaskListener> mUserAspectRatioTaskListenerCaptor; + @Captor + private ArgumentCaptor<TaskInfo> mUserAspectRationTaskInfoCaptor; + + private UserAspectRatioSettingsWindowManager mWindowManager; + private UserAspectRatioSettingsLayout mLayout; + private TaskInfo mTaskInfo; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo, + mSyncTransactionQueue, mTaskListener, new DisplayLayout(), + new CompatUIController.CompatUIHintsState(), + mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor()); + + mLayout = (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate( + R.layout.user_aspect_ratio_settings_layout, null); + mLayout.inject(mWindowManager); + + spyOn(mWindowManager); + spyOn(mLayout); + doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); + doReturn(mLayout).when(mWindowManager).inflateLayout(); + } + + @Test + public void testOnClickForUserAspectRatioSettingsButton() { + final ImageButton button = mLayout.findViewById(R.id.user_aspect_ratio_settings_button); + button.performClick(); + + verify(mWindowManager).onUserAspectRatioSettingsButtonClicked(); + verify(mOnUserAspectRatioSettingsButtonClicked).accept( + mUserAspectRationTaskInfoCaptor.capture(), + mUserAspectRatioTaskListenerCaptor.capture()); + final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = + new Pair<>(mUserAspectRationTaskInfoCaptor.getValue(), + mUserAspectRatioTaskListenerCaptor.getValue()); + Assert.assertEquals(mTaskInfo, result.first); + Assert.assertEquals(mTaskListener, result.second); + } + + @Test + public void testOnLongClickForUserAspectRatioButton() { + doNothing().when(mWindowManager).onUserAspectRatioSettingsButtonLongClicked(); + + final ImageButton button = mLayout.findViewById(R.id.user_aspect_ratio_settings_button); + button.performLongClick(); + + verify(mWindowManager).onUserAspectRatioSettingsButtonLongClicked(); + } + + @Test + public void testOnClickForUserAspectRatioSettingsHint() { + mWindowManager.mHasUserAspectRatioSettingsButton = true; + mWindowManager.createLayout(/* canShow= */ true); + final LinearLayout sizeCompatHint = mLayout.findViewById( + R.id.user_aspect_ratio_settings_hint); + sizeCompatHint.performClick(); + + verify(mLayout).setUserAspectRatioSettingsHintVisibility(/* show= */ false); + } + + private static TaskInfo createTaskInfo(boolean hasSizeCompat, + @CameraCompatControlState int cameraCompatControlState) { + ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); + taskInfo.taskId = TASK_ID; + taskInfo.topActivityInSizeCompat = hasSizeCompat; + taskInfo.cameraCompatControlState = cameraCompatControlState; + taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity"); + return taskInfo; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java new file mode 100644 index 000000000000..b48538ca99ca --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java @@ -0,0 +1,350 @@ +/* + * 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.wm.shell.compatui; + +import static android.view.WindowInsets.Type.navigationBars; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +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.verify; + +import android.app.ActivityManager; +import android.app.TaskInfo; +import android.content.ComponentName; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.util.Pair; +import android.view.DisplayInfo; +import android.view.InsetsSource; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.BiConsumer; + +/** + * Tests for {@link UserAspectRatioSettingsWindowManager}. + * + * Build/Install/Run: + * atest WMShellUnitTests:UserAspectRatioSettingsWindowManagerTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { + + private static final int TASK_ID = 1; + + @Mock private SyncTransactionQueue mSyncTransactionQueue; + @Mock + private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> + mOnUserAspectRatioSettingsButtonClicked; + @Mock private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock private UserAspectRatioSettingsLayout mLayout; + @Mock private SurfaceControlViewHost mViewHost; + @Captor + private ArgumentCaptor<ShellTaskOrganizer.TaskListener> mUserAspectRatioTaskListenerCaptor; + @Captor + private ArgumentCaptor<TaskInfo> mUserAspectRationTaskInfoCaptor; + + private final Set<String> mPackageNameCache = new HashSet<>(); + + private UserAspectRatioSettingsWindowManager mWindowManager; + private TaskInfo mTaskInfo; + + private TestShellExecutor mExecutor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mExecutor = new TestShellExecutor(); + mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + false, /* topActivityBoundsLetterboxed */ true); + mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo, + mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(), + mOnUserAspectRatioSettingsButtonClicked, mExecutor); + spyOn(mWindowManager); + doReturn(mLayout).when(mWindowManager).inflateLayout(); + doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); + } + + @Test + public void testCreateUserAspectRatioButton() { + // Doesn't create layout if show is false. + mWindowManager.mHasUserAspectRatioSettingsButton = true; + assertTrue(mWindowManager.createLayout(/* canShow= */ false)); + + verify(mWindowManager, never()).inflateLayout(); + + // Doesn't create hint popup. + mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = true; + assertTrue(mWindowManager.createLayout(/* canShow= */ true)); + + verify(mWindowManager).inflateLayout(); + mExecutor.flushAll(); + verify(mLayout).setUserAspectRatioButtonVisibility(/* show= */ true); + verify(mLayout, never()).setUserAspectRatioSettingsHintVisibility(/* show= */ true); + + // Creates hint popup. + clearInvocations(mWindowManager); + clearInvocations(mLayout); + mWindowManager.release(); + mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = false; + assertTrue(mWindowManager.createLayout(/* canShow= */ true)); + + verify(mWindowManager).inflateLayout(); + assertNotNull(mLayout); + mExecutor.flushAll(); + verify(mLayout).setUserAspectRatioButtonVisibility(/* show= */ true); + verify(mLayout).setUserAspectRatioSettingsHintVisibility(/* show= */ true); + assertTrue(mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint); + + // Returns false and doesn't create layout if mHasUserAspectRatioSettingsButton is false. + clearInvocations(mWindowManager); + mWindowManager.release(); + mWindowManager.mHasUserAspectRatioSettingsButton = false; + assertFalse(mWindowManager.createLayout(/* canShow= */ true)); + + verify(mWindowManager, never()).inflateLayout(); + } + + @Test + public void testRelease() { + mWindowManager.mHasUserAspectRatioSettingsButton = true; + mWindowManager.createLayout(/* canShow= */ true); + + verify(mWindowManager).inflateLayout(); + + mWindowManager.release(); + + verify(mViewHost).release(); + } + + @Test + public void testUpdateCompatInfo() { + mWindowManager.mHasUserAspectRatioSettingsButton = true; + mWindowManager.createLayout(/* canShow= */ true); + + // No diff + clearInvocations(mWindowManager); + TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + true, /* topActivityBoundsLetterboxed */ true); + assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true)); + + verify(mWindowManager, never()).updateSurfacePosition(); + verify(mWindowManager, never()).release(); + verify(mWindowManager, never()).createLayout(anyBoolean()); + + + // Change task listener, recreate button. + clearInvocations(mWindowManager); + final ShellTaskOrganizer.TaskListener newTaskListener = mock( + ShellTaskOrganizer.TaskListener.class); + assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true)); + + verify(mWindowManager).release(); + verify(mWindowManager).createLayout(/* canShow= */ true); + + // Change has eligibleForUserAspectRatioButton to false, dispose the component + clearInvocations(mWindowManager); + clearInvocations(mLayout); + taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + false, /* topActivityBoundsLetterboxed */ true); + assertFalse( + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true)); + verify(mWindowManager).release(); + } + + @Test + public void testUpdateCompatInfoLayoutNotInflatedYet() { + mWindowManager.mHasUserAspectRatioSettingsButton = true; + mWindowManager.createLayout(/* canShow= */ false); + + verify(mWindowManager, never()).inflateLayout(); + + // Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be + // inflated + clearInvocations(mWindowManager); + TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + false, /* topActivityBoundsLetterboxed */ true); + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); + + verify(mWindowManager, never()).inflateLayout(); + + // Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated. + clearInvocations(mWindowManager); + taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + true, /* topActivityBoundsLetterboxed */ true); + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); + + verify(mWindowManager).inflateLayout(); + } + + @Test + public void testUpdateDisplayLayout() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = 1000; + displayInfo.logicalHeight = 2000; + final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); + + mWindowManager.updateDisplayLayout(displayLayout1); + verify(mWindowManager).updateSurfacePosition(); + + // No update if the display bounds is the same. + clearInvocations(mWindowManager); + final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); + mWindowManager.updateDisplayLayout(displayLayout2); + verify(mWindowManager, never()).updateSurfacePosition(); + } + + @Test + public void testUpdateDisplayLayoutInsets() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = 1000; + displayInfo.logicalHeight = 2000; + final DisplayLayout displayLayout = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false); + + mWindowManager.updateDisplayLayout(displayLayout); + verify(mWindowManager).updateSurfacePosition(); + + // Update if the insets change on the existing display layout + clearInvocations(mWindowManager); + InsetsState insetsState = new InsetsState(); + insetsState.setDisplayFrame(new Rect(0, 0, 1000, 2000)); + InsetsSource insetsSource = new InsetsSource( + InsetsSource.createId(null, 0, navigationBars()), navigationBars()); + insetsSource.setFrame(0, 1800, 1000, 2000); + insetsState.addSource(insetsSource); + displayLayout.setInsets(mContext.getResources(), insetsState); + mWindowManager.updateDisplayLayout(displayLayout); + verify(mWindowManager).updateSurfacePosition(); + } + + @Test + public void testUpdateVisibility() { + // Create button if it is not created. + mWindowManager.removeLayout(); + mWindowManager.mHasUserAspectRatioSettingsButton = true; + mWindowManager.updateVisibility(/* canShow= */ true); + + verify(mWindowManager).createLayout(/* canShow= */ true); + + // Hide button. + clearInvocations(mWindowManager); + doReturn(View.VISIBLE).when(mLayout).getVisibility(); + mWindowManager.updateVisibility(/* canShow= */ false); + + verify(mWindowManager, never()).createLayout(anyBoolean()); + verify(mLayout).setVisibility(View.GONE); + + // Show button. + doReturn(View.GONE).when(mLayout).getVisibility(); + mWindowManager.updateVisibility(/* canShow= */ true); + + verify(mWindowManager, never()).createLayout(anyBoolean()); + verify(mLayout).setVisibility(View.VISIBLE); + } + + @Test + public void testAttachToParentSurface() { + final SurfaceControl.Builder b = new SurfaceControl.Builder(); + mWindowManager.attachToParentSurface(b); + + verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b); + } + + @Test + public void testOnUserAspectRatioButtonClicked() { + mWindowManager.onUserAspectRatioSettingsButtonClicked(); + + verify(mOnUserAspectRatioSettingsButtonClicked).accept( + mUserAspectRationTaskInfoCaptor.capture(), + mUserAspectRatioTaskListenerCaptor.capture()); + final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = + new Pair<>(mUserAspectRationTaskInfoCaptor.getValue(), + mUserAspectRatioTaskListenerCaptor.getValue()); + Assert.assertEquals(mTaskInfo, result.first); + Assert.assertEquals(mTaskListener, result.second); + } + + @Test + public void testOnUserAspectRatioButtonLongClicked_showHint() { + // Not create hint popup. + mWindowManager.mHasUserAspectRatioSettingsButton = true; + mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = true; + mWindowManager.createLayout(/* canShow= */ true); + + verify(mWindowManager).inflateLayout(); + verify(mLayout, never()).setUserAspectRatioSettingsHintVisibility(/* show= */ true); + + mWindowManager.onUserAspectRatioSettingsButtonLongClicked(); + + verify(mLayout).setUserAspectRatioSettingsHintVisibility(/* show= */ true); + } + + @Test + public void testWhenDockedStateHasChanged_needsToBeRecreated() { + ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); + newTaskInfo.configuration.uiMode |= Configuration.UI_MODE_TYPE_DESK; + + Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener)); + } + + private static TaskInfo createTaskInfo(boolean eligibleForUserAspectRatioButton, + boolean topActivityBoundsLetterboxed) { + ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); + taskInfo.taskId = TASK_ID; + taskInfo.topActivityEligibleForUserAspectRatioButton = eligibleForUserAspectRatioButton; + taskInfo.topActivityBoundsLetterboxed = topActivityBoundsLetterboxed; + taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK; + taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity"); + return taskInfo; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 2cc28acd0b17..05c6ba9f0897 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -55,9 +55,9 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TabletopModeController; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; @@ -76,7 +76,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Optional; @@ -329,21 +328,7 @@ public class PipControllerTest extends ShellTestCase { } @Test - public void onKeepClearAreasChanged_featureDisabled_pipBoundsStateDoesntChange() { - mPipController.setEnablePipKeepClearAlgorithm(false); - final int displayId = 1; - final Rect keepClearArea = new Rect(0, 0, 10, 10); - when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId); - - mPipController.mDisplaysChangedListener.onKeepClearAreasChanged( - displayId, Set.of(keepClearArea), Set.of()); - - verify(mMockPipBoundsState, never()).setKeepClearAreas(Mockito.anySet(), Mockito.anySet()); - } - - @Test - public void onKeepClearAreasChanged_featureEnabled_updatesPipBoundsState() { - mPipController.setEnablePipKeepClearAlgorithm(true); + public void onKeepClearAreasChanged_updatesPipBoundsState() { final int displayId = 1; final Rect keepClearArea = new Rect(0, 0, 10, 10); when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index 852183cbcbac..f65d7af70f49 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -18,7 +18,6 @@ package com.android.wm.shell.pip.phone; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -174,16 +173,4 @@ public class PipTouchHandlerTest extends ShellTestCase { verify(mPipResizeGestureHandler, times(1)) .updateMaxSize(expectedMaxSize.getWidth(), expectedMaxSize.getHeight()); } - - @Test - public void updateMovementBounds_withImeAdjustment_movesPip() { - mPipTouchHandler.setEnablePipKeepClearAlgorithm(false); - mFromImeAdjustment = true; - mPipTouchHandler.onImeVisibilityChanged(true /* imeVisible */, mImeHeight); - - mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds, - mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation); - - verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt()); - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java index 3a08d32bc430..e26dc7c10989 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java @@ -25,18 +25,24 @@ import static com.android.wm.shell.pip.tv.TvPipMenuController.MODE_NO_MENU; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.os.Handler; +import android.os.Looper; import android.view.SurfaceControl; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnWindowFocusChangeListener; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.SystemWindows; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -50,28 +56,38 @@ public class TvPipMenuControllerTest extends ShellTestCase { @Mock private SystemWindows mMockSystemWindows; @Mock - private SurfaceControl mMockPipLeash; - @Mock - private Handler mMockHandler; - @Mock - private TvPipActionsProvider mMockActionsProvider; - @Mock private TvPipMenuView mMockTvPipMenuView; @Mock private TvPipBackgroundView mMockTvPipBackgroundView; + private Handler mMainHandler; private TvPipMenuController mTvPipMenuController; + private OnWindowFocusChangeListener mFocusChangeListener; @Before public void setUp() { assumeTrue(isTelevision()); MockitoAnnotations.initMocks(this); + mMainHandler = new Handler(Looper.getMainLooper()); + + final ViewTreeObserver mockMenuTreeObserver = mock(ViewTreeObserver.class); + doReturn(mockMenuTreeObserver).when(mMockTvPipMenuView).getViewTreeObserver(); mTvPipMenuController = new TestTvPipMenuController(); mTvPipMenuController.setDelegate(mMockDelegate); - mTvPipMenuController.setTvPipActionsProvider(mMockActionsProvider); - mTvPipMenuController.attach(mMockPipLeash); + mTvPipMenuController.setTvPipActionsProvider(mock(TvPipActionsProvider.class)); + mTvPipMenuController.attach(mock(SurfaceControl.class)); + mFocusChangeListener = captureFocusChangeListener(mockMenuTreeObserver); + } + + private OnWindowFocusChangeListener captureFocusChangeListener( + ViewTreeObserver mockTreeObserver) { + final ArgumentCaptor<OnWindowFocusChangeListener> focusChangeListenerCaptor = + ArgumentCaptor.forClass(OnWindowFocusChangeListener.class); + verify(mockTreeObserver).addOnWindowFocusChangeListener( + focusChangeListenerCaptor.capture()); + return focusChangeListenerCaptor.getValue(); } @Test @@ -81,24 +97,25 @@ public class TvPipMenuControllerTest extends ShellTestCase { @Test public void testSwitch_FromNoMenuMode_ToMoveMode() { - showAndAssertMoveMenu(); + showAndAssertMoveMenu(true); } @Test public void testSwitch_FromNoMenuMode_ToAllActionsMode() { - showAndAssertAllActionsMenu(); + showAndAssertAllActionsMenu(true); } @Test public void testSwitch_FromMoveMode_ToAllActionsMode() { - showAndAssertMoveMenu(); - showAndAssertAllActionsMenu(); + showAndAssertMoveMenu(true); + showAndAssertAllActionsMenu(false); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test public void testSwitch_FromAllActionsMode_ToMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + showAndAssertAllActionsMenu(true); + showAndAssertMoveMenu(false); } @Test @@ -110,187 +127,282 @@ public class TvPipMenuControllerTest extends ShellTestCase { @Test public void testCloseMenu_MoveMode() { - showAndAssertMoveMenu(); + showAndAssertMoveMenu(true); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test public void testCloseMenu_AllActionsMode() { - showAndAssertAllActionsMenu(); + showAndAssertAllActionsMenu(true); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); + } + + @Test + public void testCloseMenu_MoveModeFollowedByMoveMode() { + showAndAssertMoveMenu(true); + showAndAssertMoveMenu(false); + + closeMenuAndAssertMenuClosed(true); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test public void testCloseMenu_MoveModeFollowedByAllActionsMode() { - showAndAssertMoveMenu(); - showAndAssertAllActionsMenu(); + showAndAssertMoveMenu(true); + showAndAssertAllActionsMenu(false); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); } @Test public void testCloseMenu_AllActionsModeFollowedByMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + showAndAssertAllActionsMenu(true); + showAndAssertMoveMenu(false); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test - public void testExitMoveMode_NoMenuMode() { - mTvPipMenuController.onExitMoveMode(); + public void testCloseMenu_AllActionsModeFollowedByAllActionsMode() { + showAndAssertAllActionsMenu(true); + showAndAssertAllActionsMenu(false); + + closeMenuAndAssertMenuClosed(true); + verify(mMockDelegate, never()).onInMoveModeChanged(); + } + + @Test + public void testExitMenuMode_NoMenuMode() { + mTvPipMenuController.onExitCurrentMenuMode(); assertMenuIsOpen(false); verify(mMockDelegate, never()).onMenuClosed(); + verify(mMockDelegate, never()).onInMoveModeChanged(); } @Test - public void testExitMoveMode_MoveMode() { - showAndAssertMoveMenu(); + public void testExitMenuMode_MoveMode() { + showAndAssertMoveMenu(true); - mTvPipMenuController.onExitMoveMode(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); assertMenuClosed(); verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test - public void testExitMoveMode_AllActionsMode() { - showAndAssertAllActionsMenu(); - - mTvPipMenuController.onExitMoveMode(); - assertMenuIsInAllActionsMode(); + public void testExitMenuMode_AllActionsMode() { + showAndAssertAllActionsMenu(true); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); } @Test - public void testExitMoveMode_AllActionsModeFollowedByMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + public void testExitMenuMode_AllActionsModeFollowedByMoveMode() { + showAndAssertAllActionsMenu(true); + showAndAssertMoveMenu(false); - mTvPipMenuController.onExitMoveMode(); - assertMenuIsInAllActionsMode(); + mTvPipMenuController.onExitCurrentMenuMode(); + assertSwitchedToAllActionsMode(2); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false)); - verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); - } - @Test - public void testOnBackPress_NoMenuMode() { - mTvPipMenuController.onBackPress(); - assertMenuIsOpen(false); - verify(mMockDelegate, never()).onMenuClosed(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); } @Test - public void testOnBackPress_MoveMode() { - showAndAssertMoveMenu(); + public void testExitMenuMode_AllActionsModeFollowedByAllActionsMode() { + showAndAssertAllActionsMenu(true); + showAndAssertAllActionsMenu(false); - pressBackAndAssertMenuClosed(); - verify(mMockDelegate, times(2)).onInMoveModeChanged(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); + verify(mMockDelegate, never()).onInMoveModeChanged(); } @Test - public void testOnBackPress_AllActionsMode() { - showAndAssertAllActionsMenu(); - - pressBackAndAssertMenuClosed(); - } + public void testExitMenuMode_MoveModeFollowedByAllActionsMode() { + showAndAssertMoveMenu(true); - @Test - public void testOnBackPress_MoveModeFollowedByAllActionsMode() { - showAndAssertMoveMenu(); - showAndAssertAllActionsMenu(); + showAndAssertAllActionsMenu(false); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - pressBackAndAssertMenuClosed(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); } @Test - public void testOnBackPress_AllActionsModeFollowedByMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + public void testExitMenuMode_MoveModeFollowedByMoveMode() { + showAndAssertMoveMenu(true); + showAndAssertMoveMenu(false); - mTvPipMenuController.onBackPress(); - assertMenuIsInAllActionsMode(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false)); - verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); - - pressBackAndAssertMenuClosed(); } @Test public void testOnPipMovement_NoMenuMode() { - assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE)); + moveAndAssertMoveSuccessful(false); } @Test public void testOnPipMovement_MoveMode() { - showAndAssertMoveMenu(); - assertPipMoveSuccessful(true, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE)); - verify(mMockDelegate).movePip(eq(TEST_MOVE_KEYCODE)); + showAndAssertMoveMenu(true); + moveAndAssertMoveSuccessful(true); } @Test public void testOnPipMovement_AllActionsMode() { - showAndAssertAllActionsMenu(); - assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE)); + showAndAssertAllActionsMenu(true); + moveAndAssertMoveSuccessful(false); } @Test - public void testOnPipWindowFocusChanged_NoMenuMode() { - mTvPipMenuController.onPipWindowFocusChanged(false); - assertMenuIsOpen(false); + public void testUnexpectedFocusChanges() { + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(1); + + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); + + showAndAssertMoveMenu(true); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(2); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test - public void testOnPipWindowFocusChanged_MoveMode() { - showAndAssertMoveMenu(); - mTvPipMenuController.onPipWindowFocusChanged(false); - assertMenuClosed(); + public void testAsyncScenario_AllActionsModeRequestFollowedByAsyncMoveModeRequest() { + mTvPipMenuController.showMenu(); + // Artificially delaying the focus change update and adding a move request to simulate an + // async problematic situation. + mTvPipMenuController.showMovementMenu(); + // The first focus change update arrives + mFocusChangeListener.onWindowFocusChanged(true); + + // We expect that the TvPipMenuController will directly switch to the "pending" menu mode + // - MODE_MOVE_MENU, because no change of focus is needed. + assertSwitchedToMoveMode(); + } + + @Test + public void testAsyncScenario_MoveModeRequestFollowedByAsyncAllActionsModeRequest() { + mTvPipMenuController.showMovementMenu(); + mTvPipMenuController.showMenu(); + + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(1); + verify(mMockDelegate, never()).onInMoveModeChanged(); } @Test - public void testOnPipWindowFocusChanged_AllActionsMode() { - showAndAssertAllActionsMenu(); - mTvPipMenuController.onPipWindowFocusChanged(false); + public void testAsyncScenario_DropObsoleteIntermediateModeSwitchRequests() { + mTvPipMenuController.showMovementMenu(); + mTvPipMenuController.closeMenu(); + + // Focus change from showMovementMenu() call. + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToMoveMode(); + verify(mMockDelegate).onInMoveModeChanged(); + + // Focus change from closeMenu() call. + mFocusChangeListener.onWindowFocusChanged(false); assertMenuClosed(); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); + + // Unexpected focus gain should open MODE_ALL_ACTIONS_MENU. + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(1); + + mTvPipMenuController.closeMenu(); + mTvPipMenuController.showMovementMenu(); + + assertSwitchedToMoveMode(2); + + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(2); + + // Closing the menu resets the default menu mode, so the next focus gain opens the menu in + // the default mode - MODE_ALL_ACTIONS_MENU. + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(2); + verify(mMockDelegate, times(4)).onInMoveModeChanged(); + } - private void showAndAssertMoveMenu() { + private void showAndAssertMoveMenu(boolean focusChange) { mTvPipMenuController.showMovementMenu(); + if (focusChange) { + mFocusChangeListener.onWindowFocusChanged(true); + } + assertSwitchedToMoveMode(); + } + + private void assertSwitchedToMoveMode() { + assertSwitchedToMoveMode(1); + } + + private void assertSwitchedToMoveMode(int times) { assertMenuIsInMoveMode(); - verify(mMockDelegate).onInMoveModeChanged(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_MOVE_MENU), eq(false)); - verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_MOVE_MENU)); + verify(mMockDelegate, times(2 * times - 1)).onInMoveModeChanged(); + verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU)); + verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU)); + } + + private void showAndAssertAllActionsMenu(boolean focusChange) { + showAndAssertAllActionsMenu(focusChange, 1); } - private void showAndAssertAllActionsMenu() { + private void showAndAssertAllActionsMenu(boolean focusChange, int times) { mTvPipMenuController.showMenu(); + if (focusChange) { + mFocusChangeListener.onWindowFocusChanged(true); + } + + assertSwitchedToAllActionsMode(times); + } + + private void assertSwitchedToAllActionsMode(int times) { assertMenuIsInAllActionsMode(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(true)); - verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); + verify(mMockTvPipMenuView, times(times)) + .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); + verify(mMockTvPipBackgroundView, times(times)) + .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); } - private void closeMenuAndAssertMenuClosed() { + private void closeMenuAndAssertMenuClosed(boolean focusChange) { mTvPipMenuController.closeMenu(); + if (focusChange) { + mFocusChangeListener.onWindowFocusChanged(false); + } assertMenuClosed(); } - private void pressBackAndAssertMenuClosed() { - mTvPipMenuController.onBackPress(); - assertMenuClosed(); + private void moveAndAssertMoveSuccessful(boolean expectedSuccess) { + mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE); + verify(mMockDelegate, times(expectedSuccess ? 1 : 0)).movePip(eq(TEST_MOVE_KEYCODE)); } private void assertMenuClosed() { + assertMenuClosed(1); + } + + private void assertMenuClosed(int times) { assertMenuIsOpen(false); - verify(mMockDelegate).onMenuClosed(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_NO_MENU), eq(false)); - verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_NO_MENU)); + verify(mMockDelegate, times(times)).onMenuClosed(); + verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU)); + verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU)); } private void assertMenuIsOpen(boolean open) { @@ -312,15 +424,10 @@ public class TvPipMenuControllerTest extends ShellTestCase { assertMenuIsOpen(true); } - private void assertPipMoveSuccessful(boolean expected, boolean actual) { - assertTrue("Should " + (expected ? "" : "not ") + "move PiP when the menu is in mode " - + mTvPipMenuController.getMenuModeString(), expected == actual); - } - private class TestTvPipMenuController extends TvPipMenuController { TestTvPipMenuController() { - super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMockHandler); + super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMainHandler); } @Override diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS index 17f5164cf417..436f10737cb4 100644 --- a/libs/androidfw/OWNERS +++ b/libs/androidfw/OWNERS @@ -4,4 +4,4 @@ zyy@google.com patb@google.com per-file CursorWindow.cpp=omakoto@google.com -per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com,nikitai@google.com +per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp index fcb1bfeae6b5..4d020c567972 100644 --- a/libs/hwui/AutoBackendTextureRelease.cpp +++ b/libs/hwui/AutoBackendTextureRelease.cpp @@ -18,9 +18,9 @@ #include <SkImage.h> #include <include/gpu/ganesh/SkImageGanesh.h> -#include <include/gpu/GrBackendSurfaceMutableState.h> #include <include/gpu/GrDirectContext.h> #include <include/gpu/GrBackendSurface.h> +#include <include/gpu/MutableTextureState.h> #include "renderthread/RenderThread.h" #include "utils/Color.h" #include "utils/PaintUtils.h" @@ -142,8 +142,8 @@ void AutoBackendTextureRelease::releaseQueueOwnership(GrDirectContext* context) LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan); if (mBackendTexture.isValid()) { // Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout. - GrBackendSurfaceMutableState newState(VK_IMAGE_LAYOUT_UNDEFINED, - VK_QUEUE_FAMILY_FOREIGN_EXT); + skgpu::MutableTextureState newState(VK_IMAGE_LAYOUT_UNDEFINED, + VK_QUEUE_FAMILY_FOREIGN_EXT); // The unref for this ref happens in the releaseProc passed into setBackendTextureState. The // releaseProc callback will be made when the work to set the new state has finished on the diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index eb5878d95561..8c180da9c84f 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -54,6 +54,8 @@ public: mImpl->updateChildren(std::move(updateFn)); } + void visit(std::function<void(const RenderNode&)> func) const { mImpl->visit(std::move(func)); } + [[nodiscard]] explicit operator bool() const { return mImpl.get() != nullptr; } diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index 8191f5e6a83a..a958a091a830 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -15,6 +15,8 @@ */ #include "FrameInfo.h" +#include <gui/TraceUtils.h> + #include <cstring> namespace android { @@ -51,6 +53,30 @@ static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 23, void FrameInfo::importUiThreadInfo(int64_t* info) { memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); + mSkippedFrameReason.reset(); +} + +const char* toString(SkippedFrameReason reason) { + switch (reason) { + case SkippedFrameReason::DrawingOff: + return "DrawingOff"; + case SkippedFrameReason::ContextIsStopped: + return "ContextIsStopped"; + case SkippedFrameReason::NothingToDraw: + return "NothingToDraw"; + case SkippedFrameReason::NoOutputTarget: + return "NoOutputTarget"; + case SkippedFrameReason::NoBuffer: + return "NoBuffer"; + case SkippedFrameReason::AlreadyDrawn: + return "AlreadyDrawn"; + } +} + +void FrameInfo::setSkippedFrameReason(android::uirenderer::SkippedFrameReason reason) { + ATRACE_FORMAT_INSTANT("Frame skipped: %s", toString(reason)); + addFlag(FrameInfoFlags::SkippedFrame); + mSkippedFrameReason = reason; } } /* namespace uirenderer */ diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index b15b6cb9a9ec..f7ad13978a30 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -16,15 +16,17 @@ #ifndef FRAMEINFO_H_ #define FRAMEINFO_H_ -#include "utils/Macros.h" - #include <cutils/compiler.h> +#include <memory.h> #include <utils/Timers.h> #include <array> -#include <memory.h> +#include <optional> #include <string> +#include "SkippedFrameInfo.h" +#include "utils/Macros.h" + namespace android { namespace uirenderer { @@ -186,8 +188,14 @@ public: return mFrameInfo[static_cast<int>(index)]; } + void setSkippedFrameReason(SkippedFrameReason reason); + inline std::optional<SkippedFrameReason> getSkippedFrameReason() const { + return mSkippedFrameReason; + } + private: int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)]; + std::optional<SkippedFrameReason> mSkippedFrameReason; }; } /* namespace uirenderer */ diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index 687e4dd324d3..59f21694fb77 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -148,7 +148,7 @@ void FrameInfoVisualizer::initializeRects(const int baseline, const int width) { int fast_i = 0, janky_i = 0; // Set the bottom of all the shapes to the baseline for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) { - if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) { + if (mFrameSource[fi].getSkippedFrameReason()) { continue; } float lineWidth = baseLineWidth; @@ -181,7 +181,7 @@ void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex en int janky_i = (mNumJankyRects - 1) * 4; for (size_t fi = 0; fi < mFrameSource.size(); fi++) { - if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) { + if (mFrameSource[fi].getSkippedFrameReason()) { continue; } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 1dd22cf43c5c..eee93c153b9a 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -109,6 +109,13 @@ void RenderNode::output(std::ostream& output, uint32_t level) { output << std::endl; } +void RenderNode::visit(std::function<void(const RenderNode&)> func) const { + func(*this); + if (mDisplayList) { + mDisplayList.visit(func); + } +} + int RenderNode::getUsageSize() { int size = sizeof(RenderNode); size += mStagingDisplayList.getUsedSize(); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index d1e04adcb642..529a49e9277e 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -114,7 +114,7 @@ public: return mDisplayList.containsProjectionReceiver(); } - const char* getName() const { return mName.string(); } + const char* getName() const { return mName.c_str(); } void setName(const char* name) { if (name) { @@ -129,10 +129,6 @@ public: StretchMask& getStretchMask() { return mStretchMask; } - VirtualLightRefBase* getUserContext() const { return mUserContext.get(); } - - void setUserContext(VirtualLightRefBase* context) { mUserContext = context; } - bool isPropertyFieldDirty(DirtyPropertyMask field) const { return mDirtyPropertyFields & field; } @@ -215,6 +211,8 @@ public: void output(std::ostream& output, uint32_t level); + void visit(std::function<void(const RenderNode&)>) const; + void setUsageHint(UsageHint usageHint) { mUsageHint = usageHint; } UsageHint usageHint() const { return mUsageHint; } @@ -222,6 +220,7 @@ public: int64_t uniqueId() const { return mUniqueId; } void setIsTextureView() { mIsTextureView = true; } + bool isTextureView() const { return mIsTextureView; } void markDrawStart(SkCanvas& canvas); void markDrawEnd(SkCanvas& canvas); @@ -248,7 +247,6 @@ private: const int64_t mUniqueId; String8 mName; - sp<VirtualLightRefBase> mUserContext; uint32_t mDirtyPropertyFields; RenderProperties mProperties; diff --git a/libs/hwui/SkippedFrameInfo.h b/libs/hwui/SkippedFrameInfo.h new file mode 100644 index 000000000000..de56d9a26982 --- /dev/null +++ b/libs/hwui/SkippedFrameInfo.h @@ -0,0 +1,30 @@ +/* + * Copyright 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. + */ + +#pragma once + +namespace android::uirenderer { + +enum class SkippedFrameReason { + DrawingOff, + ContextIsStopped, + NothingToDraw, + NoOutputTarget, + NoBuffer, + AlreadyDrawn, +}; + +} /* namespace android::uirenderer */ diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 2bff9cb74fa7..ea25f68d7170 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -16,14 +16,16 @@ #pragma once -#include "Properties.h" -#include "utils/Macros.h" - #include <utils/Timers.h> -#include "SkSize.h" +#include <optional> #include <string> +#include "Properties.h" +#include "SkSize.h" +#include "SkippedFrameInfo.h" +#include "utils/Macros.h" + namespace android { namespace uirenderer { @@ -110,13 +112,13 @@ public: // animate itself, such as if hasFunctors is true // This is only set if hasAnimations is true bool requiresUiRedraw = false; - // This is set to true if draw() can be called this frame - // false means that we must delay until the next vsync pulse as frame + // This is set to nullopt if draw() can be called this frame + // A value means that we must delay until the next vsync pulse as frame // production is outrunning consumption - // NOTE that if this is false CanvasContext will set either requiresUiRedraw + // NOTE that if this has a value CanvasContext will set either requiresUiRedraw // *OR* will post itself for the next vsync automatically, use this // only to avoid calling draw() - bool canDrawThisFrame = true; + std::optional<SkippedFrameReason> skippedFrameReason; // Sentinel for animatedImageDelay meaning there is no need to post such // a message. static constexpr nsecs_t kNoAnimatedImageDelay = -1; diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index af2d3b34bac7..5c8285a8e1e9 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -66,6 +66,12 @@ void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) } } +void SkiaDisplayList::visit(std::function<void(const RenderNode&)> func) const { + for (auto& child : mChildNodes) { + child.getRenderNode()->visit(func); + } +} + static bool intersects(const SkISize screenSize, const Matrix4& mat, const SkRect& bounds) { Vector3 points[] = { Vector3 {bounds.fLeft, bounds.fTop, 0}, Vector3 {bounds.fRight, bounds.fTop, 0}, diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 7af31a4dc4c6..e5bd5c9b2a3b 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -145,6 +145,8 @@ public: */ void updateChildren(std::function<void(RenderNode*)> updateFn); + void visit(std::function<void(const RenderNode&)> func) const; + /** * Returns true if there is a child render node that is a projection receiver. */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 23b3074435cf..774478669058 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,6 +17,8 @@ #include "SkiaOpenGLPipeline.h" #include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <include/gpu/ganesh/gl/GrGLBackendSurface.h> +#include <include/gpu/gl/GrGLTypes.h> #include <GrBackendSurface.h> #include <SkBlendMode.h> #include <SkImageInfo.h> @@ -139,7 +141,8 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( LOG_ALWAYS_FATAL("Unsupported color type."); } - GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo); + auto backendRT = GrBackendRenderTargets::MakeGL(frame.width(), frame.height(), 0, + STENCIL_BUFFER_SIZE, fboInfo); SkSurfaceProps props(mColorMode == ColorMode::Default ? 0 : SkSurfaceProps::kAlwaysDither_Flag, kUnknown_SkPixelGeometry); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 3d77877cf6eb..6679f8ff4f24 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -201,7 +201,7 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator String8 cachesOutput; mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput, &mRenderThread.renderState()); - ALOGE("%s", cachesOutput.string()); + ALOGE("%s", cachesOutput.c_str()); if (errorHandler) { std::ostringstream err; err << "Unable to create layer for " << node->getName(); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 2ef7802c0a3c..4064bb933491 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -357,8 +357,9 @@ bool CanvasContext::makeCurrent() { return true; } -static bool wasSkipped(FrameInfo* info) { - return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame); +static std::optional<SkippedFrameReason> wasSkipped(FrameInfo* info) { + if (info) return info->getSkippedFrameReason(); + return std::nullopt; } bool CanvasContext::isSwapChainStuffed() { @@ -407,13 +408,26 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy // If the previous frame was dropped we don't need to hold onto it, so // just keep using the previous frame's structure instead - if (wasSkipped(mCurrentFrameInfo)) { + if (const auto reason = wasSkipped(mCurrentFrameInfo)) { // Use the oldest skipped frame in case we skip more than a single frame if (!mSkippedFrameInfo) { - mSkippedFrameInfo.emplace(); - mSkippedFrameInfo->vsyncId = - mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); - mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); + switch (*reason) { + case SkippedFrameReason::AlreadyDrawn: + case SkippedFrameReason::NoBuffer: + case SkippedFrameReason::NoOutputTarget: + mSkippedFrameInfo.emplace(); + mSkippedFrameInfo->vsyncId = + mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); + mSkippedFrameInfo->startTime = + mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); + break; + case SkippedFrameReason::DrawingOff: + case SkippedFrameReason::ContextIsStopped: + case SkippedFrameReason::NothingToDraw: + // Do not report those as skipped frames as there was no frame expected to be + // drawn + break; + } } } else { mCurrentFrameInfo = mJankTracker.startFrame(); @@ -427,7 +441,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.damageAccumulator = &mDamageAccumulator; info.layerUpdateQueue = &mLayerUpdateQueue; info.damageGenerationId = mDamageId++; - info.out.canDrawThisFrame = true; + info.out.skippedFrameReason = std::nullopt; mAnimationContext->startFrame(info.mode); for (const sp<RenderNode>& node : mRenderNodes) { @@ -447,8 +461,8 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy mIsDirty = true; if (CC_UNLIKELY(!hasOutputTarget())) { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget; + mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); return; } @@ -463,23 +477,23 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy if (vsyncDelta < 2_ms) { // Already drew for this vsync pulse, UI draw request missed // the deadline for RT animations - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn; } } else { - info.out.canDrawThisFrame = true; + info.out.skippedFrameReason = std::nullopt; } // TODO: Do we need to abort out if the backdrop is added but not ready? Should that even // be an allowable combination? if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) { - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw; } - if (info.out.canDrawThisFrame) { + if (!info.out.skippedFrameReason) { int err = mNativeSurface->reserveNext(); if (err != OK) { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::NoBuffer; + mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err)); if (err != TIMED_OUT) { // A timed out surface can still recover, but assume others are permanently dead. @@ -488,11 +502,11 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy } } } else { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); } bool postedFrameCallback = false; - if (info.out.hasAnimations || !info.out.canDrawThisFrame) { + if (info.out.hasAnimations || info.out.skippedFrameReason) { if (CC_UNLIKELY(!Properties::enableRTAnimations)) { info.out.requiresUiRedraw = true; } @@ -558,9 +572,20 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { mSyncDelayDuration = 0; mIdleDuration = 0; - if (!Properties::isDrawingEnabled() || - (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + const auto skippedFrameReason = [&]() -> std::optional<SkippedFrameReason> { + if (!Properties::isDrawingEnabled()) { + return SkippedFrameReason::DrawingOff; + } + + if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { + return SkippedFrameReason::NothingToDraw; + } + + return std::nullopt; + }(); + if (skippedFrameReason) { + mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason); + if (auto grContext = getGrContext()) { // Submit to ensure that any texture uploads complete and Skia can // free its staging buffers. @@ -904,7 +929,7 @@ void CanvasContext::prepareAndDraw(RenderNode* node) { TreeInfo info(TreeInfo::MODE_RT_ONLY, *this); prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node); - if (info.out.canDrawThisFrame) { + if (!info.out.skippedFrameReason) { draw(info.out.solelyTextureViewUpdates); } else { // wait on fences so tasks don't overlap next frame @@ -1107,6 +1132,12 @@ bool CanvasContext::shouldDither() { return self->mColorMode != ColorMode::Default; } +void CanvasContext::visitAllRenderNodes(std::function<void(const RenderNode&)> func) const { + for (auto node : mRenderNodes) { + node->visit(func); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 3f02674d3b53..241f8dd879d9 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -238,6 +238,8 @@ public: static bool shouldDither(); + void visitAllRenderNodes(std::function<void(const RenderNode&)>) const; + private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline, diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 53b43ba417d0..1b333bfccbf1 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -104,7 +104,7 @@ void DrawFrameTask::run() { info.forceDrawFrame = mForceDrawFrame; mForceDrawFrame = false; canUnblockUiThread = syncFrameState(info); - canDrawThisFrame = info.out.canDrawThisFrame; + canDrawThisFrame = !info.out.skippedFrameReason.has_value(); solelyTextureViewUpdates = info.out.solelyTextureViewUpdates; if (mFrameCommitCallback) { @@ -192,11 +192,12 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { if (CC_UNLIKELY(!hasTarget || !canDraw)) { if (!hasTarget) { mSyncResult |= SyncResult::LostSurfaceRewardIfFound; + info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget; } else { // If we have a surface but can't draw we must be stopped mSyncResult |= SyncResult::ContextIsStopped; + info.out.skippedFrameReason = SkippedFrameReason::ContextIsStopped; } - info.out.canDrawThisFrame = false; } if (info.out.hasAnimations) { @@ -204,7 +205,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mSyncResult |= SyncResult::UIRedrawRequired; } } - if (!info.out.canDrawThisFrame) { + if (info.out.skippedFrameReason) { mSyncResult |= SyncResult::FrameDropped; } // If prepareTextures is false, we ran out of texture cache space diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index eb28c080c056..94ed06c806e5 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -357,7 +357,15 @@ void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) { String8 cachesOutput; mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState); - dprintf(fd, "\nPipeline=%s\n%s\n", pipelineToString(), cachesOutput.string()); + dprintf(fd, "\nPipeline=%s\n%s", pipelineToString(), cachesOutput.c_str()); + for (auto&& context : mCacheManager->mCanvasContexts) { + context->visitAllRenderNodes([&](const RenderNode& node) { + if (node.isTextureView()) { + dprintf(fd, "TextureView: %dx%d\n", node.getWidth(), node.getHeight()); + } + }); + } + dprintf(fd, "\n"); } void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e8c9d0dbd884..c3087bc1c0d2 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -6915,7 +6915,10 @@ public class AudioManager { /** * @hide - * Returns whether CSD is enabled and supported by the HAL on this device. + * Returns whether CSD is enabled and supported by the current active audio module HAL. + * This method will return {@code false) for setups in which CSD as a feature is available + * (see {@link AudioManager#isCsdAsAFeatureAvailable()}) and not enabled (see + * {@link AudioManager#isCsdAsAFeatureEnabled()}). */ @TestApi @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) @@ -6929,6 +6932,49 @@ public class AudioManager { /** * @hide + * Returns whether CSD as a feature can be manipulated by a client. This method + * returns {@code true} in countries where there isn't a safe hearing regulation + * enforced. + */ + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public boolean isCsdAsAFeatureAvailable() { + try { + return getService().isCsdAsAFeatureAvailable(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Returns {@code true} if the client has enabled CSD. This function should only + * be called if {@link AudioManager#isCsdAsAFeatureAvailable()} returns {@code true}. + */ + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public boolean isCsdAsAFeatureEnabled() { + try { + return getService().isCsdAsAFeatureEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Enables/disables the CSD feature. This function should only be called if + * {@link AudioManager#isCsdAsAFeatureAvailable()} returns {@code true}. + */ + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public void setCsdAsAFeatureEnabled(boolean csdToggleValue) { + try { + getService().setCsdAsAFeatureEnabled(csdToggleValue); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide * Describes an audio device that has not been categorized with a specific * audio type. */ diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index b2466e990b8f..5cbb4e539d0a 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -325,6 +325,15 @@ interface IAudioService { boolean isCsdEnabled(); @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + boolean isCsdAsAFeatureAvailable(); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + boolean isCsdAsAFeatureEnabled(); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + oneway void setCsdAsAFeatureEnabled(boolean csdToggleValue); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") oneway void setBluetoothAudioDeviceCategory(in String address, boolean isBle, int deviceType); @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 94a061a6280f..d6921c83b5ee 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -985,6 +985,11 @@ public final class MediaRouter2 { void onRequestCreateControllerByManagerOnHandler( RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) { + Log.i( + TAG, + TextUtils.formatSimple( + "requestCreateSessionByManager | requestId: %d, oldSession: %s, route: %s", + managerRequestId, oldSession, route)); RoutingController controller; if (oldSession.isSystemSession()) { controller = getSystemController(); diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml index 42eaf00a5a07..15a668a73d1f 100644 --- a/packages/CredentialManager/res/values-zh-rCN/strings.xml +++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml @@ -33,7 +33,7 @@ <string name="passwordless_technology_detail" msgid="6853928846532955882">"借助通行密钥,您不必依赖密码就能登录。您只需使用指纹、人脸识别功能、PIN 码或滑动图案便可验证您的身份并创建通行密钥。"</string> <string name="public_key_cryptography_title" msgid="6751970819265298039">"公钥加密"</string> <string name="public_key_cryptography_detail" msgid="6937631710280562213">"根据 FIDO 联盟(成员包括 Google、Apple、Microsoft 等)和 W3C 的标准,通行密钥使用加密密钥对。不同于“用户名+密码字符串”的传统登录凭据,采用通行密钥时,系统会为应用或网站创建一个私钥-公钥对。私钥会安全地存储在您的设备上或密码管理工具中,用于证实您的身份。公钥会被共享给应用或网站服务器。您只要使用相应密钥,就能瞬间注册并登录。"</string> - <string name="improved_account_security_title" msgid="1069841917893513424">"提升了帐号安全性"</string> + <string name="improved_account_security_title" msgid="1069841917893513424">"提升了账号安全性"</string> <string name="improved_account_security_detail" msgid="9123750251551844860">"每个密钥都是专为特定应用或网站创建的,且仅与各自对应的网站或应用关联,因此您绝不会错误地登录任何欺诈性应用或网站。另外,由于服务器只保留公钥,黑客入侵的难度会大大增加。"</string> <string name="seamless_transition_title" msgid="5335622196351371961">"无缝转换"</string> <string name="seamless_transition_detail" msgid="4475509237171739843">"在我们向无密码未来迈进的过程中,密码仍会与通行密钥并行使用。"</string> diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp index d778febe9faf..d6b7ecf5819d 100644 --- a/packages/CtsShim/build/Android.bp +++ b/packages/CtsShim/build/Android.bp @@ -208,3 +208,22 @@ android_app { ], min_sdk_version: "24", } + +//########################################################## +// Variant: Add apk to an apex +android_app { + name: "CtsShimAddApkToApex", + sdk_version: "current", + srcs: ["shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java"], + optimize: { + enabled: false, + }, + dex_preopt: { + enabled: false, + }, + manifest: "shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml", + apex_available: [ + "//apex_available:platform", + "com.android.apex.cts.shim.v2_add_apk_to_apex", + ], +} diff --git a/packages/CtsShim/build/shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml b/packages/CtsShim/build/shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml new file mode 100644 index 000000000000..0e620b062ed6 --- /dev/null +++ b/packages/CtsShim/build/shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.addapktoapex.app"> + + <application> + <activity android:name=".AddApkToApexDeviceActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/packages/CtsShim/build/shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java b/packages/CtsShim/build/shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java new file mode 100644 index 000000000000..c68904b30d6a --- /dev/null +++ b/packages/CtsShim/build/shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java @@ -0,0 +1,42 @@ +/* + * 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 android.addapktoapex.app; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +/** + * A simple activity which logs to Logcat. + */ +public class AddApkToApexDeviceActivity extends Activity { + + private static final String TAG = AddApkToApexDeviceActivity.class.getSimpleName(); + + /** + * The test string to log. + */ + private static final String TEST_STRING = "AddApkToApexTestString"; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + // Log the test string to Logcat. + Log.i(TAG, TEST_STRING); + } + +} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java index f2f019d96023..736e0efe872a 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java @@ -34,7 +34,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Process; import android.os.UserManager; -import android.provider.Settings; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -51,12 +50,10 @@ import java.util.Arrays; public class InstallStart extends Activity { private static final String TAG = InstallStart.class.getSimpleName(); - - private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = 1; - private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = 2; private PackageManager mPackageManager; private UserManager mUserManager; private boolean mAbortInstall = false; + private boolean mShouldFinish = true; private final boolean mLocalLOGV = false; @@ -131,7 +128,7 @@ public class InstallStart extends Activity { mAbortInstall = true; } - checkDevicePolicyRestriction(); + checkDevicePolicyRestrictions(); final String installerPackageNameFromIntent = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); @@ -150,7 +147,9 @@ public class InstallStart extends Activity { if (mAbortInstall) { setResult(RESULT_CANCELED); - finish(); + if (mShouldFinish) { + finish(); + } return; } @@ -281,58 +280,52 @@ public class InstallStart extends Activity { return originatingUid == installerUid; } - private void checkDevicePolicyRestriction() { - // Check for install apps user restriction first. - final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource( - UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle()); - if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) { - if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS); - mAbortInstall = true; - showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER); - return; - } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) { - if (mLocalLOGV) { - Log.i(TAG, "install not allowed by admin; showing " - + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); + private void checkDevicePolicyRestrictions() { + final String[] restrictions = new String[] { + UserManager.DISALLOW_INSTALL_APPS, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY + }; + + final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class); + for (String restriction : restrictions) { + if (!mUserManager.hasUserRestrictionForUser(restriction, Process.myUserHandle())) { + continue; } - mAbortInstall = true; - startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)); - return; - } - final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle()); - final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle()); - final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM - & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource); - if (systemRestriction != 0) { - if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER"); - mAbortInstall = true; - showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER); - } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) { mAbortInstall = true; - startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); - } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) { - mAbortInstall = true; - startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY); + + // If the given restriction is set by an admin, display information about the + // admin enforcing the restriction for the affected user. If not enforced by the admin, + // show the system dialog. + final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction); + if (showAdminSupportDetailsIntent != null) { + if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent); + startActivity(showAdminSupportDetailsIntent); + } else { + if (mLocalLOGV) Log.i(TAG, "Restriction set by system: " + restriction); + mShouldFinish = false; + showDialogInner(restriction); + } + break; } } /** - * Replace any dialog shown by the dialog with the one for the given {@link #createDialog(int)}. + * Replace any dialog shown by the dialog with the one for the given + * {@link #createDialog(String)}. * - * @param id The dialog type to add + * @param restriction The restriction to create the dialog for */ - private void showDialogInner(int id) { - if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + id + ")"); + private void showDialogInner(String restriction) { + if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + restriction + ")"); DialogFragment currentDialog = (DialogFragment) getFragmentManager().findFragmentByTag("dialog"); if (currentDialog != null) { currentDialog.dismissAllowingStateLoss(); } - DialogFragment newDialog = createDialog(id); + DialogFragment newDialog = createDialog(restriction); if (newDialog != null) { getFragmentManager().beginTransaction() .add(newDialog, "dialog").commitAllowingStateLoss(); @@ -342,34 +335,20 @@ public class InstallStart extends Activity { /** * Create a new dialog. * - * @param id The id of the dialog (determines dialog type) + * @param restriction The restriction to create the dialog for * @return The dialog */ - private DialogFragment createDialog(int id) { - if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")"); - switch (id) { - case DLG_INSTALL_APPS_RESTRICTED_FOR_USER: + private DialogFragment createDialog(String restriction) { + if (mLocalLOGV) Log.i(TAG, "createDialog(" + restriction + ")"); + switch (restriction) { + case UserManager.DISALLOW_INSTALL_APPS: return PackageUtil.SimpleErrorDialog.newInstance( R.string.install_apps_user_restriction_dlg_text); - case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER: + case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES: + case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY: return PackageUtil.SimpleErrorDialog.newInstance( R.string.unknown_apps_user_restriction_dlg_text); } return null; } - - private void startAdminSupportDetailsActivity(String restriction) { - if (mLocalLOGV) Log.i(TAG, "startAdminSupportDetailsActivity(): " + restriction); - - // If the given restriction is set by an admin, display information about the - // admin enforcing the restriction for the affected user. - final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class); - final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction); - if (showAdminSupportDetailsIntent != null) { - if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent); - startActivity(showAdminSupportDetailsIntent); - } else { - if (mLocalLOGV) Log.w(TAG, "not intent for " + restriction); - } - } } diff --git a/packages/SettingsLib/Color/.gitignore b/packages/SettingsLib/Color/.gitignore new file mode 100644 index 000000000000..378eac25d311 --- /dev/null +++ b/packages/SettingsLib/Color/.gitignore @@ -0,0 +1 @@ +build diff --git a/packages/SettingsLib/Color/Android.bp b/packages/SettingsLib/Color/Android.bp new file mode 100644 index 000000000000..713b7d959598 --- /dev/null +++ b/packages/SettingsLib/Color/Android.bp @@ -0,0 +1,15 @@ +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_library { + name: "SettingsLibColor", + use_resource_processor: true, + sdk_version: "current", + min_sdk_version: "28", + + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], +} diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml b/packages/SettingsLib/Color/AndroidManifest.xml index b68308972f6e..31e9d23fe3e4 100644 --- a/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml +++ b/packages/SettingsLib/Color/AndroidManifest.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2020 The Android Open Source Project + 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. @@ -12,17 +12,7 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License - --> + limitations under the License. +--> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="@dimen/tv_volume_icons_size" - android:height="@dimen/tv_volume_icons_size" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <group android:translateX="-4"> - <path - android:fillColor="@color/tv_volume_dialog_accent" - android:pathData="M14,8.83v6.34L11.83,13H9v-2h2.83L14,8.83M16,4l-5,5H7v6h4l5,5V4z"/> - </group> -</vector> +<manifest package="com.android.settingslib.color" /> diff --git a/packages/SettingsLib/Color/build.gradle.kts b/packages/SettingsLib/Color/build.gradle.kts new file mode 100644 index 000000000000..881bf14a102b --- /dev/null +++ b/packages/SettingsLib/Color/build.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright 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. + */ + +plugins { + alias(libs.plugins.android.library) +} + +android { + namespace = "com.android.settingslib.color" + + sourceSets { + sourceSets.getByName("main") { + res.setSrcDirs(listOf("res")) + manifest.srcFile("AndroidManifest.xml") + } + } +} diff --git a/packages/SettingsLib/Spa/spa/res/values/colors.xml b/packages/SettingsLib/Color/res/values/colors.xml index ca4a0b2bbed6..b0b9b10952b8 100644 --- a/packages/SettingsLib/Spa/spa/res/values/colors.xml +++ b/packages/SettingsLib/Color/res/values/colors.xml @@ -16,8 +16,6 @@ --> <resources> - <color name="settingslib_protection_color">@android:color/white</color> - <!-- Dynamic colors--> <color name="settingslib_color_blue600">#1a73e8</color> <color name="settingslib_color_blue400">#669df6</color> diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp index e80eb661cdd8..24ccab235fa1 100644 --- a/packages/SettingsLib/IllustrationPreference/Android.bp +++ b/packages/SettingsLib/IllustrationPreference/Android.bp @@ -14,6 +14,7 @@ android_library { resource_dirs: ["res"], static_libs: [ + "SettingsLibColor", "androidx.preference_preference", "lottie", ], diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml index accaa67db1fc..e53a43e7953e 100644 --- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml +++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml @@ -17,51 +17,4 @@ <resources> <color name="settingslib_protection_color">@android:color/white</color> - - <!-- Dynamic colors--> - <color name="settingslib_color_blue600">#1a73e8</color> - <color name="settingslib_color_blue400">#669df6</color> - <color name="settingslib_color_blue300">#8ab4f8</color> - <color name="settingslib_color_blue100">#d2e3fc</color> - <color name="settingslib_color_blue50">#e8f0fe</color> - <color name="settingslib_color_green600">#1e8e3e</color> - <color name="settingslib_color_green500">#34A853</color> - <color name="settingslib_color_green400">#5bb974</color> - <color name="settingslib_color_green100">#ceead6</color> - <color name="settingslib_color_green50">#e6f4ea</color> - <color name="settingslib_color_red600">#d93025</color> - <color name="settingslib_color_red500">#B3261E</color> - <color name="settingslib_color_red400">#ee675c</color> - <color name="settingslib_color_red100">#fad2cf</color> - <color name="settingslib_color_red50">#fce8e6</color> - <color name="settingslib_color_yellow600">#f9ab00</color> - <color name="settingslib_color_yellow400">#fcc934</color> - <color name="settingslib_color_yellow100">#feefc3</color> - <color name="settingslib_color_yellow50">#fef7e0</color> - <color name="settingslib_color_grey900">#202124</color> - <color name="settingslib_color_grey800">#3c4043</color> - <color name="settingslib_color_grey700">#5f6368</color> - <color name="settingslib_color_grey600">#80868b</color> - <color name="settingslib_color_grey500">#9AA0A6</color> - <color name="settingslib_color_grey400">#bdc1c6</color> - <color name="settingslib_color_grey300">#dadce0</color> - <color name="settingslib_color_grey200">#e8eaed</color> - <color name="settingslib_color_grey100">#f1f3f4</color> - <color name="settingslib_color_grey50">#f8f9fa</color> - <color name="settingslib_color_orange600">#e8710a</color> - <color name="settingslib_color_orange400">#fa903e</color> - <color name="settingslib_color_orange300">#fcad70</color> - <color name="settingslib_color_orange100">#fedfc8</color> - <color name="settingslib_color_pink600">#e52592</color> - <color name="settingslib_color_pink400">#ff63b8</color> - <color name="settingslib_color_pink300">#ff8bcb</color> - <color name="settingslib_color_pink100">#fdcfe8</color> - <color name="settingslib_color_purple600">#9334e6</color> - <color name="settingslib_color_purple400">#af5cf7</color> - <color name="settingslib_color_purple300">#c58af9</color> - <color name="settingslib_color_purple100">#e9d2fd</color> - <color name="settingslib_color_cyan600">#12b5c8</color> - <color name="settingslib_color_cyan400">#4ecde6</color> - <color name="settingslib_color_cyan300">#78d9ec</color> - <color name="settingslib_color_cyan100">#cbf0f8</color> </resources> diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java index 07102d55ef7e..82b7e04926eb 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java @@ -23,6 +23,8 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.util.Pair; +import com.android.settingslib.color.R; + import com.airbnb.lottie.LottieAnimationView; import com.airbnb.lottie.LottieProperty; import com.airbnb.lottie.model.KeyPath; diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java index f166a18f528d..0447ef8357eb 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java @@ -21,6 +21,8 @@ import android.content.res.Configuration; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import com.android.settingslib.color.R; + import com.airbnb.lottie.LottieAnimationView; import com.airbnb.lottie.LottieProperty; import com.airbnb.lottie.model.KeyPath; diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index e76139fbbc79..1cc2867ecf0e 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -14,6 +14,7 @@ * limitations under the License. */ +import com.android.build.api.dsl.CommonExtension import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.AndroidBasePlugin import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -25,7 +26,7 @@ plugins { } allprojects { - extra["jetpackComposeVersion"] = "1.6.0-alpha01" + extra["jetpackComposeVersion"] = "1.6.0-alpha02" } subprojects { @@ -47,10 +48,10 @@ subprojects { afterEvaluate { plugins.withType<AndroidBasePlugin> { - configure<BaseExtension> { + the(CommonExtension::class).apply { if (buildFeatures.compose == true) { composeOptions { - kotlinCompilerExtensionVersion = "1.4.4" + kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() } } } diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml index ee40b022259d..0f467b9c3386 100644 --- a/packages/SettingsLib/Spa/gradle/libs.versions.toml +++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml @@ -16,8 +16,9 @@ [versions] agp = "8.1.0" +compose-compiler = "1.5.1" dexmaker-mockito = "2.28.3" -kotlin = "1.8.10" +kotlin = "1.9.0" truth = "1.1" [libraries] diff --git a/packages/SettingsLib/Spa/settings.gradle.kts b/packages/SettingsLib/Spa/settings.gradle.kts index 9909781b0623..aac0fe97f793 100644 --- a/packages/SettingsLib/Spa/settings.gradle.kts +++ b/packages/SettingsLib/Spa/settings.gradle.kts @@ -40,3 +40,5 @@ rootProject.name = "SpaLib" include(":spa") include(":gallery") include(":testutils") +include(":SettingsLibColor") +project(":SettingsLibColor").projectDir = File(rootDir, "../Color") diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp index 7f5948c36e8d..6df0e9956f27 100644 --- a/packages/SettingsLib/Spa/spa/Android.bp +++ b/packages/SettingsLib/Spa/spa/Android.bp @@ -24,6 +24,7 @@ android_library { srcs: ["src/**/*.kt"], use_resource_processor: true, static_libs: [ + "SettingsLibColor", "androidx.slice_slice-builders", "androidx.slice_slice-core", "androidx.slice_slice-view", diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index 377e72edb82a..84198ded7253 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -52,6 +52,7 @@ android { } dependencies { + api(project(":SettingsLibColor")) api("androidx.appcompat:appcompat:1.7.0-alpha03") api("androidx.slice:slice-builders:1.1.0-alpha02") api("androidx.slice:slice-core:1.1.0-alpha02") @@ -62,7 +63,7 @@ dependencies { api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion") api("androidx.lifecycle:lifecycle-livedata-ktx") api("androidx.lifecycle:lifecycle-runtime-compose") - api("androidx.navigation:navigation-compose:2.7.0-beta01") + api("androidx.navigation:navigation-compose:2.7.0-rc01") api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha") api("com.google.android.material:material:1.7.0-alpha03") debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion") diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt index 5f7fe850fa8f..a6cc3a9a6dd2 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt @@ -33,7 +33,7 @@ import com.airbnb.lottie.compose.animateLottieCompositionAsState import com.airbnb.lottie.compose.rememberLottieComposition import com.airbnb.lottie.compose.rememberLottieDynamicProperties import com.airbnb.lottie.compose.rememberLottieDynamicProperty -import com.android.settingslib.spa.R +import com.android.settingslib.color.R @Composable fun Lottie( diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig index 0b74fa8ee361..d1bcb5746414 100644 --- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig +++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig @@ -2,7 +2,7 @@ package: "com.android.settingslib.media.flags" flag { name: "use_media_router2_for_info_media_manager" - namespace: "placeholder_namespace" + namespace: "media_solutions" description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation." bug: "192657812" }
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index ab0775844c9c..df32ef223afb 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Lêeroordrag"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoertoestel"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling van kontakte en oproepgeskiedenis"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruik vir deling van kontakte en oproepgeskiedenis"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling van internetverbinding"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksboodskappe"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-toegang"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 8d9bf8c8ac36..c20a96063e46 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ፋይል ማስተላለፍ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ግቤት መሣሪያ"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"የበይነመረብ ድረስ"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"የእውቂያዎች እና የጥሪ ታሪክ ማጋራት"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"እውቂያዎችን እና የጥሪ ታሪክን ለማጋራት ይጠቀሙበት"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"የበይነ መረብ ተያያዥ ማጋሪያ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"የጽሑፍ መልዕክቶች"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"የሲም መዳረሻ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 5a1a135ae099..7337d7364e17 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز الإرسال"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"الوصول إلى الإنترنت"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"مشاركة جهات الاتصال وسجل المكالمات"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"استخدام إعدادات بلوتوث لمشاركة جهات الاتصال وسجل المكالمات"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"مشاركة اتصال الإنترنت"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"الرسائل النصية"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"الوصول إلى شريحة SIM"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 6080dbfbfead..dd3e5f4d15f2 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তৰণ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইচ"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইণ্টাৰনেট সংযোগ"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰা"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰাৰ বাবে ব্যৱহাৰ কৰক"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইণ্টাৰনেট সংযোগ শ্বেয়াৰ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"পাঠ বাৰ্তা"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিমৰ এক্সেছ"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 3cf8afa185aa..05231ac20444 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl transferi"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Daxiletmə cihazı"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternetə giriş"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktlar və zəng tarixçəsi paylaşımı"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar və zəng tarixçəsi paylaşımı üçün istifadə edin"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"internet bağlantı paylaşımı"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mətn Mesajları"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-karta giriş"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 29d2f7284108..3cefd5983e0c 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup Internetu"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje kontakata i istorije poziva"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Koristite za deljenje kontakata i istorije poziva"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internet veze"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM kartici"</string> diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml index b993eb69e38f..e70d694bb87f 100644 --- a/packages/SettingsLib/res/values-be/arrays.xml +++ b/packages/SettingsLib/res/values-be/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (бяспечны)"</item> <item msgid="7322156123728520872">"4K (шырокамаштабны)"</item> <item msgid="7735692090314849188">"4K (шырокамаштабны, бяспечны)"</item> - <item msgid="7346816300608639624">"720p, 1080p (два экраны)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Няма"</item> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 6489255ead92..47fa1dbd47aa 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Перадача файлаў"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Прылада ўводу"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ у інтэрнэт"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Абагульванне кантактаў і гісторыі выклікаў"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Ужываць для абагульвання кантактаў і гісторыі выклікаў"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Прадастаўленне доступу да Інтэрнэту"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Тэкставыя паведамленні"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ да SIM-карты"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 91dbb339cdc6..bd117b030e4b 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Прехвърляне на файл"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Входно устройство"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Достъп до интернет"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Споделяне на контактите и ист. на обажд."</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Служи за споделяне на контактите и историята на обажданията"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделяне на връзката с интернет"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстови съобщения"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Достъп до SIM картата"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index fdbc9e47c3ec..d7018944e17e 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তর"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইস"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইন্টারনেট অ্যাক্সেস"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"পরিচিতি এবং কলের ইতিহাস শেয়ার করা"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"পরিচিতি ও কলের ইতিহাস শেয়ার করার জন্য ব্যবহার করুন"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইন্টারনেট কানেকশন শেয়ার করা হচ্ছে"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"এসএমএস"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"সিম অ্যাক্সেস"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index ec84dc639717..99e160de438e 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenošenje fajla"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i historije poziva"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotrijebite za dijeljenje kontakata i historije poziva"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internet veze"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 29013854596f..49358bdada16 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferència de fitxers"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositiu d\'entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accés a Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartició de contactes i historial de trucades"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Utilitza per compartir contactes i l\'historial de trucades"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartició de connexió d\'Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Missatges de text"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accés a la SIM"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index b52e7b71a39d..dc2d456c6b37 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Přenos souborů"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupní zařízení"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Přístup k internetu"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Sdílení kontaktů a historie volání"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používat ke sdílení kontaktů a historie hovorů"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Sdílení internetového připojení"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové zprávy"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Přístup k SIM kartě"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index b96c28ac3117..83a67c1e7ac1 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverførsel"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inputenhed"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetadgang"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling af kontakter og opkaldshistorik"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Brug til deling af kontakter og opkaldshistorik"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling af internetforbindelse"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-beskeder"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Adgang til SIM-kort"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index d93baf1a142e..6575f2083f34 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dateiübertragung"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Eingabegerät"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetzugriff"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Teilen von Kontakten und der Anrufliste"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Zum Teilen von Kontakten und der Anrufliste verwenden"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Freigabe der Internetverbindung"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Zugriff auf SIM"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index e8edbffa7ebd..1a36ef8c7496 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Μεταφορά αρχείου"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Συσκευή εισόδου"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Πρόσβαση στο Διαδίκτυο"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Κοινοποίηση επαφών και ιστορικού κλήσεων"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Χρήση για την κοινοποίηση επαφών και του ιστορικού κλήσεων"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Κοινή χρήση σύνδεσης στο Διαδίκτυο"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Μηνύματα κειμένου"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Πρόσβαση SIM"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 2a2e4caff965..fa00776149f4 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index a9a95b1c42f4..5b1e86f1ccc7 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -107,8 +107,8 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string> + <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Allow access to contacts and call history"</string> + <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Info will be used for call announcements and more"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text Messages"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 2a2e4caff965..fa00776149f4 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 2a2e4caff965..fa00776149f4 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index af34e5b4e7bc..8f4ba0ee077f 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -107,8 +107,8 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string> + <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Allow access to contacts and call history"</string> + <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Info will be used for call announcements and more"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Text Messages"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 52cd3fa49214..ec6e152b087c 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llamadas"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso para compartir contactos e historial de llamadas"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a SIM"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index dfad8d3ecf9c..6934f8a4b3af 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llamadas"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para compartir los contactos y el historial de llamadas"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a tarjeta SIM"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 53995ce3b506..9979edacd6ea 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failiedastus"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sisendseade"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Juurdepääs internetile"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktide ja kõneajaloo jagamine"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kasutage kontaktide ja kõneajaloo jagamiseks"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneti-ühenduse jagamine"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstsõnumid"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Juurdepääs SIM-ile"</string> diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index 106dc89e20db..95adf968af12 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (segurua)"</item> <item msgid="7322156123728520872">"4K (hobetua)"</item> <item msgid="7735692090314849188">"4K (hobetua, segurua)"</item> - <item msgid="7346816300608639624">"720p, 1080p (bi pantaila)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Bat ere ez"</item> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 06528e5f704b..dacd8c63decf 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fitxategi-transferentzia"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sarrerako gailua"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneteko konexioa"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktuak eta deien historia partekatzeko aukera"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Erabili kontaktuetarako eta deien historia partekatzeko"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneteko konexioa partekatzea"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Testu-mezuak"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMerako sarbidea"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index a46e327e8a21..b67b2d77d483 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"انتقال فایل"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"دستگاه ورودی"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"دسترسی به اینترنت"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"همرسانی مخاطبین و سابقه تماس"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"استفاده برای همرسانی مخاطبین و سابقه تماس"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"اشتراکگذاری اتصال اینترنت"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"پیامهای نوشتاری"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"دسترسی سیمکارت"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index a5ce34823c53..45d97844107e 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Tiedostonsiirto"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Syöttölaite"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetyhteys"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Yhteystietojen ja soittohistorian jako"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Käytä yhteystiedoissa ja soittohistorian jakamiseen"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetyhteyden jakaminen"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstiviestit"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-kortin käyttö"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml index 8bba20dec595..15cb17d0817c 100644 --- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (sécurisé)"</item> <item msgid="7322156123728520872">"4K (adapté à la haute résolution)"</item> <item msgid="7735692090314849188">"4K (adapté haute rés., sécurisé)"</item> - <item msgid="7346816300608639624">"720p, 1080p (double écran)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Aucun"</item> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index fa565e678ab4..c165acc591f8 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichier"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage des contacts et des appels"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Sert à partager des contacts et l\'historique des appels"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Messages texte"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 4972ebbc3a88..55658e7be784 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichiers"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage contacts/historique des appels"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"À utiliser pour partage des contacts/historique des appels"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la SIM"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index fadaea556d93..f1d0ed175770 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de ficheiros"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e hist. de chamadas"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso da opción de compartir contactos e historial de chamadas"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Uso compartido da conexión a Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensaxes de texto"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso á SIM"</string> diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml index c4f90e43eb25..4f7cbd26532c 100644 --- a/packages/SettingsLib/res/values-gu/arrays.xml +++ b/packages/SettingsLib/res/values-gu/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (સુરક્ષિત)"</item> <item msgid="7322156123728520872">"4K (ઉચ્ચ સ્તરીય)"</item> <item msgid="7735692090314849188">"4K (ઉચ્ચ સ્તરીય, સુરક્ષિત)"</item> - <item msgid="7346816300608639624">"720p, 1080p (દ્વિ સ્ક્રીન)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"કોઈ નહીં"</item> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 11bd628b047e..9f84dd4a2fb7 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ફાઇલ સ્થાનાંતરણ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ઇનપુટ ડિવાઇસ"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ઇન્ટરનેટ ઍક્સેસ"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"સંપર્કો અને કૉલ ઇતિહાસની શેરિંગ"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"સંપર્કો અને કૉલ ઇતિહાસની શેરિંગ માટે ઉપયોગ કરો"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ઇન્ટરનેટ કનેક્શન શેરિંગ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ સંદેશા"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"સિમ ઍક્સેસ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 828fb60966e3..f417f8e3ea97 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"फ़ाइल स्थानांतरण"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिवाइस"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट ऐक्सेस"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"संपर्क और कॉल का इतिहास शेयर करें"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"इसका इस्तेमाल संपर्क और कॉल का इतिहास शेयर करने के लिए करें"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन साझाकरण"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"लेख संदेश"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम ऐक्सेस"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 4c2f89a24647..0da7d1ddb895 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prijenos datoteke"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i povijesti poziva"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotreba za dijeljenje kontakata i povijesti poziva"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internetske veze"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 136afb1405c8..1bca6a0ba561 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fájlátvitel"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Beviteli eszköz"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetelérés"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Névjegyek és hívásnapló megosztása"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Névjegyek és hívásnapló megosztásához használható"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetkapcsolat megosztása"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Szöveges üzenetek"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-elérés"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 58b2df54d0af..a5bccea25838 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Ֆայլերի փոխանցում"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ներմուծման սարք"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ինտերնետի հասանելիություն"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Կիսվել կոնտակտներով/զանգերի պատմությամբ"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Օգտագործել՝ կոնտակտներով/զանգերի պատմությամբ կիսվելու համար"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ինտերնետ կապի տարածում"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS հաղորդագրություններ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM քարտի հասանելիություն"</string> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index e309af7b4cf9..038800cf4bfa 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (aman)"</item> <item msgid="7322156123728520872">"4K (ditingkatkan)"</item> <item msgid="7735692090314849188">"4K (ditingkatkan, aman)"</item> - <item msgid="7346816300608639624">"720p, 1080p (layar ganda)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Tidak ada"</item> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 71ecc3497162..d9c0a06ed12b 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer file"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Perangkat masukan"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Berbagi kontak dan histori panggilan"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gunakan untuk berbagi kontak dan histori panggilan"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Berbagi koneksi internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index d78bd13ecba0..7e746ff85a56 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Skráaflutningur"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inntakstæki"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetaðgangur"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deiling tengiliða og símtalaferils"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Nota til að deila tengiliðum og símtalaferli"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deiling nettengingar"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textaskilaboð"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Aðgangur að SIM-korti"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index becf59e0977b..1e4b9817e3a3 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo di input"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivisione contatti e cronologia chiamate"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usa per condivisione di contatti e cronologia chiamate"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Condivisione connessione Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accesso alla SIM"</string> @@ -545,12 +547,12 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Chiedi ogni volta"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Questo telefono"</string> + <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Questo smartphone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Questo tablet"</string> <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base con altoparlante"</string> <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo esterno"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string> - <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string> + <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo smartphone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossibile riprodurre su questo dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Esegui l\'upgrade dell\'account per cambiare"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Qui non è possibile riprodurre i download"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 77787dcfffe2..328cc829bf90 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"מכשיר קלט"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"גישה לאינטרנט"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"שיתוף אנשי הקשר והיסטוריית השיחות"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ההגדרה משמשת לשיתוף של אנשי הקשר והיסטוריית השיחות"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"שיתוף חיבור לאינטרנט"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"הודעות טקסט"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"גישה ל-SIM"</string> @@ -316,7 +318,7 @@ <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"יש לבחור תצורת USB"</string> <string name="allow_mock_location" msgid="2102650981552527884">"אפשרות של מיקומים מדומים"</string> <string name="allow_mock_location_summary" msgid="179780881081354579">"אפשרות של מיקומים מדומים"</string> - <string name="debug_view_attributes" msgid="3539609843984208216">"לאפשר בדיקת תכונת תצוגה"</string> + <string name="debug_view_attributes" msgid="3539609843984208216">"לאפשר את הבדיקה של מאפיינים של ה-View"</string> <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"השארת חבילת הגלישה פעילה תמיד, גם כש-Wi‑Fi פעיל (למעבר מהיר בין רשתות)."</string> <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"אם השירות זמין, יש להשתמש בשיפור מהירות באמצעות חומרה לצורך שיתוף אינטרנט בין ניידים"</string> <string name="adb_warning_title" msgid="7708653449506485728">"לאפשר ניפוי באגים של USB?"</string> @@ -343,7 +345,7 @@ <string name="select_application" msgid="2543228890535466325">"בחירת אפליקציה"</string> <string name="no_application" msgid="9038334538870247690">"אף אחת"</string> <string name="wait_for_debugger" msgid="7461199843335409809">"יש להמתין לכלי לניפוי באגים"</string> - <string name="wait_for_debugger_summary" msgid="6846330006113363286">"אפליקציה שנוקו בה הבאגים ממתינה למנקה הבאגים לצירוף לפני ביצוע"</string> + <string name="wait_for_debugger_summary" msgid="6846330006113363286">"האפליקציה שמתבצע בה ניקוי באגים ממתינה שהכלי לניפוי באגים יתחבר אליה לפני הריצה"</string> <string name="debug_input_category" msgid="7349460906970849771">"קלט"</string> <string name="debug_drawing_category" msgid="5066171112313666619">"שרטוט"</string> <string name="debug_hw_drawing_category" msgid="5830815169336975162">"עיבוד מואץ של חומרה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index deaba3511d40..46bb17ecf4aa 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ファイル転送"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"入力デバイス"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"インターネットアクセス"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"連絡先と通話履歴の共有"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"連絡先と通話履歴の共有に使用します"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"インターネット接続の共有"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"テキスト メッセージ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMアクセス"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index fb7bada19905..a0205bb30fe6 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ფაილების გადაცემა"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"შეყვანის მოწყობილობა"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ინტერნეტზე წვდომა"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"კონტაქტ. და საუბრის ისტორიის გაზიარება"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"გამოიყენეთ კონტაქტებისა და საუბრის ისტორიის გასაზიარებლად"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ინტერნეტ კავშირის გაზიარება"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ტექსტური შეტყობინებები"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM წვდომა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 0c55c2d9a3b1..3b240d4f57b5 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл жіберу"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Кіріс құрылғысы"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке қосылу"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Контактілер мен қоңыраулар тарихын бөлісу"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Контактілер мен қоңыраулар тарихын бөлісу үшін пайдалану"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланысын ортақ қолдану"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Мәтіндік хабарлар"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картасына кіру"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index f9f964f64143..36c25a9bf467 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ផ្ទេរឯកសារ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ឧបករណ៍បញ្ចូល"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ការចូលប្រើអ៊ីនធឺណិត"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ការចែករំលែកទំនាក់ទំនង និងប្រវត្តិហៅទូរសព្ទ"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ការប្រើសម្រាប់ការចែករំលែកទំនាក់ទំនង និងប្រវត្តិហៅទូរសព្ទ"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ចែករំលែកការតភ្ជាប់អ៊ីនធឺណិត"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"សារជាអក្សរ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ការចូលដំណើរការស៊ីម"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index e17121a3288b..aa9394250efa 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ಫೈಲ್ ವರ್ಗಾವಣೆ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ಇನ್ಪುಟ್ ಸಾಧನ"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ಇಂಟರ್ನೆಟ್ ಆ್ಯಕ್ಸೆಸ್"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ಸಂಪರ್ಕಗಳು ಹಾಗೂ ಕರೆ ಇತಿಹಾಸ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ಸಂಪರ್ಕಗಳು ಮತ್ತು ಕರೆ ಇತಿಹಾಸ ಹಂಚಿಕೆಗಾಗಿ ಬಳಸಿ"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ಪಠ್ಯ ಸಂದೇಶಗಳು"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ಸಿಮ್ ಆ್ಯಕ್ಸೆಸ್"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 148e81dd9375..2b2a09f397f8 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"파일 전송"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"입력 장치"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"인터넷 액세스"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"연락처 및 통화 기록 공유"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"연락처 및 통화 기록 공유에 사용"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"인터넷 연결 공유"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"문자 메시지"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 액세스"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index b3b5abdea7ef..92df6145674b 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл алмашуу"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Киргизүү түзмөгү"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке мүмкүнчүлүк алуу"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Байланыштарды жана чалуу таржымалын бөлүшүү"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Байланыштарды жана чалуу таржымалын бөлүшүү үчүн колдонуу"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланышын бөлүшүү"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS билдирүүлөрү"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картаны пайдалануу мүмкүнчүлүгү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 396351892a2e..fdd393dfd060 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ການໂອນຍ້າຍໄຟລ໌"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ອຸປະກອນປ້ອນຂໍ້ມູນ"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ການເຂົ້າເຖິງອິນເຕີເນັດ"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ແບ່ງປັນລາຍຊື່ຜູ້ຕິດຕໍ່ ແລະ ປະຫວັດການໂທ"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ໃຊ້ສໍາລັບແບ່ງປັນລາຍຊື່ຜູ້ຕິດຕໍ່ ແລະ ປະຫວັດການໂທ"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ການແບ່ງປັນການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ຂໍ້ຄວາມ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ການເຂົ້າເຖິງ SIM"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 9ede5341e86a..7376c11d3031 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failo perkėlimas"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Įvesties įrenginys"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prieiga prie interneto"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktų ir skambučių istorijos bendrinimas"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Naudoti kontaktams ir skambučių istorijai bendrinti"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneto ryšio bendrinimas"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksto pranešimai"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM prieiga"</string> diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml index c9161d92c54c..7626c66c6bc8 100644 --- a/packages/SettingsLib/res/values-lv/arrays.xml +++ b/packages/SettingsLib/res/values-lv/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4 KB (drošs)"</item> <item msgid="7322156123728520872">"4 KB (mērogots)"</item> <item msgid="7735692090314849188">"4 KB (mērogots, drošs)"</item> - <item msgid="7346816300608639624">"720p, 1080p (dubults ekrāns)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Nav"</item> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index c33243bda66c..85ca364dae05 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failu pārsūtīšana"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ievades ierīce"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Piekļuve internetam"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktpersonu un zvanu vēst. kopīgošana"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Paredzēts kontaktpersonu un zvanu vēstures kopīgošanai"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneta savienojuma koplietošana"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Īsziņas"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Piekļuve SIM kartei"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index a3d9e96da830..cdc08318b9fe 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос на датотека"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Влезен уред"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Пристап до интернет"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Споделување контакти и историја на повици"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Користење на споделувањето контакти и историја на повици"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделување конекција на интернет"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстуални пораки"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Пристап до SIM"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 8b3caae1935b..fe7a375ad788 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ഫയൽ കൈമാറൽ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ഇൻപുട്ട് ഉപകരണം"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ഇന്റർനെറ്റ് ആക്സസ്"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"കോൺടാക്റ്റുകളും കോൾ ചരിത്രം പങ്കിടലും"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"കോൺടാക്റ്റുകളുടെയും കോൾ ചരിത്രം പങ്കിടലിന്റെയും ഉപയോഗം"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ഇന്റർനെറ്റ് കണക്ഷൻ പങ്കിടൽ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"അക്ഷര സന്ദേശങ്ങൾ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"സിം ആക്സസ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index a2447d268e87..23b3c2e581d3 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл дамжуулалт"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Оруулах төхөөрөмж"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернэт хандалт"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Харилцагчид ба дуудлагын түүх хуваалцах"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Харилцагчид ба дуудлагын түүх хуваалцахад ашиглах"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернэт холболтыг хуваалцах"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Мессеж"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Хандалт"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index e76c70882683..8e10da7fe356 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानांतरण"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिव्हाइस"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट अॅक्सेस"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"संपर्क आणि कॉल इतिहास शेअरिंग"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"संपर्क आणि कॉल इतिहास शेअरिंगसाठी वापरा"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन शेअररण"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"मजकूर मेसेज"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम अॅक्सेस"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 3f35079877fa..70d99a1dee5a 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Pemindahan fail"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Peranti input"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Perkongsian kenalan & sejarah panggilan"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Digunakan untuk perkongsian kenalan dan sejarah panggilan"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Perkongsian sambungan Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesej Teks"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 9cc605134f06..8c596ada3b09 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ဖိုင်လွဲပြောင်းခြင်း"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ထည့်သွင်းသော စက်"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"အင်တာနက်ချိတ်ဆက်ခြင်း"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"အဆက်အသွယ်၊ ခေါ်ဆိုမှုမှတ်တမ်း မျှဝေခြင်း"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"အဆက်အသွယ်နှင့် ခေါ်ဆိုမှုမှတ်တမ်း မျှဝေရန် သုံးသည်"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"အင်တာနက်ဆက်သွယ်မှု မျှဝေခြင်း"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"မိုဘိုင်းမက်ဆေ့ဂျ်များ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM အသုံးပြုခြင်း"</string> diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml index 0ab39ee542c9..c456ae186c02 100644 --- a/packages/SettingsLib/res/values-nb/arrays.xml +++ b/packages/SettingsLib/res/values-nb/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (sikker)"</item> <item msgid="7322156123728520872">"4K (oppskalert)"</item> <item msgid="7735692090314849188">"4K (oppskalert, sikker)"</item> - <item msgid="7346816300608639624">"720p, 1080p (dobbel skjerm)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Ingen"</item> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 9d3506860a67..f5b607d3c0da 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverføring"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inndataenhet"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internett-tilgang"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling av kontakter og anropslogg"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Bruk for deling av kontakter og anropslogg"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling av internettilkobling"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstmeldinger"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Tilgang til SIM-kortet"</string> @@ -556,7 +558,7 @@ <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan ikke spille av nedlastinger her"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Prøv igjen etter annonsen"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Vekk enheten for å spille her"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Enheten er ikke godkjent"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Enheten er ikke godkjent til å spille av"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Kan ikke spille av dette her"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index e536c16ff09d..a3c121aff59c 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानान्तरण"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट उपकरण"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"इन्टरनेट एक्सेस"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"कन्ट्याक्ट र कल हिस्ट्री सेयर गर्ने"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"कन्ट्याक्ट र कल हिस्ट्री सेयर गर्न प्रयोग गरियोस्"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इन्टरनेट जडान साझेदारी गर्दै"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"टेक्स्ट म्यासेजहरू"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM एक्सेस"</string> diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml index 6898086315e7..4451c93c86dd 100644 --- a/packages/SettingsLib/res/values-nl/arrays.xml +++ b/packages/SettingsLib/res/values-nl/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (veilig)"</item> <item msgid="7322156123728520872">"4K (opgeschaald)"</item> <item msgid="7735692090314849188">"4K (opgeschaald, veilig)"</item> - <item msgid="7346816300608639624">"720p, 1080p (dubbel scherm)"</item> + <item msgid="7346816300608639624">"720p, 1080p (dual screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Geen"</item> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index c73abb4abd5d..aa7e7a0bb943 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Bestandsoverdracht"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoerapparaat"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacten en gespreksgeschiedenis delen"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruiken om contacten en gespreksgeschiedenis te delen"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetverbinding delen"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-berichten"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 89d3636e1834..6154db13f779 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍ପୁଟ୍ ଡିଭାଇସ୍"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟରନେଟ ଆକ୍ସେସ"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ଶେୟାରିଙ୍ଗ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ଟେକ୍ସଟ୍ ମେସେଜ୍"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ"</string> diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml index 0ac6bb57f7df..3cb0f50cce2b 100644 --- a/packages/SettingsLib/res/values-pa/arrays.xml +++ b/packages/SettingsLib/res/values-pa/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (ਸੁਰੱਖਿਅਤ)"</item> <item msgid="7322156123728520872">"4K (ਪੂਰਾ ਕੀਤਾ)"</item> <item msgid="7735692090314849188">"4K (ਪੂਰਾ ਕੀਤਾ, ਸੁਰੱਖਿਅਤ)"</item> - <item msgid="7346816300608639624">"720p, 1080p (ਦੂਹਰੀ ਸਕ੍ਰੀਨ)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"ਕੋਈ ਨਹੀਂ"</item> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 8ba9e7892f5c..1ab9dda4897f 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ਇਨਪੁੱਟ ਡੀਵਾਈਸ"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ਸੰਪਰਕ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ ਸਾਂਝਾ ਕਰਨਾ"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ਸੰਪਰਕ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ ਸਾਂਝਾ ਕਰਨ ਲਈ ਵਰਤੋਂ ਕਰੋ"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ਇੰਟਰਨੈੱਟ ਕਨੈਕਸ਼ਨ ਸਾਂਝਾਕਰਨ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ਲਿਖਤ ਸੁਨੇਹੇ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ਸਿਮ ਪਹੁੰਚ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index e75ea77d82b8..c0dce8dea963 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Przesyłanie pliku"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Urządzenie wejściowe"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Dostęp do internetu"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Udostępnianie kontaktów i historii połączeń"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Używaj w przypadku udostępniania kontaktów i historii połączeń"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Udostępnianie połączenia internetowego"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-y"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostęp do karty SIM"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 1507e5b65700..9493e203f6a8 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index a40cd2ae571a..12d37d2710eb 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência do ficheiro"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Part. histórico de chamadas e contactos"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para partilha do histórico de chamadas e dos contactos"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partilha da ligação à internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao SIM"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 1507e5b65700..9493e203f6a8 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 6fda8d277c1a..0f68d270d26c 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer de fișiere"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispozitiv de intrare"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acces la internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Acces la agendă și istoricul apelurilor"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Folosește pentru accesul la agendă și istoricul apelurilor"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Distribuirea conexiunii la internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesaje text"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acces la SIM"</string> diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml index 3e3b21c70d98..e26b1a623bcb 100644 --- a/packages/SettingsLib/res/values-ru/arrays.xml +++ b/packages/SettingsLib/res/values-ru/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (защита)"</item> <item msgid="7322156123728520872">"4K (масштабирование)"</item> <item msgid="7735692090314849188">"4K (масштабирование, защита)"</item> - <item msgid="7346816300608639624">"720p, 1080p (два экрана)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Нет"</item> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 8127ef2ceb0d..2a390aa79cae 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Профиль HID"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ к интернету"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ к контактам и журналу звонков"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Использовать для доступа к контактам и журналу звонков"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Профиль PAN"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстовые сообщения"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ к SIM-карте"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 277125df1b50..15e00c941c76 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ගොනු හුවමාරුව"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ආදාන උපාංගය"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"අන්තර්ජාල ප්රවේශය"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"සම්බන්ධතා සහ ඇමතුම් ඉතිහාසය බෙදා ගැනීම"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"සම්බන්ධතා සහ ඇමතුම් ඉතිහාසය බෙදා ගැනීම සඳහා භාවිතා කරන්න"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"අන්තර්ජාල සම්බන්ධතා බෙදාගැනීම"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"පෙළ පණිවිඩ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ප්රවේශය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 52b0f0bf8063..5be4d1b9bd25 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupné zariadenie"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prístup na internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľať kontakty a históriu hovorov"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používané pri zdieľaní kontaktov a histórie hovorov"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Zdieľanie pripojenia na Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové správy"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Prístup k SIM karte"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index fdd0a229ee4a..ecef6970389f 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vnosna naprava"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetni dostop"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje stikov in zgodovine klicev"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uporabite za deljenje stikov in zgodovine klicev."</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internetne povezave"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sporočila SMS"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostop do kartice SIM"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 1622ff73e789..e4d402d6460f 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferimi i skedarëve"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Pajisja e hyrjes"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Qasje në internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ndarje: kontakte e historik telefonatash"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Përdor për ndarje kontaktesh e të historikut të telefonatave"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ndarja e lidhjes së internetit"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesazhet me tekst"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Qasje në kartën SIM"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index d1b32898b9ac..2f5ebdd99fd0 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос датотеке"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Улазни уређај"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Приступ Интернету"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Дељење контаката и историје позива"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Користите за дељење контаката и историје позива"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Дељење интернет везе"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ови"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Приступ SIM картици"</string> diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml index dfe491d5c246..3a344ce2325b 100644 --- a/packages/SettingsLib/res/values-sv/arrays.xml +++ b/packages/SettingsLib/res/values-sv/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (säkert)"</item> <item msgid="7322156123728520872">"4K (uppskalat)"</item> <item msgid="7735692090314849188">"4K (uppskalat, säkert)"</item> - <item msgid="7346816300608639624">"720 p, 1080 p (dubbla skärmar)"</item> + <item msgid="7346816300608639624">"720 p, 1080 p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Inga"</item> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 2c7c736bdda4..3cc2dc41ad25 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filöverföring"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Indataenhet"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetåtkomst"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Delning av kontakter och samtalshistorik"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Använd för delning av kontakter och samtalshistorik"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Delning av Internetanslutning"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-åtkomst"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 60d9186b596f..67ab4d3365b5 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Uhamishaji wa faili"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kifaa cha kuingiza"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ufikiaji wa intaneti"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kushiriki anwani na rekodi ya simu zilizopigwa"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Tumia kushiriki anwani na rekodi ya simu zilizopigwa"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Kushiriki muunganisho wa tovuti"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ufikiaji wa SIM"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index ce10e213a3a1..b99a0c53d59d 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"உள்ளீட்டுச் சாதனம்"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"இணைய அணுகல்"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"தொடர்புகள் & அழைப்புப் பதிவைப் பகிர்தல்"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"தொடர்புகளையும் அழைப்புப் பதிவையும் பகிர்வதற்குப் பயன்படுத்து"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"இணைய இணைப்பு பகிர்தல்"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"உரைச் செய்திகள்"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"சிம் அணுகல்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 567bffb1a2e2..3c12cdc913d5 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ఫైల్ బదిలీ"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ఇన్పుట్ పరికరం"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ఇంటర్నెట్ యాక్సెస్"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"కాంటాక్ట్లు, కాల్ హిస్టరీ షేరింగ్"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"కాంటాక్ట్లు, కాల్ హిస్టరీ షేరింగ్ కోసం ఉపయోగించండి"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ షేరింగ్"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"టెక్స్ట్ మెసేజ్లు"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string> diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml index 23d5f606caba..480aee4b2f51 100644 --- a/packages/SettingsLib/res/values-th/arrays.xml +++ b/packages/SettingsLib/res/values-th/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (ปลอดภัย)"</item> <item msgid="7322156123728520872">"4K (เพิ่มความละเอียด)"</item> <item msgid="7735692090314849188">"4K (เพิ่มความละเอียด ปลอดภัย)"</item> - <item msgid="7346816300608639624">"720p, 1080p (หน้าจอคู่)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"ไม่มี"</item> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 69324d1f675e..f3d08ba0b269 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"การถ่ายโอนไฟล์"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"อุปกรณ์อินพุต"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"การเข้าถึงอินเทอร์เน็ต"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"การแชร์รายชื่อติดต่อและประวัติการโทร"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ใช้สำหรับการแชร์รายชื่อติดต่อและประวัติการโทร"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"การแชร์การเชื่อมต่ออินเทอร์เน็ต"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ข้อความ"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"การเข้าถึงซิม"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 889e39616d1b..9159ca4368e0 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Paglilipat ng file"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Device sa pag-input"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Access sa internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts at pagbabahagi ng call history"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gamitin para sa contacts at pagbabahagi ng call history"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Pagbabahagi ng koneksyon sa internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mga Text Message"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Access sa SIM"</string> diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml index ed3755a06f36..27591bee7e3d 100644 --- a/packages/SettingsLib/res/values-tr/arrays.xml +++ b/packages/SettingsLib/res/values-tr/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (güvenli)"</item> <item msgid="7322156123728520872">"4K (yukarı ölçeklenmiş)"</item> <item msgid="7735692090314849188">"4K (yukarı ölçeklenmiş, güvenli)"</item> - <item msgid="7346816300608639624">"720p, 1080p (çift ekran)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Hiçbiri"</item> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 495bcd9838f9..0f8df0231928 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dosya aktarımı"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Giriş cihazı"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternet erişimi"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kişi ve çağrı geçmişi paylaşımı"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kişi ve çağrı geçmişi paylaşımı için kullanın"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"İnternet bağlantısı paylaşımı"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Kısa Mesajlar"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Erişimi"</string> diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml index 83336d5ca20d..6032efbf3ed1 100644 --- a/packages/SettingsLib/res/values-uk/arrays.xml +++ b/packages/SettingsLib/res/values-uk/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (захист)"</item> <item msgid="7322156123728520872">"4K (масштабування)"</item> <item msgid="7735692090314849188">"4K (масштабування, захист)"</item> - <item msgid="7346816300608639624">"720p, 1080p (два екрани)"</item> + <item msgid="7346816300608639624">"720p, 1080p (Dual Screen)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Нічого"</item> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 2158ec1a63e6..8c24312d2679 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Пристрій введення"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ до Інтернету"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ до контактів і історії викликів"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Використовуйте, щоб надсилати контакти й історію викликів"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Надання доступу до Інтернету"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстові повідомлення"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ до SIM-карти"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 5dc733224310..5967addc43d6 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"فائل کی منتقلی"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ان پٹ آلہ"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"انٹرنیٹ تک رسائی"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"رابطے اور کال کی سرگزشت کا اشتراک"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"رابطے اور کال کی سرگزشت کے اشتراک کے لیے استعمال کریں"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"انٹرنیٹ کنکشن کا اشتراک کرنا"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ٹیکسٹ پیغامات"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM رسائی"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index cdc7e1c42967..b9c17fcafb1e 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -107,8 +107,8 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl uzatish"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kiritish qurilmasi"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetga ulanish"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontakt va chaqiruvlar tarixiga kirish"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar va chaqiruvlar tarixiga kirish uchun foydalaning"</string> + <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Chaqiruv va kontaktlarga ruxsat berilsinmi?"</string> + <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Maʼlumotlar chaqiruvlar haqida xabar berish uchun ishlatiladi"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet aloqasi ulashmasi"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS xabarlari"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM kartaga kirish"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 8ed63e9d787d..cc8dacd83c14 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Chuyển tệp"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Thiết bị đầu vào"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Truy cập Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Chia sẻ danh bạ và nhật ký cuộc gọi"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Dùng để chia sẻ danh bạ và nhật ký cuộc gọi"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Chia sẻ kết nối internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tin nhắn văn bản"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Truy cập SIM"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml index 1ae55e48a928..432dd5f829e2 100644 --- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K(安全)"</item> <item msgid="7322156123728520872">"4K(画质提升)"</item> <item msgid="7735692090314849188">"4K(画质提升、安全)"</item> - <item msgid="7346816300608639624">"720p,1080p(双屏)"</item> + <item msgid="7346816300608639624">"720p、1080p(双屏幕)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"无"</item> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 0f0b9c4e9c4f..84af4b52613b 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"文件传输"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"联系人信息和通话记录分享"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用于联系人信息和通话记录分享"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"共享互联网连接"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"短信"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡访问权限"</string> @@ -552,7 +554,7 @@ <string name="media_transfer_default_device_name" msgid="4315604017399871828">"连接的设备"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"无法在此设备上播放"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"升级帐号后才能切换"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"升级账号后才能切换"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"无法在此设备上播放下载的内容"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"广告之后重试"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"若要在此设备上播放,请唤醒设备"</string> @@ -576,7 +578,7 @@ <string name="delete_blob_text" msgid="2819192607255625697">"删除共享数据"</string> <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"确定要删除这些共享数据吗?"</string> <string name="user_add_user_item_summary" msgid="5748424612724703400">"用户拥有个人专属的应用和内容"</string> - <string name="user_add_profile_item_summary" msgid="5418602404308968028">"您可以限制其他人使用来自您的帐号的应用和内容"</string> + <string name="user_add_profile_item_summary" msgid="5418602404308968028">"您可以限制其他人使用来自您的账号的应用和内容"</string> <string name="user_add_user_item_title" msgid="2394272381086965029">"用户"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"受限个人资料"</string> <string name="user_add_user_title" msgid="5457079143694924885">"要添加新用户吗?"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 57718d89931a..dfde69f7a276 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"互聯網連線"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"分享通訊錄及通話記錄"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用於分享通訊錄及通話記錄"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"互聯網連線分享"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"短訊"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 18317dace3fb..0257527d3377 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"網際網路連線"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"分享聯絡人和通話記錄"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用於分享聯絡人和通話記錄"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"網際網路連線分享"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"簡訊"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取權"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index edf5f3c1f4c1..27bad753bfa6 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -107,8 +107,10 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dlulisa ifayela"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Idivaysi yokufakwayo"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ukufinyelela i-Inthanethi"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ukwabelana ngoxhumana nabo nomlando wekholi"</string> - <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Ukusetshenziswa kokwabelana ngoxhumana nabo nomlando wekholi"</string> + <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) --> + <skip /> + <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) --> + <skip /> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ukwabelana ngoxhumano lwe-Inthanethi"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Imilayezo yombhalo"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ukufinyelela kwe-SIM"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index 9ab84d254ed6..f90a17ae8761 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -45,6 +45,7 @@ public class BatteryStatus { private static final int EXTREME_LOW_BATTERY_THRESHOLD = 3; private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000; + public static final int BATTERY_LEVEL_UNKNOWN = -1; public static final int CHARGING_UNKNOWN = -1; public static final int CHARGING_SLOWLY = 0; public static final int CHARGING_REGULAR = 1; @@ -186,12 +187,13 @@ public class BatteryStatus { /** Gets the battery level from the intent. */ public static int getBatteryLevel(Intent batteryChangedIntent) { if (batteryChangedIntent == null) { - return -1; /*invalid battery level*/ + return BATTERY_LEVEL_UNKNOWN; } - final int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + final int level = + batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL_UNKNOWN); final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); return scale == 0 - ? -1 /*invalid battery level*/ + ? BATTERY_LEVEL_UNKNOWN : Math.round((level / (float) scale) * 100f); } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index fe3ef5d0d46f..dbc3bf7b793e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.media.AudioDeviceAttributes; import android.media.AudioManager; @@ -520,9 +521,13 @@ public class LocalMediaManager implements BluetoothCallback { if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) { - MediaDevice mutingExpectedDevice = getMutingExpectedDevice(); - if (mutingExpectedDevice != null) { - mMediaDevices.add(mutingExpectedDevice); + if (isTv()) { + mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + } else { + MediaDevice mutingExpectedDevice = getMutingExpectedDevice(); + if (mutingExpectedDevice != null) { + mMediaDevices.add(mutingExpectedDevice); + } } break; } @@ -542,6 +547,12 @@ public class LocalMediaManager implements BluetoothCallback { } } + private boolean isTv() { + PackageManager pm = mContext.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION) + || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK); + } + private MediaDevice getMutingExpectedDevice() { if (mBluetoothAdapter == null || mAudioManager.getMutingExpectedDevice() == null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index 9234d374e1ad..147412d08d2c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -22,6 +22,7 @@ import static android.media.MediaRoute2Info.TYPE_DOCK; import static android.media.MediaRoute2Info.TYPE_GROUP; import static android.media.MediaRoute2Info.TYPE_HDMI; import static android.media.MediaRoute2Info.TYPE_HEARING_AID; +import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER; import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER; import static android.media.MediaRoute2Info.TYPE_REMOTE_TV; import static android.media.MediaRoute2Info.TYPE_UNKNOWN; @@ -82,7 +83,8 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE, MediaDeviceType.TYPE_BLUETOOTH_DEVICE, MediaDeviceType.TYPE_CAST_DEVICE, - MediaDeviceType.TYPE_CAST_GROUP_DEVICE}) + MediaDeviceType.TYPE_CAST_GROUP_DEVICE, + MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER}) public @interface MediaDeviceType { int TYPE_UNKNOWN = 0; int TYPE_PHONE_DEVICE = 1; @@ -92,6 +94,7 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { int TYPE_BLUETOOTH_DEVICE = 5; int TYPE_CAST_DEVICE = 6; int TYPE_CAST_GROUP_DEVICE = 7; + int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 8; } @Retention(RetentionPolicy.SOURCE) @@ -161,6 +164,9 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { case TYPE_BLE_HEADSET: mType = MediaDeviceType.TYPE_BLUETOOTH_DEVICE; break; + case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER: + mType = MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER; + break; case TYPE_UNKNOWN: case TYPE_REMOTE_TV: case TYPE_REMOTE_SPEAKER: diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java b/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java new file mode 100644 index 000000000000..1134d13132e8 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java @@ -0,0 +1,127 @@ +/* + * 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.settingslib.wifi.dpp; + +import android.content.Intent; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.text.TextUtils; + +import java.util.List; + + +/** + * Wifi dpp intent helper functions to share between the Settings App and SystemUI. + */ +public class WifiDppIntentHelper { + static final String EXTRA_WIFI_SECURITY = "security"; + + /** The data corresponding to {@code WifiConfiguration} SSID */ + static final String EXTRA_WIFI_SSID = "ssid"; + + /** The data corresponding to {@code WifiConfiguration} preSharedKey */ + static final String EXTRA_WIFI_PRE_SHARED_KEY = "preSharedKey"; + + /** The data corresponding to {@code WifiConfiguration} hiddenSSID */ + static final String EXTRA_WIFI_HIDDEN_SSID = "hiddenSsid"; + static final String SECURITY_NO_PASSWORD = "nopass"; //open network or OWE + static final String SECURITY_WEP = "WEP"; + static final String SECURITY_WPA_PSK = "WPA"; + static final String SECURITY_SAE = "SAE"; + + /** + * Set all extra except {@code EXTRA_WIFI_NETWORK_ID} for the intent to + * launch configurator activity later. + * + * @param intent the target to set extra + * @param wifiManager an instance of {@code WifiManager} + * @param wifiConfiguration the Wi-Fi network for launching configurator activity + */ + public static void setConfiguratorIntentExtra(Intent intent, WifiManager wifiManager, + WifiConfiguration wifiConfiguration) { + String ssid = removeFirstAndLastDoubleQuotes(wifiConfiguration.SSID); + String security = getSecurityString(wifiConfiguration); + + // When the value of this key is read, the actual key is not returned, just a "*". + // Call privileged system API to obtain actual key. + String preSharedKey = removeFirstAndLastDoubleQuotes(getPresharedKey(wifiManager, + wifiConfiguration)); + + if (!TextUtils.isEmpty(ssid)) { + intent.putExtra(EXTRA_WIFI_SSID, ssid); + } + if (!TextUtils.isEmpty(security)) { + intent.putExtra(EXTRA_WIFI_SECURITY, security); + } + if (!TextUtils.isEmpty(preSharedKey)) { + intent.putExtra(EXTRA_WIFI_PRE_SHARED_KEY, preSharedKey); + } + intent.putExtra(EXTRA_WIFI_HIDDEN_SSID, wifiConfiguration.hiddenSSID); + } + + private static String getSecurityString(WifiConfiguration config) { + if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) { + return SECURITY_SAE; + } + if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) { + return SECURITY_NO_PASSWORD; + } + if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK) + || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA2_PSK)) { + return SECURITY_WPA_PSK; + } + return (config.wepKeys[0] == null) ? SECURITY_NO_PASSWORD : SECURITY_WEP; + } + + private static String removeFirstAndLastDoubleQuotes(String str) { + if (TextUtils.isEmpty(str)) { + return str; + } + + int begin = 0; + int end = str.length() - 1; + if (str.charAt(begin) == '\"') { + begin++; + } + if (str.charAt(end) == '\"') { + end--; + } + return str.substring(begin, end + 1); + } + + private static String getPresharedKey(WifiManager wifiManager, + WifiConfiguration wifiConfiguration) { + List<WifiConfiguration> privilegedWifiConfigurations = + wifiManager.getPrivilegedConfiguredNetworks(); + + for (WifiConfiguration privilegedWifiConfiguration : privilegedWifiConfigurations) { + if (privilegedWifiConfiguration.networkId == wifiConfiguration.networkId) { + // WEP uses a shared key hence the AuthAlgorithm.SHARED is used + // to identify it. + if (wifiConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE) + && wifiConfiguration.allowedAuthAlgorithms.get( + WifiConfiguration.AuthAlgorithm.SHARED)) { + return privilegedWifiConfiguration + .wepKeys[privilegedWifiConfiguration.wepTxKeyIndex]; + } else { + return privilegedWifiConfiguration.preSharedKey; + } + } + } + return wifiConfiguration.preSharedKey; + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelperTest.java new file mode 100644 index 000000000000..d73df2d6862c --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelperTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.wifi.dpp; + +import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_HIDDEN_SSID; +import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_PRE_SHARED_KEY; +import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_SECURITY; +import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.EXTRA_WIFI_SSID; +import static com.android.settingslib.wifi.dpp.WifiDppIntentHelper.SECURITY_NO_PASSWORD; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +import java.util.ArrayList; + +@RunWith(RobolectricTestRunner.class) +public class WifiDppIntentHelperTest { + @Mock + private WifiManager mWifiManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mWifiManager.getPrivilegedConfiguredNetworks()).thenReturn(new ArrayList<>()); + } + + @Test + public void setConfiguratorIntentExtra_returnsCorrectValues() { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.SSID = EXTRA_WIFI_SSID; + wifiConfiguration.preSharedKey = EXTRA_WIFI_PRE_SHARED_KEY; + wifiConfiguration.hiddenSSID = true; + + Intent expected = new Intent(); + WifiDppIntentHelper.setConfiguratorIntentExtra(expected, mWifiManager, wifiConfiguration); + + assertThat(expected.getStringExtra(EXTRA_WIFI_SSID)).isEqualTo(EXTRA_WIFI_SSID); + assertThat(expected.getStringExtra(EXTRA_WIFI_SECURITY)).isEqualTo(SECURITY_NO_PASSWORD); + assertThat(expected.getStringExtra(EXTRA_WIFI_PRE_SHARED_KEY)).isEqualTo( + EXTRA_WIFI_PRE_SHARED_KEY); + assertThat(expected.getBooleanExtra(EXTRA_WIFI_HIDDEN_SSID, false)) + .isEqualTo(true); + } +} diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 423c8a3d23eb..2d62e2a95da1 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -242,6 +242,7 @@ public class SecureSettings { Settings.Secure.HEARING_AID_CALL_ROUTING, Settings.Secure.HEARING_AID_MEDIA_ROUTING, Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING, - Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED + Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, + Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 58106c05affa..bb67bbc39812 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -441,7 +441,7 @@ public class GlobalSettingsValidators { String.valueOf(Global.Wearable.TETHERED_CONFIG_TETHERED) })); VALIDATORS.put(Global.Wearable.PHONE_SWITCHING_SUPPORTED, BOOLEAN_VALIDATOR); - VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index bbfdc384bd60..4494765fd8e6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -200,6 +200,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"})); VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR); @@ -261,6 +262,8 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.NAV_BAR_KIDS_MODE, BOOLEAN_VALIDATOR); VALIDATORS.put( Secure.NAVIGATION_MODE, new DiscreteValueValidator(new String[] {"0", "1", "2"})); + VALIDATORS.put(Secure.NAVIGATION_MODE_RESTORE, + new DiscreteValueValidator(new String[] {"-1", "0", "1", "2"})); VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_LEFT, new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE)); VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_RIGHT, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 0dd85695b07a..80cf6c313316 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -184,6 +184,7 @@ public class SystemSettingsValidators { VALIDATORS.put(System.POINTER_LOCATION, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SHOW_TOUCHES, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SHOW_KEY_PRESSES, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.SHOW_ROTARY_INPUT, BOOLEAN_VALIDATOR); VALIDATORS.put(System.WINDOW_ORIENTATION_LISTENER_LOG, BOOLEAN_VALIDATOR); VALIDATORS.put(System.LOCKSCREEN_SOUNDS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(System.LOCKSCREEN_DISABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 99a00e401bb0..c830d6b2b611 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -835,7 +835,11 @@ public class SettingsBackupAgent extends BackupAgentHelper { continue; } - if (settingsToPreserve.contains(getQualifiedKeyForSetting(key, contentUri))) { + // Filter out Settings.Secure.NAVIGATION_MODE from modified preserve settings. + // Let it take part in restore process. See also b/244532342. + boolean isSettingPreserved = settingsToPreserve.contains( + getQualifiedKeyForSetting(key, contentUri)); + if (isSettingPreserved && !Settings.Secure.NAVIGATION_MODE.equals(key)) { Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as " + "preserved"); continue; @@ -896,6 +900,23 @@ public class SettingsBackupAgent extends BackupAgentHelper { } else { destination = contentUri; } + + // Value is written to NAVIGATION_MODE_RESTORE to mark navigation mode + // has been set before on source device. + // See also: b/244532342. + if (Settings.Secure.NAVIGATION_MODE.equals(key)) { + contentValues.clear(); + contentValues.put(Settings.NameValueTable.NAME, + Settings.Secure.NAVIGATION_MODE_RESTORE); + contentValues.put(Settings.NameValueTable.VALUE, value); + cr.insert(destination, contentValues); + // Avoid restore original setting if it has been preserved. + if (isSettingPreserved) { + Log.i(TAG, "Skipping restore for setting navigation_mode " + + "as it is marked as preserved"); + continue; + } + } settingsHelper.restoreValue(this, cr, contentValues, destination, key, value, mRestoredFromSdkInt); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 1192e0086123..d2b444b3c40e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1926,6 +1926,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED); + dumpSetting(s, p, + Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED, + SecureSettingsProto.Assist.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED); p.end(assistToken); final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 5475faded431..c697c1ff66ec 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -79,6 +79,7 @@ public class SettingsBackupTest { Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup? Settings.System.SHOW_TOUCHES, Settings.System.SHOW_KEY_PRESSES, + Settings.System.SHOW_ROTARY_INPUT, Settings.System.SIP_ADDRESS_ONLY, // value, not a setting Settings.System.SIP_ALWAYS, // value, not a setting Settings.System.SYSTEM_LOCALES, // bug? @@ -723,7 +724,8 @@ public class SettingsBackupTest { Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY, - Settings.Secure.AUDIO_DEVICE_INVENTORY, // setting not controllable by user + Settings.Secure.AUDIO_DEVICE_INVENTORY, // not controllable by user + Settings.Secure.AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED, // not controllable by user Settings.Secure.BACKUP_AUTO_RESTORE, Settings.Secure.BACKUP_ENABLED, Settings.Secure.BACKUP_PROVISIONED, @@ -859,7 +861,8 @@ public class SettingsBackupTest { Settings.Secure.CREDENTIAL_SERVICE, Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED, - Settings.Secure.DND_CONFIGS_MIGRATED); + Settings.Secure.DND_CONFIGS_MIGRATED, + Settings.Secure.NAVIGATION_MODE_RESTORE); @Test public void systemSettingsBackedUpOrDenied() { diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java index ef062dfd3ec3..4b10b56f49fb 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java @@ -23,14 +23,16 @@ import static junit.framework.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.content.ContentProvider; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.database.Cursor; import android.database.MatrixCursor; @@ -44,8 +46,8 @@ import android.provider.Settings; import android.telephony.TelephonyManager; import android.test.mock.MockContentProvider; import android.test.mock.MockContentResolver; +import android.util.ArrayMap; -import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; @@ -77,13 +79,14 @@ public class SettingsHelperTest { @Mock private Context mContext; @Mock private Resources mResources; - @Mock private ContentResolver mContentResolver; @Mock private AudioManager mAudioManager; @Mock private TelephonyManager mTelephonyManager; + @Mock private MockContentResolver mContentResolver; + private MockSettingsProvider mSettingsProvider; + @Before public void setUp() { - clearLongPressPowerValues(); MockitoAnnotations.initMocks(this); when(mContext.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManager); when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn( @@ -91,14 +94,20 @@ public class SettingsHelperTest { when(mContext.getResources()).thenReturn(mResources); when(mContext.getApplicationContext()).thenReturn(mContext); when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); - when(mContext.getContentResolver()).thenReturn(getContentResolver()); mSettingsHelper = spy(new SettingsHelper(mContext)); + mContentResolver = spy(new MockContentResolver()); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + mSettingsProvider = new MockSettingsProvider(mContext); + mContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider); } @After public void tearDown() { - clearLongPressPowerValues(); + Settings.Global.putString(mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, + null); + Settings.Global.putString(mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, + null); } @Test @@ -123,33 +132,30 @@ public class SettingsHelperTest { mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY, SETTING_KEY, SETTING_VALUE, /* restoredFromSdkInt */ 0); - verifyZeroInteractions(mContentResolver); + // The only time of interaction happened during setUp() + verify(mContentResolver, times(1)) + .addProvider(Settings.AUTHORITY, mSettingsProvider); + + verifyNoMoreInteractions(mContentResolver); } @Test public void testRestoreValue_lppForAssistantEnabled_updatesValue() { - ContentResolver cr = - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getContentResolver(); when(mResources.getBoolean( R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn( true); - mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY, + mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY, Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0); - assertThat( - Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)) - .isEqualTo(5); - assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, - -1)).isEqualTo(2); + assertThat(Settings.Global.getInt( + mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(5); + assertThat(Settings.Global.getInt( + mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(2); } @Test public void testRestoreValue_lppForAssistantNotEnabled_updatesValueToDefaultConfig() { - ContentResolver cr = - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getContentResolver(); when(mResources.getBoolean( R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn( true); @@ -161,21 +167,17 @@ public class SettingsHelperTest { R.integer.config_keyChordPowerVolumeUp)).thenReturn( 1); - mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY, + mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY, Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0); - assertThat( - Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)) - .isEqualTo(1); - assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, - -1)).isEqualTo(1); + assertThat(Settings.Global.getInt( + mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1); + assertThat(Settings.Global.getInt( + mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1); } @Test public void testRestoreValue_lppForAssistantNotEnabledDefaultConfig_updatesValue() { - ContentResolver cr = - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getContentResolver(); when(mResources.getBoolean( R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn( true); @@ -187,47 +189,39 @@ public class SettingsHelperTest { R.integer.config_keyChordPowerVolumeUp)).thenReturn( 1); - mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY, + mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY, Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0); - assertThat( - Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)) - .isEqualTo(1); - assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, - -1)).isEqualTo(1); + assertThat(Settings.Global.getInt( + mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1); + assertThat(Settings.Global.getInt( + mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1); } @Test public void testRestoreValue_lppForAssistantNotAvailable_doesNotRestore() { - ContentResolver cr = - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getContentResolver(); - when(mResources.getBoolean( - R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn( - false); + when(mResources.getBoolean(R.bool.config_longPressOnPowerForAssistantSettingAvailable)) + .thenReturn(false); - mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY, - Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0); + mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY, + Settings.Global.POWER_BUTTON_LONG_PRESS, "500", 0); - assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, - -1))).isEqualTo(-1); + assertThat((Settings.Global.getInt( + mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1); } @Test public void testRestoreValue_lppForAssistantInvalid_doesNotRestore() { - ContentResolver cr = - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getContentResolver(); when(mResources.getBoolean( R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn( false); - mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY, + mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY, Settings.Global.POWER_BUTTON_LONG_PRESS, "trees", 0); - assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, - -1))).isEqualTo(-1); + assertThat((Settings.Global.getInt( + mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1); } @Test @@ -363,9 +357,6 @@ public class SettingsHelperTest { final String newRingtoneValueCanonicalized = "content://media/internal/audio/media/100?title=Song&canonical=1"; - MockContentResolver mMockContentResolver = new MockContentResolver(); - when(mContext.getContentResolver()).thenReturn(mMockContentResolver); - ContentProvider mockMediaContentProvider = new MockContentProvider(mContext) { @Override @@ -386,25 +377,22 @@ public class SettingsHelperTest { } }; - ContentProvider mockSettingsContentProvider = - new MockSettingsProvider(mContext, getContentResolver()); - mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider); + mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - resetRingtoneSettingsToDefault(mMockContentResolver); - assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE)) + resetRingtoneSettingsToDefault(); + assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE)) .isEqualTo(DEFAULT_RINGTONE_VALUE); mSettingsHelper.restoreValue( mContext, - mMockContentResolver, + mContentResolver, new ContentValues(), Uri.EMPTY, Settings.System.RINGTONE, sourceRingtoneValue, 0); - assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE)) .isEqualTo(newRingtoneValueCanonicalized); } @@ -417,9 +405,6 @@ public class SettingsHelperTest { final String newRingtoneValueCanonicalized = "content://0@media/external/audio/media/100?title=Song&canonical=1"; - MockContentResolver mMockContentResolver = new MockContentResolver(); - when(mContext.getContentResolver()).thenReturn(mMockContentResolver); - MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID}); cursor.addRow(new Object[] {100L}); @@ -458,24 +443,21 @@ public class SettingsHelperTest { } }; - ContentProvider mockSettingsContentProvider = - new MockSettingsProvider(mContext, getContentResolver()); - mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider); + mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); + mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - resetRingtoneSettingsToDefault(mMockContentResolver); + resetRingtoneSettingsToDefault(); mSettingsHelper.restoreValue( mContext, - mMockContentResolver, + mContentResolver, new ContentValues(), Uri.EMPTY, Settings.System.RINGTONE, sourceRingtoneValue, 0); - assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE)) .isEqualTo(newRingtoneValueCanonicalized); } @@ -488,9 +470,6 @@ public class SettingsHelperTest { final String newRingtoneValueCanonicalized = "content://0@media/external/audio/media/200?title=notificationPing&canonicalize=1"; - MockContentResolver mMockContentResolver = new MockContentResolver(); - when(mContext.getContentResolver()).thenReturn(mMockContentResolver); - MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID}); cursor.addRow(new Object[] {200L}); @@ -529,17 +508,14 @@ public class SettingsHelperTest { } }; - ContentProvider mockSettingsContentProvider = - new MockSettingsProvider(mContext, getContentResolver()); - mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider); + mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); + mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - resetRingtoneSettingsToDefault(mMockContentResolver); + resetRingtoneSettingsToDefault(); mSettingsHelper.restoreValue( mContext, - mMockContentResolver, + mContentResolver, new ContentValues(), Uri.EMPTY, Settings.System.NOTIFICATION_SOUND, @@ -548,7 +524,7 @@ public class SettingsHelperTest { assertThat( Settings.System.getString( - mMockContentResolver, Settings.System.NOTIFICATION_SOUND)) + mContentResolver, Settings.System.NOTIFICATION_SOUND)) .isEqualTo(newRingtoneValueCanonicalized); } @@ -561,9 +537,6 @@ public class SettingsHelperTest { final String newRingtoneValueCanonicalized = "content://0@media/external/audio/media/300?title=alarmSound&canonical=1"; - MockContentResolver mMockContentResolver = new MockContentResolver(); - when(mContext.getContentResolver()).thenReturn(mMockContentResolver); - MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID}); cursor.addRow(new Object[] {300L}); @@ -600,26 +573,29 @@ public class SettingsHelperTest { assertThat(selectionArgs).isEqualTo(new String[] {"alarmSound"}); return cursor; } + + @Override + public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, + Bundle opts) { + return null; + } }; - ContentProvider mockSettingsContentProvider = - new MockSettingsProvider(mContext, getContentResolver()); - mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider); + mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); + mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - resetRingtoneSettingsToDefault(mMockContentResolver); + resetRingtoneSettingsToDefault(); mSettingsHelper.restoreValue( mContext, - mMockContentResolver, + mContentResolver, new ContentValues(), Uri.EMPTY, Settings.System.ALARM_ALERT, sourceRingtoneValue, 0); - assertThat(Settings.System.getString(mMockContentResolver, Settings.System.ALARM_ALERT)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT)) .isEqualTo(newRingtoneValueCanonicalized); } @@ -628,9 +604,6 @@ public class SettingsHelperTest { final String sourceRingtoneValue = "content://0@media/external/audio/media/1?title=Song&canonical=1"; - MockContentResolver mMockContentResolver = new MockContentResolver(); - when(mContext.getContentResolver()).thenReturn(mMockContentResolver); - // This is to mock the case that there are multiple results by querying title + // ringtone_type. MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID}); @@ -651,32 +624,26 @@ public class SettingsHelperTest { } }; - ContentProvider mockSettingsContentProvider = - new MockSettingsProvider(mContext, getContentResolver()); - mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider); + mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); + mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider); - resetRingtoneSettingsToDefault(mMockContentResolver); + resetRingtoneSettingsToDefault(); mSettingsHelper.restoreValue( mContext, - mMockContentResolver, + mContentResolver, new ContentValues(), Uri.EMPTY, Settings.System.RINGTONE, sourceRingtoneValue, 0); - assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE)) .isEqualTo(DEFAULT_RINGTONE_VALUE); } @Test public void testRestoreValue_customRingtone_restoreSilentValue() { - MockContentResolver mMockContentResolver = new MockContentResolver(); - when(mContext.getContentResolver()).thenReturn(mMockContentResolver); - ContentProvider mockMediaContentProvider = new MockContentProvider(mContext) { @Override @@ -691,37 +658,46 @@ public class SettingsHelperTest { } }; - ContentProvider mockSettingsContentProvider = - new MockSettingsProvider(mContext, getContentResolver()); - mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider); + mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); - resetRingtoneSettingsToDefault(mMockContentResolver); + resetRingtoneSettingsToDefault(); mSettingsHelper.restoreValue( mContext, - mMockContentResolver, + mContentResolver, new ContentValues(), Uri.EMPTY, Settings.System.RINGTONE, "_silent", 0); - assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE)) .isEqualTo(null); } - public static class MockSettingsProvider extends MockContentProvider { - ContentResolver mBaseContentResolver; - - public MockSettingsProvider(Context context, ContentResolver baseContentResolver) { + private static class MockSettingsProvider extends MockContentProvider { + private final ArrayMap<String, String> mKeyValueStore = new ArrayMap<>(); + MockSettingsProvider(Context context) { super(context); - this.mBaseContentResolver = baseContentResolver; } @Override public Bundle call(String method, String request, Bundle args) { - return mBaseContentResolver.call(Settings.AUTHORITY, method, request, args); + if (method.startsWith("PUT_")) { + mKeyValueStore.put(request, args.getString("value")); + return null; + } else if (method.startsWith("GET_")) { + return Bundle.forPair("value", mKeyValueStore.getOrDefault(request, "")); + } + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + String name = values.getAsString("name"); + String value = values.getAsString("value"); + mKeyValueStore.put(name, value); + return null; } } @@ -752,15 +728,13 @@ public class SettingsHelperTest { } private int getAutoRotationSettingValue() { - return Settings.System.getInt( - getContentResolver(), + return Settings.System.getInt(mContentResolver, Settings.System.ACCELEROMETER_ROTATION, /* default= */ -1); } private void setAutoRotationSettingValue(int value) { - Settings.System.putInt( - getContentResolver(), + Settings.System.putInt(mContentResolver, Settings.System.ACCELEROMETER_ROTATION, value ); @@ -769,7 +743,7 @@ public class SettingsHelperTest { private void restoreAutoRotationSetting(int newValue) { mSettingsHelper.restoreValue( mContext, - getContentResolver(), + mContentResolver, new ContentValues(), /* destination= */ Settings.System.CONTENT_URI, /* name= */ Settings.System.ACCELEROMETER_ROTATION, @@ -777,31 +751,19 @@ public class SettingsHelperTest { /* restoredFromSdkInt= */ 0); } - private ContentResolver getContentResolver() { - return InstrumentationRegistry.getInstrumentation().getTargetContext() - .getContentResolver(); - } - - private void clearLongPressPowerValues() { - ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getContentResolver(); - Settings.Global.putString(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, null); - Settings.Global.putString(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, null); - } - - private void resetRingtoneSettingsToDefault(ContentResolver contentResolver) { + private void resetRingtoneSettingsToDefault() { Settings.System.putString( - contentResolver, Settings.System.RINGTONE, DEFAULT_RINGTONE_VALUE); + mContentResolver, Settings.System.RINGTONE, DEFAULT_RINGTONE_VALUE); Settings.System.putString( - contentResolver, Settings.System.NOTIFICATION_SOUND, DEFAULT_NOTIFICATION_VALUE); + mContentResolver, Settings.System.NOTIFICATION_SOUND, DEFAULT_NOTIFICATION_VALUE); Settings.System.putString( - contentResolver, Settings.System.ALARM_ALERT, DEFAULT_ALARM_VALUE); + mContentResolver, Settings.System.ALARM_ALERT, DEFAULT_ALARM_VALUE); - assertThat(Settings.System.getString(contentResolver, Settings.System.RINGTONE)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE)) .isEqualTo(DEFAULT_RINGTONE_VALUE); - assertThat(Settings.System.getString(contentResolver, Settings.System.NOTIFICATION_SOUND)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.NOTIFICATION_SOUND)) .isEqualTo(DEFAULT_NOTIFICATION_VALUE); - assertThat(Settings.System.getString(contentResolver, Settings.System.ALARM_ALERT)) + assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT)) .isEqualTo(DEFAULT_ALARM_VALUE); } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index 889c026a0568..dd71dfa0b008 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -48,6 +48,7 @@ fun SceneScope.Notifications( Column( modifier = modifier + .element(key = Notifications.Elements.Notifications) .fillMaxWidth() .defaultMinSize(minHeight = 300.dp) .clip(RoundedCornerShape(32.dp)) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt index 38712b01ae44..291617f8edde 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt @@ -1,11 +1,12 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween +import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.TransitionBuilder import com.android.systemui.scene.ui.composable.QuickSettings fun TransitionBuilder.goneToQuickSettingsTransition() { spec = tween(durationMillis = 500) - fade(QuickSettings.rootElementKey) + translate(QuickSettings.rootElementKey, Edge.Top, true) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt index 1d57c1a377e5..45df2b1bb20c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt @@ -1,11 +1,12 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween +import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.TransitionBuilder import com.android.systemui.scene.ui.composable.Shade fun TransitionBuilder.goneToShadeTransition() { spec = tween(durationMillis = 500) - fade(Shade.rootElementKey) + translate(Shade.rootElementKey, Edge.Top, true) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt index 9a8a3e2048d1..e63bc4e458eb 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt @@ -1,11 +1,12 @@ package com.android.systemui.scene.ui.composable.transitions import androidx.compose.animation.core.tween +import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.TransitionBuilder import com.android.systemui.scene.ui.composable.QuickSettings fun TransitionBuilder.lockscreenToQuickSettingsTransition() { spec = tween(durationMillis = 500) - fade(QuickSettings.rootElementKey) + translate(QuickSettings.rootElementKey, Edge.Top, true) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt index 6c7964b30989..21a10b1bc936 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt @@ -10,6 +10,5 @@ fun TransitionBuilder.shadeToQuickSettingsTransition() { spec = tween(durationMillis = 500) translate(Notifications.Elements.Notifications, Edge.Bottom) - fade(Notifications.Elements.Notifications) timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 798bdec49f11..ab0225d63ac5 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -409,6 +409,18 @@ open class ClockRegistry( scope.launch(bgDispatcher) { mutateSetting { it.copy(seedColor = value) } } } + // Returns currentClockId if clock is connected, otherwise DEFAULT_CLOCK_ID. Since this + // is dependent on which clocks are connected, it may change when a clock is installed or + // removed from the device (unlike currentClockId). + // TODO: Merge w/ CurrentClockId when we convert to a flow. We shouldn't need both behaviors. + val activeClockId: String + get() { + if (!availableClocks.containsKey(currentClockId)) { + return DEFAULT_CLOCK_ID + } + return currentClockId + } + init { // Register default clock designs for (clock in defaultClockProvider.getClocks()) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index e539c955a3c6..b28920c590c5 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -62,9 +62,10 @@ class DefaultClockController( private val burmeseLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese) private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale) + protected var onSecondaryDisplay: Boolean = false override val events: DefaultClockEvents - override val config = ClockConfig() + override val config = ClockConfig(DEFAULT_CLOCK_ID) init { val parent = FrameLayout(ctx) @@ -142,6 +143,11 @@ class DefaultClockController( view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizePx) recomputePadding(targetRegion) } + + override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) { + this@DefaultClockController.onSecondaryDisplay = onSecondaryDisplay + recomputePadding(null) + } } open fun recomputePadding(targetRegion: Rect?) {} @@ -182,13 +188,19 @@ class DefaultClockController( override fun recomputePadding(targetRegion: Rect?) { // We center the view within the targetRegion instead of within the parent // view by computing the difference and adding that to the padding. - val parent = view.parent - val yDiff = - if (targetRegion != null && parent is View && parent.isLaidOut()) - targetRegion.centerY() - parent.height / 2f - else 0f val lp = view.getLayoutParams() as FrameLayout.LayoutParams - lp.topMargin = (-0.5f * view.bottom + yDiff).toInt() + lp.topMargin = + if (onSecondaryDisplay) { + // On the secondary display we don't want any additional top/bottom margin. + 0 + } else { + val parent = view.parent + val yDiff = + if (targetRegion != null && parent is View && parent.isLaidOut()) + targetRegion.centerY() - parent.height / 2f + else 0f + (-0.5f * view.bottom + yDiff).toInt() + } view.setLayoutParams(lp) } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index d962732ba884..e2f4793b8f91 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -177,6 +177,9 @@ interface ClockFaceEvents { * targetRegion is relative to the parent view. */ fun onTargetRegionChanged(targetRegion: Rect?) + + /** Called to notify the clock about its display. */ + fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) } /** Tick rates for clocks */ @@ -196,6 +199,8 @@ data class ClockMetadata( /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ data class ClockConfig( + val id: String, + /** Transition to AOD should move smartspace like large clock instead of small clock */ val useAlternateSmartspaceAODTransition: Boolean = false, diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java index cf7d2c57923c..3d9645a3d983 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java @@ -58,9 +58,26 @@ public interface VolumeDialogController { void userActivity(); void getState(); - boolean areCaptionsEnabled(); - void setCaptionsEnabled(boolean isEnabled); - + /** + * Get Captions enabled state + * + * @param checkForSwitchState set true when we'd like to switch captions enabled state after + * getting the latest captions state. + */ + void getCaptionsEnabledState(boolean checkForSwitchState); + + /** + * Set Captions enabled state + * + * @param enabled the captions enabled state we'd like to update. + */ + void setCaptionsEnabledState(boolean enabled); + + /** + * Get Captions component state + * + * @param fromTooltip if it's triggered from tooltip. + */ void getCaptionsComponentState(boolean fromTooltip); @ProvidesInterface(version = StreamState.VERSION) @@ -192,7 +209,22 @@ public interface VolumeDialogController { void onScreenOff(); void onShowSafetyWarning(int flags); void onAccessibilityModeChanged(Boolean showA11yStream); + + /** + * Callback function for captions component state changed event + * + * @param isComponentEnabled the lateset captions component state. + * @param fromTooltip if it's triggered from tooltip. + */ void onCaptionComponentStateChanged(Boolean isComponentEnabled, Boolean fromTooltip); + + /** + * Callback function for captions enabled state changed event + * + * @param isEnabled the lateset captions enabled state. + * @param checkBeforeSwitch intend to switch captions enabled state after the callback. + */ + void onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkBeforeSwitch); // requires version 2 void onShowCsdWarning(@AudioManager.CsdWarning int csdWarning, int durationMs); } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml new file mode 100644 index 000000000000..593f507f3c88 --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +** +** Copyright 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. +*/ +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/presentation" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <com.android.keyguard.KeyguardStatusView + android:id="@+id/clock" + android:layout_width="410dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:orientation="vertical"> + + <include + android:id="@+id/keyguard_clock_container" + layout="@layout/keyguard_clock_switch" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </com.android.keyguard.KeyguardStatusView> + +</FrameLayout> diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml index fc92e017fbcd..2eb1bb5dd941 100644 --- a/packages/SystemUI/res-keyguard/values-af/strings.xml +++ b/packages/SystemUI/res-keyguard/values-af/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN word vereis ná vassluit"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Wagwoord word vereis ná vassluit"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Patroon word vereis ná vassluit"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Die opdatering sal installeer wanneer die toestel nie gebruik word nie"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Meer sekuriteit vereis. PIN ruk lank nie gebruik nie."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Meer sekuriteit vereis. Wagwoord ruk lank nie gebruik nie."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Meer sekuriteit vereis. Patroon ruk lank nie gebruik nie."</string> diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml index 88670cdccf3d..5fd946b1f188 100644 --- a/packages/SystemUI/res-keyguard/values-am/strings.xml +++ b/packages/SystemUI/res-keyguard/values-am/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ከመቆለፊያ በኋላ ፒን ያስፈልጋል"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ከመቆለፊያ በኋላ የይለፍ ቃል ያስፈልጋል"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ከመቆለፊያ በኋላ ስርዓተ ጥለት ያስፈልጋል"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"መሣሪያው ጥቅም ላይ በማይውልበት ጊዜ ዝማኔ ይጫናል"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ተጨማሪ የደህንነት ጥበቃ ያስፈልጋል። ፒን ለተወሰነ ጊዜ ጥቅም ላይ አልዋለም።"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ተጨማሪ የደህንነት ጥበቃ ያስፈልጋል። የይለፍ ቃል ለተወሰነ ጊዜ ጥቅም ላይ አልዋለም።"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ተጨማሪ የደህንነት ጥበቃ ያስፈልጋል። ስርዓተ ጥለት ለተወሰነ ጊዜ ጥቅም ላይ አልዋለም።"</string> diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index b66f6fd79cd8..b6479f456a2f 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"يجب إدخال رقم التعريف الشخصي بعد إلغاء التأمين."</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"يجب إدخال كلمة المرور بعد إلغاء التأمين."</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"يجب رسم النقش بعد إلغاء التأمين."</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"سيتم تثبيت التحديث عندما لا يكون الجهاز قيد الاستخدام."</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"يجب تعزيز الأمان. لم يُستخدَم رقم PIN لبعض الوقت."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"يجب تعزيز الأمان. لم تستخدَم كلمة المرور لبعض الوقت."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"يجب تعزيز الأمان. لم يُستخدَم النقش لبعض الوقت."</string> diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml index 67967560a47f..a41a704f345a 100644 --- a/packages/SystemUI/res-keyguard/values-as/strings.xml +++ b/packages/SystemUI/res-keyguard/values-as/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"লকডাউনৰ পাছত পিন দিয়াৰ আৱশ্যক"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"লকডাউনৰ পাছত পাছৱৰ্ড দিয়াৰ আৱশ্যক"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"লকডাউনৰ পাছত আৰ্হি দিয়াৰ আৱশ্যক"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ডিভাইচটো ব্যৱহাৰ হৈ নাথাকোঁতে আপডে’ট ইনষ্টল হ’ব"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"অতিৰিক্ত সুৰক্ষাৰ আৱশ্যক। কিছু সময় ধৰি আৰ্হি ব্যৱহাৰ কৰা হোৱা নাই।"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"অতিৰিক্ত সুৰক্ষাৰ আৱশ্যক। কিছু সময় ধৰি পাছৱৰ্ড ব্যৱহাৰ কৰা হোৱা নাই।"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"অতিৰিক্ত সুৰক্ষাৰ আৱশ্যক। কিছু সময় ধৰি আৰ্হি ব্যৱহাৰ কৰা হোৱা নাই।"</string> diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml index d8cf6c08c985..f66fb3c9dc94 100644 --- a/packages/SystemUI/res-keyguard/values-az/strings.xml +++ b/packages/SystemUI/res-keyguard/values-az/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Kilidləmədən sonra PIN tələb edilir"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Kilidləmədən sonra parol tələb edilir"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Kilidləmədən sonra model tələb edilir"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Cihaz istifadə edilmədikdə güncəllənmə quraşdırılacaq"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Qoruma lazımdır. PIN bir müddət işlənməyib."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Qoruma lazımdır. Parol bir müddət işlənməyib."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Qoruma lazımdır. Model bir müddət işlənməyib."</string> diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml index 72067e7bc098..b0a647199416 100644 --- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN je obavezan posle zaključavanja"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Lozinka je obavezna posle zaključavanja"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Šablon je obavezan posle zaključavanja"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Ažuriranje će se instalirati kada se uređaj ne koristi"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Potrebna je dodatna zaštita. PIN dugo nije korišćen."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Potrebna je dodatna zaštita. Lozinka dugo nije korišćena."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Potrebna je dodatna zaštita. Šablon dugo nije korišćen."</string> diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml index 12c693f43d6a..11cc77dca5da 100644 --- a/packages/SystemUI/res-keyguard/values-be/strings.xml +++ b/packages/SystemUI/res-keyguard/values-be/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Пасля блакіроўкі неабходна ўвесці PIN-код"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Пасля блакіроўкі неабходна ўвесці пароль"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Пасля блакіроўкі неабходна ўвесці ўзор разблакіроўкі"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Абнаўленне будзе ўсталявана, калі прылада не выкарыстоўваецца"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Патрабуецца дадатковая праверка. Даўно не выкарыстоўваўся PIN-код."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Патрабуецца дадатковая праверка. Даўно не выкарыстоўваўся пароль."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Патрабуецца дадатковая праверка. Даўно не выкарыстоўваўся ўзор разблакіроўкі."</string> diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml index 6726d42526ec..c554a274f466 100644 --- a/packages/SystemUI/res-keyguard/values-bg/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"След заключването се изисква ПИН код"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"След заключването се изисква парола"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"След заключването се изисква фигура"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Актуализацията ще се инсталира, когато устройството не се използва"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Изисква се допъл. защита. ПИН кодът не е ползван скоро."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Изисква се допъл. защита. Паролата не е ползвана скоро."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Изисква се допъл. защита. Фигурата не е ползвана скоро."</string> diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index 457f85da6ee5..69f533c3bd47 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"লকডাউন হওয়ার পরে পিন দিতে হবে"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"লকডাউন হওয়ার পরে পাসওয়ার্ড দিতে হবে"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"লকডাউন হওয়ার পরে প্যাটার্ন দিতে হবে"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ডিভাইস ব্যবহার না করাকালীন আপডেট ইনস্টল করা হবে"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"অতিরিক্ত সুরক্ষা দরকার। পিন কিছুক্ষণ ব্যবহার করা হয়নি।"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"অতিরিক্ত সুরক্ষা দরকার। পাসওয়ার্ড কিছুক্ষণ ব্যবহার করা হয়নি।"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"অতিরিক্ত সুরক্ষা দরকার। প্যাটার্ন কিছুক্ষণ ব্যবহার করা হয়নি।"</string> diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml index 6dc147f6a109..4c519c8d5362 100644 --- a/packages/SystemUI/res-keyguard/values-bs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN je potreban nakon zaključavanja"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Lozinka je potrebna nakon zaključavanja"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Uzorak je potreban nakon zaključavanja"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Ažuriranje će se instalirati dok se uređaj ne koristi"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Potrebna je dodatna zaštita. PIN dugo nije unošen."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Potrebna je dodatna zaštita. Lozinka dugo nije unošena."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Potrebna je dodatna zaštita. Uzorak dugo nije unošen."</string> diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml index 8b901c0a490f..3bd65083d810 100644 --- a/packages/SystemUI/res-keyguard/values-ca/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Cal el PIN després del bloqueig de seguretat"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Cal la contrasenya després del bloqueig de seguretat"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Cal el patró després del bloqueig de seguretat"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"L\'actualització s\'instal·larà quan el dispositiu no estigui en ús"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Cal més seguretat. Fa temps que no utilitzes el PIN."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Cal més seguretat. Contrasenya no utilitzada fa temps."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Cal més seguretat. Fa temps que no utilitzes el patró."</string> diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml index b4c034315f1e..e075d850ba65 100644 --- a/packages/SystemUI/res-keyguard/values-cs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Po uzamčení je třeba zadat PIN"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Po uzamčení je třeba zadat heslo"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Po uzamčení je třeba zadat gesto"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Aktualizace se nainstaluje, až zařízení nebudete používat"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Je potřeba další krok. PIN dlouho nepoužit."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Je potřeba další krok. Heslo dlouho nepoužito."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Je potřeba další krok. Gesto dlouho nepoužito."</string> diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml index 3840785d3295..027166ff72c0 100644 --- a/packages/SystemUI/res-keyguard/values-da/strings.xml +++ b/packages/SystemUI/res-keyguard/values-da/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Pinkode er påkrævet efter brug af låsning"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Adgangskode er påkrævet efter brug af låsning"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Mønster er påkrævet efter brug af låsning"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Opdateringen installeres, når enheden ikke er i brug"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Mere sikkerhed er påkrævet. Pinkoden er ikke blevet brugt i et stykke tid."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Mere sikkerhed er påkrævet. Adgangskoden er ikke blevet brugt i et stykke tid."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Mere sikkerhed er påkrævet. Mønsteret er ikke blevet brugt i et stykke tid."</string> diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml index 5d069ff8c41d..117f7a92cf01 100644 --- a/packages/SystemUI/res-keyguard/values-de/strings.xml +++ b/packages/SystemUI/res-keyguard/values-de/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Nach einer Sperre muss die PIN eingegeben werden"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Nach einer Sperre muss das Passwort eingegeben werden"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Nach einer Sperre muss das Muster gezeichnet werden"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Update wird installiert, wenn das Gerät nicht verwendet wird"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Zusätzliche Sicherheitsmaßnahme erforderlich. Die PIN wurde länger nicht genutzt."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Zusätzliche Sicherheitsmaßnahme erforderlich. Passwort wurde länger nicht genutzt."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Zusätzliche Sicherheitsmaßnahme erforderlich. Muster wurde länger nicht genutzt."</string> diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml index f02be893d196..cd7637c1b1bd 100644 --- a/packages/SystemUI/res-keyguard/values-el/strings.xml +++ b/packages/SystemUI/res-keyguard/values-el/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Απαιτείται PIN μετά από κλείδωμα"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Απαιτείται κωδικός πρόσβασης μετά από κλείδωμα"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Απαιτείται μοτίβο μετά από κλείδωμα"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Η ενημέρωση θα εγκατασταθεί όταν δεν χρησιμοποιείται η συσκευή"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Απαιτ. πρόσθ. ασφάλ. Το PIN έχει καιρό να χρησιμοπ."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Απαιτ. πρόσθ. ασφάλ. Ο κωδ. πρ. έχει καιρό να χρησ."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Απαιτ. πρόσθ. ασφάλ. Το μοτίβο έχει καιρό να χρησιμ."</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml index ab7208b6f5d3..0ace8a739da8 100644 --- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN is required after lockdown"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Password is required after lockdown"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Pattern is required after lockdown"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Update will be installed when the device is not in use"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Added security required. PIN not used for a while."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Added security required. Password not used for a while."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Added security required. Pattern not used for a while."</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml index ab7208b6f5d3..0ace8a739da8 100644 --- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN is required after lockdown"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Password is required after lockdown"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Pattern is required after lockdown"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Update will be installed when the device is not in use"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Added security required. PIN not used for a while."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Added security required. Password not used for a while."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Added security required. Pattern not used for a while."</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml index ab7208b6f5d3..0ace8a739da8 100644 --- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN is required after lockdown"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Password is required after lockdown"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Pattern is required after lockdown"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Update will be installed when the device is not in use"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Added security required. PIN not used for a while."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Added security required. Password not used for a while."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Added security required. Pattern not used for a while."</string> diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml index 2aa9c0452009..debbeb127c2f 100644 --- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Se requiere el PIN después del bloqueo"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Se requiere la contraseña después del bloqueo"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Se requiere el patrón después del bloqueo"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Se instalará la actualización cuando el dispositivo esté en desuso"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Reforzar seguridad. PIN sin uso mucho tiempo."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Reforzar seguridad. Contraseña sin uso mucho tiempo."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Reforzar seguridad. Patrón sin uso mucho tiempo."</string> diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml index 78ecc8501322..0ea98a8be752 100644 --- a/packages/SystemUI/res-keyguard/values-es/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Se necesita el PIN después del bloqueo"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Se necesita la contraseña después del bloqueo"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Se necesita el patrón después del bloqueo"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"La actualización se instalará cuando el dispositivo no esté en uso"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Se debe reforzar la seguridad. PIN no usado en mucho tiempo."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Se debe reforzar la seguridad. Contraseña no usada en mucho tiempo."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Se debe reforzar la seguridad. Patrón no usado en mucho tiempo."</string> diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml index 26ead1fdbcf7..722a02237b3d 100644 --- a/packages/SystemUI/res-keyguard/values-et/strings.xml +++ b/packages/SystemUI/res-keyguard/values-et/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Pärast lukustamist on PIN-kood nõutav"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Pärast lukustamist on parool nõutav"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Pärast lukustamist on muster nõutav"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Värskendus installitakse, kui seadet ei kasutata"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Turvalisuse suurendamine on nõutav. PIN-koodi ei ole mõnda aega kasutatud."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Turvalisuse suurendamine on nõutav. Parooli ei ole mõnda aega kasutatud."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Turvalisuse suurendamine on nõutav. Mustrit ei ole mõnda aega kasutatud."</string> diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index 52d23363785b..d3293696ec58 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PINa behar da blokeoa desgaitu ostean"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Pasahitza behar da blokeoa desgaitu ostean"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Eredua behar da blokeoa desgaitu ostean"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Gailua erabiltzen ari ez zarenean instalatuko da eguneratzea"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Segurtasuna areagotu behar da. PINa ez da erabili aldi batez."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Segurtasuna areagotu behar da. Pasahitza ez da erabili aldi batez."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Segurtasuna areagotu behar da. Eredua ez da erabili aldi batez."</string> diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index 1e7978e6a894..4815815a1dc9 100644 --- a/packages/SystemUI/res-keyguard/values-fa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"بعداز قفل همه باید از پین استفاده کرد"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"بعداز قفل همه باید از گذرواژه استفاده کرد"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"بعداز قفل همه باید از الگو استفاده کرد"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"بهروزرسانی وقتی دستگاه درحال استفاده نیست نصب خواهد شد"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"امنیت بیشتر لازم است. مدتی از پین استفاده نشده است."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"امنیت بیشتر لازم است. گذرواژه مدتی استفاده نشده است."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"امنیت بیشتر لازم است. مدتی از الگو استفاده نشده است."</string> diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml index 2ec0c995c57f..02d41d8a22be 100644 --- a/packages/SystemUI/res-keyguard/values-fi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN-koodi tarvitaan lukitustilan jälkeen"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Salasana tarvitaan lukitustilan jälkeen"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Kuvio tarvitaan lukitustilan jälkeen"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Päivitys asennetaan, kun laite ei ole käytössä"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Lisäsuojausta tarvitaan. PIN-koodia ei ole käytetty."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Lisäsuojausta tarvitaan. Salasanaa ei ole käytetty."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Lisäsuojausta tarvitaan. Kuviota ei ole käytetty."</string> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index b0bfa3333212..fa1a191b8944 100644 --- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Le NIP est requis après le verrouillage"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Le mot de passe est requis après le verrouillage"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Le schéma est requis après le verrouillage"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"La mise à jour sera installée lorsque l\'appareil n\'est pas utilisé"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Plus de sécurité requise. NIP non utilisé pour un temps."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Plus de sécurité requise. MDP non utilisé pour un temps."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Plus de sécurité requise. Schéma non utilisé pour un temps."</string> diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml index 5161832a18fb..d687a1d7e9a2 100644 --- a/packages/SystemUI/res-keyguard/values-fr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Code requis après un blocage"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Mot de passe requis après un blocage"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Schéma requis après un blocage"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"La mise à jour aura lieu lorsque votre appareil n\'est pas utilisé"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Code inutilisé depuis un moment. Renforcez la sécurité."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Mot de passe inutilisé depuis un moment. Renforcez la sécurité."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Schéma inutilisé depuis un moment. Renforcez la sécurité."</string> diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml index e33a899c3b75..3faa7ca0d5fa 100644 --- a/packages/SystemUI/res-keyguard/values-gl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Requírese o PIN tras o bloqueo"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Requírese o contrasinal tras o bloqueo"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Requírese o padrón tras o bloqueo"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"A actualización instalarase cando o dispositivo non se estea usando"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Requírese seguranza adicional. O PIN non se usou desde hai tempo."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Requírese seguranza adicional. O contrasinal non se usou desde hai tempo."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Requírese seguranza adicional. O padrón non se usou desde hai tempo."</string> diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml index 1d806ca8f8a2..99c988363256 100644 --- a/packages/SystemUI/res-keyguard/values-gu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"પિન પછી પાસવર્ડ આવશ્યક છે"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"લૉકડાઉન પછી પાસવર્ડ આવશ્યક છે"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"પૅટર્ન પછી પાસવર્ડ આવશ્યક છે"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"જ્યારે ડિવાઇસ ઉપયોગમાં ન હોય ત્યારે અપડેટ ઇન્સ્ટૉલ કરવામાં આવશે"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"વધારાની સુરક્ષા આવશ્યક છે. થોડા સમય માટે પિનનો ઉપયોગ થયો નથી."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"વધારાની સુરક્ષા આવશ્યક છે. થોડા સમય માટે પાસવર્ડનો ઉપયોગ થયો નથી."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"વધારાની સુરક્ષા આવશ્યક છે. થોડા સમય માટે પૅટર્નનો ઉપયોગ થયો નથી."</string> diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml index 808950e72eac..9d32f04206dc 100644 --- a/packages/SystemUI/res-keyguard/values-hi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"लॉकडाउन के बाद, पिन डालना ज़रूरी है"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"लॉकडाउन के बाद, पासवर्ड डालना ज़रूरी है"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"लॉकडाउन के बाद, पैटर्न ड्रॉ करना ज़रूरी है"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"अपडेट तब इंस्टॉल किया जाएगा, जब डिवाइस का इस्तेमाल न किया जा रहा हो"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"अतिरिक्त सुरक्षा ज़रूरी है. कुछ समय से पिन नहीं डाला गया."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"अतिरिक्त सुरक्षा ज़रूरी है. कुछ समय से पासवर्ड नहीं डाला गया."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"अतिरिक्त सुरक्षा ज़रूरी है. कुछ समय से पैटर्न ड्रॉ नहीं किया गया."</string> diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml index 970ae5cde368..b4224bfbc497 100644 --- a/packages/SystemUI/res-keyguard/values-hr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN je obavezan nakon zaključavanja"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Zaporka je obavezna nakon zaključavanja"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Uzorak je obavezan nakon zaključavanja"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Ažuriranje će se instalirati kad uređaj nije u upotrebi"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Potrebna je dodatna sigurnost. PIN nije upotrijebljen duže vrijeme."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Potrebna je dodatna sigurnost. Zaporka nije upotrijebljena duže vrijeme."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Potrebna je dodatna sigurnost. Uzorak nije upotrijebljen duže vrijeme."</string> diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml index bc0a98de376d..bc712c7cc882 100644 --- a/packages/SystemUI/res-keyguard/values-hu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN-kód megadása szükséges a zárolás után"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Jelszó megadása szükséges a zárolás után"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Minta megadása szükséges a zárolás után"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"A frissítést telepíti a rendszer, amikor nincs használatban az eszköz"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Fokozott biztonság szükséges. Régóta nem használta a PIN-t."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Fokozott biztonság szükséges. Régóta nem használta jelszavát."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Fokozott biztonság szükséges. Régóta nem használta a mintát."</string> diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml index 230c6ba49575..4d7bbbef2a08 100644 --- a/packages/SystemUI/res-keyguard/values-hy/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Արգելափակումից հետո հարկավոր է մուտքագրել PIN կոդը"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Արգելափակումից հետո հարկավոր է մուտքագրել գաղտնաբառը"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Արգելափակումից հետո հարկավոր է գծել նախշը"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Թարմացումը կտեղադրվի, երբ սարքն օգտագործվելիս չլինի"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"PIN կոդը որոշ ժամանակ չի օգտագործվել։"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Գաղտնաբառը որոշ ժամանակ չի օգտագործվել։"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Նախշը որոշ ժամանակ չի օգտագործվել։"</string> diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml index cf01634c6620..aa766e963670 100644 --- a/packages/SystemUI/res-keyguard/values-in/strings.xml +++ b/packages/SystemUI/res-keyguard/values-in/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN diperlukan setelah kunci total"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Sandi diperlukan setelah kunci total"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Pola diperlukan setelah kunci total"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Update akan diinstal saat perangkat sedang tidak digunakan"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Perlu keamanan tambahan. PIN tidak digunakan selama beberapa waktu."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Perlu keamanan tambahan. Sandi tidak digunakan selama beberapa waktu."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Perlu keamanan tambahan. Pola tidak digunakan selama beberapa waktu."</string> diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml index ca364002c017..99f177939378 100644 --- a/packages/SystemUI/res-keyguard/values-is/strings.xml +++ b/packages/SystemUI/res-keyguard/values-is/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN-númers er krafist eftir læsingu"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Aðgangsorðs er krafist eftir læsingu"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Mynsturs er krafist eftir læsingu"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Uppfærslan verður sett upp þegar tækið er ekki í notkun"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Viðbótaröryggis krafist. PIN-númer var ekki notað um hríð."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Viðbótaröryggis krafist. Aðgangsorð var ekki notað um hríð."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Viðbótaröryggis krafist. Mynstur var ekki notað um hríð."</string> diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml index e46ae3743ff0..e5079c7fdaff 100644 --- a/packages/SystemUI/res-keyguard/values-it/strings.xml +++ b/packages/SystemUI/res-keyguard/values-it/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN richiesto dopo il blocco"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Password richiesta dopo il blocco"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Sequenza richiesta dopo il blocco"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"L\'aggiornamento verrà installato quando il dispositivo non sarà in uso"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Occorre maggiore sicurezza. PIN non usato per un po\' di tempo."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Occorre maggiore sicurezza. Password non usata per un po\' di tempo."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Occorre maggiore sicurezza. Sequenza non usata per un po\' di tempo."</string> diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml index 667bf9bf3841..bc663559a770 100644 --- a/packages/SystemUI/res-keyguard/values-iw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"נדרש קוד אימות לאחר הפעלת \'ללא \'ביטול נעילה בטביעת אצבע\'\'"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"נדרשת סיסמה לאחר הפעלת \'ללא \'ביטול נעילה בטביעת אצבע\'\'"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"נדרש קו ביטול נעילה לאחר הפעלת \'ללא \'ביטול נעילה בטביעת אצבע\'\'"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"העדכון יותקן כשהמכשיר לא יהיה בשימוש"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"נדרשת אבטחה מוגברת. לא השתמשת בקוד אימות זמן מה."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"נדרשת אבטחה מוגברת. לא השתמשת בסיסמה זמן מה."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"נדרשת אבטחה מוגברת. לא השתמשת בקו ביטול נעילה זמן מה."</string> diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml index 52309849d1f5..1d59a630b8c2 100644 --- a/packages/SystemUI/res-keyguard/values-ja/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ロックダウン後は PIN の入力が必要になります"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ロックダウン後はパスワードの入力が必要になります"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ロックダウン後はパターンの入力が必要になります"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"アップデートは、デバイスを使用していないときにインストールされます"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"セキュリティ強化が必要: PIN がしばらく未使用です。"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"セキュリティ強化が必要: パスワードがしばらく未使用です。"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"セキュリティ強化が必要: パターンがしばらく未使用です。"</string> diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml index 04a7b0d4e7e9..5bd6b2e3d883 100644 --- a/packages/SystemUI/res-keyguard/values-ka/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"დაბლოკვის შემდეგ საჭიროა PIN-კოდი"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"დაბლოკვის შემდეგ საჭიროა პაროლი"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"დაბლოკვის შემდეგ საჭიროა ნიმუში"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"განახლება დაინსტალირდება, როცა მოწყობილობა არ გამოიყენება"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"საჭიროა დამ. უსაფრთ. PIN-კოდი ერთხანს არ გამოიყენ."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"საჭ. დამ. უსაფრთ. პაროლი ერთხანს არ გამოიყენება."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"საჭიროა დამ. უსაფრთ. ნიმუში ერთხანს არ გამოიყენება."</string> diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml index deab7b846911..83d270ded60c 100644 --- a/packages/SystemUI/res-keyguard/values-kk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Құлыпталғаннан кейін PIN кодын енгізу қажет."</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Құлыпталғаннан кейін құпия сөз енгізу қажет."</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Құлыпталғаннан кейін өрнек енгізу қажет."</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Құрылғы қолданылмағанда жаңартылады."</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Қауіпсіздікті күшейту қажет. PIN коды біраз уақыт қолданылмаған."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Қауіпсіздікті күшейту қажет. Құпия сөз біраз уақыт қолданылмаған."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Қауіпсіздікті күшейту қажет. Өрнек біраз уақыт қолданылмаған."</string> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 13251513f2f7..5306cb1ff4e6 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ត្រូវការកូដ PIN បន្ទាប់ពីការចាក់សោ"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ត្រូវការពាក្យសម្ងាត់បន្ទាប់ពីការចាក់សោ"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ត្រូវការលំនាំបន្ទាប់ពីការចាក់សោ"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"កំណែថ្មីនឹងត្រូវបានដំឡើង នៅពេលឧបករណ៍មិនជាប់ប្រើ"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ត្រូវការសុវត្ថិភាពបន្ថែម។ មិនបានប្រើកូដ PIN មួយរយៈ។"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ត្រូវការសុវត្ថិភាពបន្ថែម។ មិនបានប្រើពាក្យសម្ងាត់មួយរយៈ។"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ត្រូវការសុវត្ថិភាពបន្ថែម។ មិនបានប្រើលំនាំមួយរយៈ។"</string> diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml index 749ebb6bd38a..d609a23d2c87 100644 --- a/packages/SystemUI/res-keyguard/values-kn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ಲಾಕ್ಡೌನ್ ಮಾಡಿದ ನಂತರ ಪಿನ್ ಬಳಸುವ ಅಗತ್ಯವಿದೆ"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ಲಾಕ್ಡೌನ್ನ ನಂತರ ಪಾಸ್ವರ್ಡ್ನ ಅಗತ್ಯವಿದೆ"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ಲಾಕ್ಡೌನ್ ಮಾಡಿದ ನಂತರ ಪ್ಯಾಟರ್ನ್ ಬಳಸುವ ಅಗತ್ಯವಿದೆ"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ಸಾಧನವನ್ನು ಬಳಕೆ ಮಾಡದ ಸಮಯದಲ್ಲಿ ಅಪ್ಡೇಟ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗುತ್ತದೆ"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ಹೆಚ್ಚುವರಿ ಸುರಕ್ಷತೆಯ ಅಗತ್ಯವಿದೆ. ಪಿನ್ ಅನ್ನು ಸ್ವಲ್ಪ ಕಾಲದಿಂದ ಬಳಸಿಲ್ಲ."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ಹೆಚ್ಚುವರಿ ಸುರಕ್ಷತೆಯ ಅಗತ್ಯವಿದೆ. ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಸ್ವಲ್ಪ ಕಾಲದಿಂದ ಬಳಸಿಲ್ಲ."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ಹೆಚ್ಚುವರಿ ಸುರಕ್ಷತೆಯ ಅಗತ್ಯವಿದೆ. ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ಸ್ವಲ್ಪ ಕಾಲದಿಂದ ಬಳಸಿಲ್ಲ."</string> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index ae75286fcb13..532253e5d3c3 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"기기가 잠겨 PIN을 입력해야 합니다."</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"기기가 잠겨 비밀번호를 입력해야 합니다."</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"기기가 잠겨 패턴을 입력해야 합니다."</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"기기를 사용하지 않을 때 업데이트가 설치됩니다."</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"보안을 강화해야 합니다. 한동안 PIN이 사용되지 않았습니다."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"보안을 강화해야 합니다. 한동안 비밀번호가 사용되지 않았습니다."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"보안을 강화해야 합니다. 한동안 패턴이 사용되지 않았습니다."</string> diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index 638fd9838ed5..9ad9d560394f 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Бекем кулпулангандан кийин PIN код талап кылынат"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Бекем кулпулангандан кийин сырсөз талап кылынат"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Бекем кулпулангандан кийн грфикалык ачкыч талп клынт"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Жаңыртуу түзмөк колдонулбай турганда орнотулат"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Кошмча кпсуздук тлап клнат. PIN код бир нче убкыт бою клднулгн эмeс."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Кошмча кпсуздук тлап клнат. Сырсз бир нче убкыт бою клднулгн эмeс."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Кошмча кпсуздук тлап клнат. Грфиклык ачкч бир нче убкыт бою клднулгн эмeс."</string> diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml index efe53773bc0d..0059d7fbacee 100644 --- a/packages/SystemUI/res-keyguard/values-lo/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ຕ້ອງໃສ່ PIN ຫຼັງຈາກທີ່ລັອກໄວ້"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ຕ້ອງໃສ່ລະຫັດຜ່ານຫຼັງຈາກທີ່ລັອກໄວ້"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ຕ້ອງແຕ້ມຮູບແບບຫຼັງຈາກທີ່ລັອກໄວ້"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ລະບົບຈະຕິດຕັ້ງການອັບເດດເມື່ອບໍ່ມີການນຳໃຊ້ອຸປະກອນ"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ຕ້ອງເພີ່ມຄວາມປອດໄພ. ບໍ່ໄດ້ໃຊ້ PIN ມາໄລຍະໜຶ່ງແລ້ວ."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ຕ້ອງເພີ່ມຄວາມປອດໄພ. ບໍ່ໄດ້ໃຊ້ລະຫັດຜ່ານມາໄລຍະໜຶ່ງແລ້ວ."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ຕ້ອງເພີ່ມຄວາມປອດໄພ. ບໍ່ໄດ້ໃຊ້ຮູບແບບມາໄລຍະໜຶ່ງແລ້ວ."</string> diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml index 59f0aa3be487..01e2f8820700 100644 --- a/packages/SystemUI/res-keyguard/values-lt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Po užrakinimo reikalingas PIN kodas"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Po užrakinimo reikalingas slaptažodis"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Po užrakinimo reikalingas atrakinimo piešinys"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Naujinys bus įdiegtas, kai įrenginys nebus naudojamas"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Reikalinga papildoma sauga. PIN kodas nebuvo naudojamas kurį laiką."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Reikalinga papildoma sauga. Slaptažodis nebuvo naudojamas kurį laiką."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Reikalinga papildoma sauga. Atrakinimo piešinys nebuvo naudojamas kurį laiką."</string> diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml index ee9e8c2ba50a..2133694f99a8 100644 --- a/packages/SystemUI/res-keyguard/values-lv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Pēc bloķēšanas ir jāievada PIN"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Pēc bloķēšanas ir jāievada parole"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Pēc bloķēšanas ir jāzīmē kombinācija"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Atjauninājums tiks instalēts, kad ierīce netiks izmantota."</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Jāveic papildu drošības darbība. PIN ilgu laiku nav lietots."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Jāveic papildu drošības darbība. Parole ilgu laiku nav lietota."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Jāveic papildu drošības darbība. Kombinācija ilgu laiku nav lietota."</string> diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml index a869d605286a..2771c7f5b5d6 100644 --- a/packages/SystemUI/res-keyguard/values-mk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Потребен е PIN-код по заклучување"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Потребна е лозинка по заклучување"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Потребна е шема по заклучување"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Ажурирањето ќе се инсталира кога нема да се користи уредот"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Потребна е дополнителна безбедност. PIN-кодот не бил користен некое време."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Потребна е дополнителна безбедност. Лозинката не била користена некое време."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Потребна е дополнителна безбедност. Шемата не била користена некое време."</string> diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml index e247cc7bda56..02ee66fc83b4 100644 --- a/packages/SystemUI/res-keyguard/values-ml/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ലോക്ക്ഡൗണിന് ശേഷം പിൻ നൽകേണ്ടതുണ്ട്"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ലോക്ക്ഡൗണിന് ശേഷം പാസ്വേഡ് നൽകേണ്ടതുണ്ട്"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ലോക്ക്ഡൗണിന് ശേഷം പാറ്റേൺ വരയ്ക്കേണ്ടതുണ്ട്"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ഉപകരണം ഉപയോഗിക്കാതിരിക്കുമ്പോൾ അപ്ഡേറ്റ് ഇൻസ്റ്റാൾ ആകും"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"അധിക സുരക്ഷ വേണം. അൽപകാലം പിൻ ഉപയോഗിച്ചില്ല."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"അധിക സുരക്ഷ വേണം. അൽപകാലം പാസ്വേഡ് ഉപയോഗിച്ചില്ല."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"അധിക സുരക്ഷ വേണം. അൽപകാലം പാറ്റേൺ ഉപയോഗിച്ചില്ല."</string> diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml index 7b555fb64839..2b9f81e9e358 100644 --- a/packages/SystemUI/res-keyguard/values-mn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Түгжсэний дараа ПИН шаардлагатай"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Түгжсэний дараа нууц үг шаардлагатай"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Түгжсэний дараа хээ шаардлагатай"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Төхөөрөмжийг ашиглаагүй үед шинэчлэлтийг суулгана"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Нэмэлт аюулгүй байдал шаардлагатай. ПИН-г хэсэг хугацаанд ашиглаагүй."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Нэмэлт аюулгүй байдал шаардлагатай. Нууц үгийг хэсэг хугацаанд ашиглаагүй."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Нэмэлт аюулгүй байдал шаардлагатай. Хээг хэсэг хугацаанд ашиглаагүй."</string> diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml index ada2f1167ff5..7aa7bdd26c08 100644 --- a/packages/SystemUI/res-keyguard/values-mr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"लॉकडाउननंतर पिन आवश्यक आहे"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"लॉकडाउननंतर पासवर्ड आवश्यक आहे"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"लॉकडाउननंतर पॅटर्न आवश्यक आहे"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"डिव्हाइस वापरात नसताना अपडेट इंस्टॉल केले जाईल"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"अतिरिक्त सुरक्षा आवश्यक आहे. काही वेळेसाठी पिन अनलॉक केला गेला नव्हता."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"अतिरिक्त सुरक्षा आवश्यक आहे. काही वेळेसाठी पासवर्ड अनलॉक केला गेला नव्हता."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"अतिरिक्त सुरक्षा आवश्यक आहे. काही वेळेसाठी पॅटर्न अनलॉक केला गेला नव्हता."</string> diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml index 60c45317bd6f..bdfa4a77eb8a 100644 --- a/packages/SystemUI/res-keyguard/values-ms/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN diperlukan selepas kunci semua"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Kata laluan diperlukan selepas kunci semua"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Corak diperlukan selepas kunci semua"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Kemaskinian akan dipasang apabila peranti tidak digunakan"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Keselamatan tambahan diperlukan. PIN tidak digunakan."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Keselamatan tambahan diperlukan. Kata laluan tidak digunakan."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Keselamatan tambahan diperlukan. Corak tidak digunakan."</string> diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml index 4092c9a02cee..e85cf8a602c3 100644 --- a/packages/SystemUI/res-keyguard/values-my/strings.xml +++ b/packages/SystemUI/res-keyguard/values-my/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"လော့ခ်ဒေါင်းလုပ်ပြီးနောက် ပင်နံပါတ်လိုအပ်သည်"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"လော့ခ်ဒေါင်းလုပ်ပြီးနောက် စကားဝှက်လိုအပ်သည်"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"လော့ခ်ဒေါင်းလုပ်ပြီးနောက် ပုံဖော်ခြင်းလိုအပ်သည်"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"စက်သုံးနေခြင်း မရှိချိန်တွင် အပ်ဒိတ် ထည့်သွင်းပါမည်"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ထပ်ဆောင်းလုံခြုံရေး လိုအပ်သည်။ ပင်နံပါတ်မသုံးသည်မှာ အနည်းငယ်ကြာပြီ။"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ထပ်ဆောင်းလုံခြုံရေး လိုအပ်သည်။ စကားဝှက်မသုံးသည်မှာ အနည်းငယ်ကြာပြီ။"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ထပ်ဆောင်းလုံခြုံရေး လိုအပ်သည်။ ပုံဖော်ခြင်းမသုံးသည်မှာ အနည်းငယ်ကြာပြီ။"</string> diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml index deb1421fcd78..455d08620fbb 100644 --- a/packages/SystemUI/res-keyguard/values-nb/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN-koden kreves etter låsing"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Passordet kreves etter låsing"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Mønsteret kreves etter låsing"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Oppdateringen installeres når enheten ikke brukes"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Økt sikkerhet kreves. Har ikke brukt PIN-koden nylig"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Økt sikkerhet kreves. Har ikke brukt passordet nylig"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Økt sikkerhet kreves. Har ikke brukt mønsteret nylig"</string> diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml index 4cc238d905f2..f0094a3d427d 100644 --- a/packages/SystemUI/res-keyguard/values-ne/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"लकडाउन गरेपछि PIN हाल्नु पर्ने हुन्छ"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"लकडाउन गरेपछि पासवर्ड हाल्नु पर्ने हुन्छ"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"लकडाउन गरेपछि प्याटर्न कोर्नु पर्ने हुन्छ"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"डिभाइस नचलाइएका बेला अपडेट इन्स्टल हुने छ"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"थप सुरक्षित बनाउनु पर्ने हुन्छ। केही समयदेखि PIN प्रयोग गरिएको छैन।"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"थप सुरक्षित बनाउनु पर्ने हुन्छ। केही समयदेखि पासवर्ड प्रयोग गरिएको छैन।"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"थप सुरक्षित बनाउनु पर्ने हुन्छ। केही समयदेखि प्याटर्न प्रयोग गरिएको छैन।"</string> diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml index 60578fa220ca..a236639f9df8 100644 --- a/packages/SystemUI/res-keyguard/values-nl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Na lockdown is de pincode vereist"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Na lockdown is het wachtwoord vereist"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Na lockdown is het patroon vereist"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Update wordt geïnstalleerd als het apparaat niet wordt gebruikt"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Extra beveiliging. Pincode is lang niet gebruikt."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Extra beveiliging. Wachtwoord is lang niet gebruikt."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Extra beveiliging. Patroon is lang niet gebruikt."</string> diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml index 4baae488746b..b31c9c0c2207 100644 --- a/packages/SystemUI/res-keyguard/values-or/strings.xml +++ b/packages/SystemUI/res-keyguard/values-or/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ଲକଡାଉନ ହେବା ପରେ PIN ଆବଶ୍ୟକ"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ଲକଡାଉନ ହେବା ପରେ ପାସୱାର୍ଡ ଆବଶ୍ୟକ"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ଲକଡାଉନ ହେବା ପରେ ପାଟର୍ନ ଆବଶ୍ୟକ"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ଡିଭାଇସ ବ୍ୟବହାର କରାଯାଉନଥିବା ବେଳେ ଅପଡେଟ ଇନଷ୍ଟଲ ହେବ"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ଅତିରିକ୍ତ ସୁରକ୍ଷା ଆବଶ୍ୟକ। କିଛି ସମୟ ପାଇଁ PIN ବ୍ୟବହାର କରାଯାଇନାହିଁ।"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ଅତିରିକ୍ତ ସୁରକ୍ଷା ଆବଶ୍ୟକ। କିଛି ସମୟ ପାଇଁ ପାସୱାର୍ଡ ବ୍ୟବହାର କରାଯାଇନାହିଁ।"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ଅତିରିକ୍ତ ସୁରକ୍ଷା ଆବଶ୍ୟକ। କିଛି ସମୟ ପାଇଁ ପାଟର୍ନ ବ୍ୟବହାର କରାଯାଇନାହିଁ।"</string> diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml index 230683258bb7..209b63fbd8f8 100644 --- a/packages/SystemUI/res-keyguard/values-pa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ਲਾਕਡਾਊਨ ਤੋਂ ਬਾਅਦ ਪਿੰਨ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ਲਾਕਡਾਊਨ ਤੋਂ ਬਾਅਦ ਪਾਸਵਰਡ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ਲਾਕਡਾਊਨ ਤੋਂ ਬਾਅਦ ਪੈਟਰਨ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ਡੀਵਾਈਸ ਵਰਤੋਂ ਵਿੱਚ ਨਾ ਹੋਣ \'ਤੇ ਅੱਪਡੇਟ ਸਥਾਪਤ ਹੋ ਜਾਵੇਗਾ"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ਵਾਧੂ ਸੁਰੱਖਿਆ ਦੀ ਲੋੜ ਹੈ। ਪਿੰਨ ਨੂੰ ਕੁਝ ਸਮੇਂ ਲਈ ਵਰਤਿਆ ਨਹੀਂ ਗਿਆ।"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ਵਾਧੂ ਸੁਰੱਖਿਆ ਦੀ ਲੋੜ ਹੈ। ਪਾਸਵਰਡ ਨੂੰ ਕੁਝ ਸਮੇਂ ਲਈ ਵਰਤਿਆ ਨਹੀਂ ਗਿਆ।"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ਵਾਧੂ ਸੁਰੱਖਿਆ ਦੀ ਲੋੜ ਹੈ। ਪੈਟਰਨ ਨੂੰ ਕੁਝ ਸਮੇਂ ਲਈ ਵਰਤਿਆ ਨਹੀਂ ਗਿਆ।"</string> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index f30d2cfd5474..7ec988ecf777 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Po zablokowaniu wymagany jest kod PIN"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Po zablokowaniu wymagane jest hasło"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Po zablokowaniu wymagany jest wzór"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Instalacja aktualizacji nastąpi, gdy urządzenie nie będzie w użyciu"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Wzmocnij ochronę. Od dawna nie używano kodu PIN."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Wzmocnij ochronę. Od dawna nie używano hasła."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Wzmocnij ochronę. Od dawna nie używano wzoru."</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml index e9b21ed9b2af..78a80912e99e 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"O PIN é obrigatório após o Bloqueio total"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"A senha é obrigatória após o Bloqueio total"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"O padrão é obrigatório após o Bloqueio total"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"A atualização será instalada quando o dispositivo não estiver em uso"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Segurança necessária. PIN não usado há um tempo."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Segurança necessária. Senha não usada há um tempo."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Segurança necessária. Padrão não usado há um tempo."</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index b10f31381bd9..2dc7d272e468 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"O PIN é necessário após o bloqueio"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"A palavra-passe é necessária após o bloqueio"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"O padrão é necessário após o bloqueio"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"A atualização é instalada quando o dispositivo não estiver a ser usado"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Mais segurança necessária. PIN não usado há muito."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Mais segurança necessária. Não usada há muito."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"+ segurança necessária. Padrão não usado há muito."</string> diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml index e9b21ed9b2af..78a80912e99e 100644 --- a/packages/SystemUI/res-keyguard/values-pt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"O PIN é obrigatório após o Bloqueio total"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"A senha é obrigatória após o Bloqueio total"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"O padrão é obrigatório após o Bloqueio total"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"A atualização será instalada quando o dispositivo não estiver em uso"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Segurança necessária. PIN não usado há um tempo."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Segurança necessária. Senha não usada há um tempo."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Segurança necessária. Padrão não usado há um tempo."</string> diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml index 3799ce7b0d3b..e5be788f7676 100644 --- a/packages/SystemUI/res-keyguard/values-ro/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Codul PIN este solicitat după blocarea strictă"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Parola este solicitată după blocarea strictă"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Modelul este solicitat după blocarea strictă"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Actualizarea se va instala când dispozitivul nu este folosit"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Mai multă securitate necesară. PIN-ul nu a fost folosit de ceva timp."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Mai multă securitate necesară. Parola nu a fost folosită de ceva timp."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Mai multă securitate necesară. Modelul nu a fost folosit de ceva timp."</string> diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml index 184b28db87ac..45149a59d672 100644 --- a/packages/SystemUI/res-keyguard/values-ru/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"После блокировки необходимо ввести PIN-код."</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"После блокировки необходимо ввести пароль."</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"После блокировки необходимо ввести графический ключ."</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Обновление будет установлено, когда устройство не используется."</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"PIN-код давно не использовался. Усильте защиту."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Пароль давно не использовался. Усильте защиту."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Граф. ключ давно не использовался. Усильте защиту."</string> diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml index a484119c8678..17ced75aaf1b 100644 --- a/packages/SystemUI/res-keyguard/values-si/strings.xml +++ b/packages/SystemUI/res-keyguard/values-si/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"අගුළු දැමීමෙන් පසු PIN අවශ්ය වේ"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"අගුළු දැමීමෙන් පසු මුරපදය අවශ්ය වේ"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"අගුළු දැමීමෙන් පසු රටාව අවශ්ය වේ"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"උපාංගය භාවිතයේ නොමැති විට යාවත්කාලීනය ස්ථාපනය වනු ඇත"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"අමතර ආරක්ෂාවක් අවශ්යයි. PIN ටික කලකට භාවිතා කර නැත."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"අමතර ආරක්ෂාවක් අවශ්යයි. මුරපදය ටික කලකට භාවිතා කර නැත."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"අමතර ආරක්ෂාවක් අවශ්යයි. රටාව ටික කලකට භාවිතා කර නැත."</string> diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml index 473027357cd3..ef08a6cd5258 100644 --- a/packages/SystemUI/res-keyguard/values-sk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Po silnej zámke sa vyžaduje PIN"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Po silnej zámke sa vyžaduje heslo"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Po silnej zámke sa vyžaduje vzor"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Aktualizácia sa nainštaluje, keď zariadenie nebudete používať"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Treba lepšie zabezp. Kód PIN nebol dlhšie použitý."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Treba lepšie zabezp. Heslo nebolo dlhšie použité."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Treba lepšie zabezp. Vzor nebol dlhšie použitý."</string> diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml index bddb9ad55069..a42989c18ea9 100644 --- a/packages/SystemUI/res-keyguard/values-sl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Po zaklepu se zahteva vnos kode PIN"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Po zaklepu se zahteva vnos gesla"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Po zaklepu se zahteva vnos vzorca"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Posodobitev bo nameščena, ko naprava ne bo v uporabi"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Zahtevana je dodatna varnost. Koda PIN nekaj časa ni bila uporabljena."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Zahtevana je dodatna varnost. Geslo nekaj časa ni bilo uporabljeno."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Zahtevana je dodatna varnost. Vzorec nekaj časa ni bil uporabljen."</string> diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml index 1739119a7efc..84f7bb5b08a8 100644 --- a/packages/SystemUI/res-keyguard/values-sq/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Pas bllokimit kërkohet kodi PIN"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Pas bllokimit kërkohet fjalëkalimi"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Pas bllokimit kërkohet motivi"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Përditësimi do të instalohet kur pajisja të mos përdoret"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Kërkohet një siguri më e lartë. Kodi PIN nuk është përdorur për njëfarë kohe."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Kërkohet një siguri më e lartë. Fjalëkalimi nuk është përdorur për njëfarë kohe."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Kërkohet një siguri më e lartë. Motivi nuk është përdorur për njëfarë kohe."</string> diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml index b8c4b55476bf..437018d93879 100644 --- a/packages/SystemUI/res-keyguard/values-sr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN је обавезан после закључавања"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Лозинка је обавезна после закључавања"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Шаблон је обавезан после закључавања"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Ажурирање ће се инсталирати када се уређај не користи"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Потребна је додатна заштита. PIN дуго није коришћен."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Потребна је додатна заштита. Лозинка дуго није коришћена."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Потребна је додатна заштита. Шаблон дуго није коришћен."</string> diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml index bec177125dbf..b4b1996094bc 100644 --- a/packages/SystemUI/res-keyguard/values-sv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Pinkod krävs efter låsning"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Lösenord krävs efter låsning"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Mönster krävs efter låsning"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Uppdateringen installeras när enheten inte används"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Ökad säkerhet krävs. Pinkoden har inte använts på länge."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Ökad säkerhet krävs. Lösenordet har inte använts på länge."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Ökad säkerhet krävs. Mönstret har inte använts på länge."</string> diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml index c6a8ed525ceb..8ca90465ee3a 100644 --- a/packages/SystemUI/res-keyguard/values-sw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"PIN inahitajika baada ya kufunga"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Nenosiri linahitajika baada ya kufunga"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Mchoro unahitajika baada ya kufunga"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Sasisho litasakinishwa wakati hutumii kifaa"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Usalama wa ziada unahitajika. PIN haikutumika kwa muda."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Usalama wa ziada unahitajika. Nenosiri halikutumika kwa muda."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Usalama wa ziada unahitajika. Mchoro haukutumika kwa muda."</string> diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml index 147b36bb32ae..7671194c3b14 100644 --- a/packages/SystemUI/res-keyguard/values-ta/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"முழுப் பூட்டு காரணமாகப் பின் தேவை"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"முழுப் பூட்டு காரணமாகக் கடவுச்சொல் தேவை"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"முழுப் பூட்டு காரணமாகப் பேட்டர்ன் தேவை"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"சாதனம் உபயோகத்தில் இல்லாதபோது புதுப்பிப்பு நிறுவப்படும்"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"சேர்த்த பாதுகாப்பு தேவை. பின் உபயோகிக்கவில்லை."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"சேர்த்த பாதுகாப்பு தேவை. கடவுச்சொல் உபயோகிக்கவில்லை."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"சேர்த்த பாதுகாப்பு தேவை. பேட்டர்ன் உபயோகிக்கவில்லை."</string> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index 317fb304234a..623b589861a9 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"లాక్డౌన్ తర్వాత PIN అవసరం"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"లాక్డౌన్ తర్వాత పాస్వర్డ్ అవసరం"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"లాక్డౌన్ తర్వాత ఆకృతి అవసరం"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"పరికరం ఉపయోగంలో లేనప్పుడు అప్డేట్ ఇన్స్టాల్ చేయబడుతుంది"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"మరింత సెక్యూరిటీ యాడ్ చెయ్యాలి. PINని ఈమధ్య వాడలేదు."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"యాడెడ్ సెక్యూరిటీ కావాలి. పాస్వర్డ్ ఈ మధ్య వాడలేదు."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"మరింత సెక్యూరిటీ కావాలి. ఆకృతిని ఈ మధ్య వాడలేదు."</string> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index 85a14fa12ef4..c2441075d02d 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"ต้องป้อน PIN หลังจากการปิดล็อก"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"ต้องป้อนรหัสผ่านหลังจากการปิดล็อก"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"ต้องวาดรูปแบบหลังจากการปิดล็อก"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"ระบบจะติดตั้งการอัปเดตเมื่อไม่มีการใช้งานอุปกรณ์"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"ต้องเพิ่มความปลอดภัย ไม่ได้ใช้ PIN มาระยะหนึ่ง"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"ต้องเพิ่มความปลอดภัย ไม่ได้ใช้รหัสผ่านมาระยะหนึ่ง"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"ต้องเพิ่มความปลอดภัย ไม่ได้ใช้รูปแบบมาระยะหนึ่ง"</string> diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml index 1e51e0b5b789..cd8f81046f96 100644 --- a/packages/SystemUI/res-keyguard/values-tl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Kailangan ang PIN pagkatapos ng lockdown"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Kailangan ang password pagkatapos ng lockdown"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Kailangan ang pattern pagkatapos ng lockdown"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Ii-install ang update kapag hindi ginagamit ang device"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Kailangan pa ng seguridad. Matagal na hindi ginamit ang PIN."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Kailangan pa ng seguridad. Matagal na hindi ginamit ang password."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Kailangan pa ng seguridad. Matagal na hindi ginamit ang pattern."</string> diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml index 05a9c95311a3..ddeba67719d7 100644 --- a/packages/SystemUI/res-keyguard/values-tr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Tam kilitlemenin ardından PIN gerekli"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Tam kilitlemenin ardından şifre gerekli"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Tam kilitlemenin ardından desen gerekli"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Güncelleme, cihazın kullanılmadığı bir sırada yüklenecek"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Daha fazla güvenlik gerekli. PIN bir süredir kullanılmamış."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Daha fazla güvenlik gerekli. Şifre bir süredir kullanılmamış."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Daha fazla güvenlik gerekli. Desen bir süredir kullanılmamış."</string> diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml index 1c3c3cae75fb..f06d17df9146 100644 --- a/packages/SystemUI/res-keyguard/values-uk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Після блокування входу потрібно ввести PIN-код"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Після блокування входу потрібно ввести пароль"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Після блокування входу потрібно намалювати ключ"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Оновлення встановиться, коли пристрій не використовуватиметься"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Потрібен додатковий захист. PIN-код довго не використовувався."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Потрібен додатковий захист. Пароль довго не використовувався."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Потрібен додатковий захист. Ключ довго не використовувався."</string> diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml index 7784766bc644..8adbaca955f7 100644 --- a/packages/SystemUI/res-keyguard/values-ur/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"لاک ڈاؤن کے بعد PIN کی ضرورت ہوتی ہے"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"لاک ڈاؤن کے بعد پاس ورڈ کی ضرورت ہوتی ہے"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"لاک ڈاؤن کے بعد پیٹرن کی ضرورت ہوتی ہے"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"آلہ استعمال میں نہ ہونے پر اپ ڈیٹ انسٹال ہو جائے گی"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"مزید سیکیورٹی چاہیے۔ PIN کچھ عرصے اسے استعمال نہیں ہوا ہے۔"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"مزید سیکیورٹی چاہیے۔ پاس ورڈ کچھ عرصے سے استعمال نہیں ہوا ہے۔"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"مزید سیکیورٹی چاہیے۔ پیٹرن کچھ عرصے سے استعمال نہیں ہوا ہے۔"</string> diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml index 28d18da03894..96dfa05a74aa 100644 --- a/packages/SystemUI/res-keyguard/values-uz/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Bloklangandan keyin PIN kodni kiritish kerak"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Bloklangandan keyin parolni kiritish kerak"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Bloklangandan keyin grafik kalitni chizish kerak"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Yangilanish qurilma ishlatilmaganda oʻrnatiladi"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Xavfsizlikni oshiring. PIN kod ancha vaqt ishlatilmadi."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Xavfsizlikni oshiring. Parol ancha vaqt ishlatilmadi."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Xavfsizlikni oshiring. Grafik kalit ancha vaqt chizilmadi"</string> diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml index 726a9e79f412..94d4fe257ead 100644 --- a/packages/SystemUI/res-keyguard/values-vi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Cần nhập mã PIN sau khi hết thời gian khoá"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Cần nhập mật khẩu sau khi hết thời gian khoá"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Cần vẽ hình mở khoá sau khi hết thời gian khoá"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Bản cập nhật sẽ được cài đặt khi bạn không sử dụng thiết bị"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Cần tăng cường bảo mật. Đã lâu chưa dùng mã PIN."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Cần tăng cường bảo mật. Đã lâu chưa dùng mật khẩu"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Cần tăng cường bảo mật. Đã lâu chưa dùng hình mở khoá."</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml index 0efe3bf822a0..59261a3d414b 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"一旦设备被锁定,必须输入 PIN 码才能解锁"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"一旦设备被锁定,必须输入密码才能解锁"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"一旦设备被锁定,必须绘制图案才能解锁"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"更新会在设备处于未使用状态时安装"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"需要锁定设备以提高安全性。已有一段时间未使用 PIN 码了。"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"需要锁定设备以提高安全性。已有一段时间未使用密码了。"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"需要锁定设备以提高安全性。已有一段时间未使用图案了。"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml index 58c3034c8585..dad6f31ac553 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"需要輸入 PIN 才能解除鎖定"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"需要輸入密碼解才能解除鎖定"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"需要畫出解鎖圖案才能解除鎖定"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"系統會在裝置未使用時安裝更新"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"需要加強安全設定:已有一段時間沒有使用 PIN。"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"需要加強安全設定:已有一段時間沒有使用密碼。"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"需要加強安全設定:已有一段時間沒有使用解鎖圖案。"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml index 35e7824d2824..88b7e4374ff9 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"裝置鎖定後,必須輸入 PIN 碼才能解鎖"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"裝置鎖定後,必須輸入密碼才能解鎖"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"裝置鎖定後,必須畫出解鎖圖案才能解鎖"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"系統會在裝置處於未使用狀態時安裝更新"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"裝置已有一段時間未鎖定,請使用 PIN 碼鎖定裝置以策安全。"</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"裝置已有一段時間未鎖定,請使用密碼鎖定裝置以策安全。"</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"裝置已有一段時間未鎖定,請使用解鎖圖案鎖定裝置以策安全。"</string> diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml index e229c7eb743f..c5e99abbda54 100644 --- a/packages/SystemUI/res-keyguard/values-zu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml @@ -76,8 +76,7 @@ <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Iphinikhodi iyadingeka ngemva kokukhiya"</string> <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Iphasiwedi iyadingeka ngemuva kokukhiya"</string> <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"Iphethini iyadingeka ngemva kokukhiya"</string> - <!-- no translation found for kg_prompt_unattended_update (4366635751738712452) --> - <skip /> + <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"Isibuyekezo sizofakwa lapho idivayisi ingasetshenziswa"</string> <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"Ukuphepha okwengeziwe kuyadingeka. Iphinikhodi ayisetshenziswanga isikhathi eside."</string> <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"Ukuphepha okwengeziwe kuyadingeka. Iphasiwedi ayisetshenziswanga isikhathi eside."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"Ukuphepha okwengeziwe kuyadingeka. Iphethini ayisetshenziswanga isikhathi eside."</string> diff --git a/packages/SystemUI/res-product/values-zh-rCN/strings.xml b/packages/SystemUI/res-product/values-zh-rCN/strings.xml index 68952194c75b..de308ddb726a 100644 --- a/packages/SystemUI/res-product/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res-product/values-zh-rCN/strings.xml @@ -38,8 +38,8 @@ <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="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> <string name="thermal_shutdown_title" product="device" msgid="2954206342842856379">"设备先前因过热而关机"</string> <string name="thermal_shutdown_title" product="tablet" msgid="8941033526856177533">"平板电脑先前因过热而关机"</string> diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media.xml b/packages/SystemUI/res/drawable-television/ic_volume_media.xml deleted file mode 100644 index 6a368d503866..000000000000 --- a/packages/SystemUI/res/drawable-television/ic_volume_media.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License - --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="@dimen/tv_volume_icons_size" - android:height="@dimen/tv_volume_icons_size" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <path - android:fillColor="@color/tv_volume_dialog_accent" - android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM10,8.83v6.34L7.83,13L5,13v-2h2.83L10,8.83zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77 0,-4.28 -2.99,-7.86 -7,-8.77z"/> -</vector> diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml deleted file mode 100644 index 6eb944fd257a..000000000000 --- a/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License - --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="@dimen/tv_volume_icons_size" - android:height="@dimen/tv_volume_icons_size" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <group android:translateX="-2"> - <path - android:fillColor="@color/tv_volume_dialog_accent" - android:pathData="M16,7.97v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02 0,-1.77 -1.02,-3.29 -2.5,-4.03zM5,9v6h4l5,5L14,4L9,9L5,9zM12,8.83v6.34L9.83,13L7,13v-2h2.83L12,8.83z"/> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml deleted file mode 100644 index 7a44aa6cbcc9..000000000000 --- a/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License - --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="@dimen/tv_volume_icons_size" - android:height="@dimen/tv_volume_icons_size" - android:viewportHeight="24" - android:viewportWidth="24"> - <path - android:fillColor="@color/tv_volume_dialog_accent" - android:pathData="M4.34,2.93L2.93,4.34 7.29,8.7 7,9L3,9v6h4l5,5v-6.59l4.18,4.18c-0.65,0.49 -1.38,0.88 -2.18,1.11v2.06c1.34,-0.3 2.57,-0.92 3.61,-1.75l2.05,2.05 1.41,-1.41L4.34,2.93zM10,15.17L7.83,13L5,13v-2h2.83l0.88,-0.88L10,11.41v3.76zM19,12c0,0.82 -0.15,1.61 -0.41,2.34l1.53,1.53c0.56,-1.17 0.88,-2.48 0.88,-3.87 0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM12,4l-1.88,1.88L12,7.76zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v1.79l2.48,2.48c0.01,-0.08 0.02,-0.16 0.02,-0.24z"/> -</vector> diff --git a/packages/SystemUI/res/drawable-television/volume_row_seekbar.xml b/packages/SystemUI/res/drawable-television/volume_row_seekbar.xml deleted file mode 100644 index e49fc15fbd7f..000000000000 --- a/packages/SystemUI/res/drawable-television/volume_row_seekbar.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android" - android:paddingMode="stack"> - <item android:id="@android:id/background" - android:gravity="center_vertical|fill_horizontal"> - <layer-list> - <item android:id="@+id/volume_seekbar_background_solid"> - <shape> - <size android:height="@dimen/volume_dialog_slider_width" /> - <solid android:color="@color/tv_volume_dialog_seek_bar_background"/> - <corners android:radius="@dimen/volume_dialog_slider_corner_radius" /> - </shape> - </item> - </layer-list> - </item> - <item android:id="@android:id/progress" - android:gravity="center_vertical|fill_horizontal"> - <com.android.systemui.util.RoundedCornerProgressDrawable - android:drawable="@drawable/volume_row_seekbar_progress" - /> - </item> -</layer-list> diff --git a/packages/SystemUI/res/drawable-television/volume_row_seekbar_progress.xml b/packages/SystemUI/res/drawable-television/volume_row_seekbar_progress.xml deleted file mode 100644 index bce193a564a0..000000000000 --- a/packages/SystemUI/res/drawable-television/volume_row_seekbar_progress.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ 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. - --> - -<!-- Progress drawable for volume row SeekBars. This is the accent-colored round rect that moves up - and down as the progress value changes. --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android" - android:autoMirrored="true"> - <item android:id="@+id/volume_seekbar_progress_solid"> - <shape android:shape="rectangle"> - <size android:height="@dimen/volume_dialog_slider_width"/> - <solid android:color="@color/tv_volume_dialog_seek_bar_fill" /> - <corners android:radius="@dimen/volume_dialog_slider_width" /> - </shape> - </item> -</layer-list> diff --git a/packages/SystemUI/res/drawable/auth_credential_emergency_button_background.xml b/packages/SystemUI/res/drawable/auth_credential_emergency_button_background.xml new file mode 100644 index 000000000000..85450b446d48 --- /dev/null +++ b/packages/SystemUI/res/drawable/auth_credential_emergency_button_background.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<inset xmlns:android="http://schemas.android.com/apk/res/android"> + <shape android:shape="rectangle"> + <corners android:radius="25dp"/> + <solid android:color="@android:color/system_accent3_100" /> + </shape> +</inset> diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml index 4a9d41fae1d5..b83f15a1a247 100644 --- a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml +++ b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml @@ -14,6 +14,4 @@ Copyright (C) 2015 The Android Open Source Project limitations under the License. --> <inset xmlns:android="http://schemas.android.com/apk/res/android" - android:insetLeft="3dp" - android:insetRight="3dp" android:drawable="@drawable/ic_speaker_mute" /> diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml index e2ce34f5008e..e439f775f8ea 100644 --- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml +++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml @@ -61,29 +61,46 @@ </RelativeLayout> - <LinearLayout + <FrameLayout android:id="@+id/auth_credential_input" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> - <ImeAwareEditText - android:id="@+id/lockPassword" - style="?passwordTextAppearance" - android:layout_width="208dp" + <LinearLayout + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" - android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" - android:inputType="textPassword" - android:minHeight="48dp" /> + android:layout_gravity="center_horizontal|top" + android:orientation="vertical"> - <TextView - android:id="@+id/error" - style="?errorTextAppearance" - android:layout_gravity="center" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + <ImeAwareEditText + android:id="@+id/lockPassword" + style="?passwordTextAppearance" + android:layout_width="208dp" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" + android:inputType="textPassword" + android:minHeight="48dp"/> + + <TextView + android:id="@+id/error" + style="?errorTextAppearance" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + </LinearLayout> - </LinearLayout> + <Button + android:id="@+id/emergencyCallButton" + style="@style/AuthCredentialEmergencyButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layout_gravity="center_horizontal|bottom" + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp" + android:text="@string/work_challenge_emergency_button_text"/> + </FrameLayout> </com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml index 88f138f9b093..d5af37733b3b 100644 --- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml +++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml @@ -60,27 +60,44 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"/> + <TextView + android:id="@+id/error" + style="?errorTextAppearanceLand" + android:layout_below="@id/description" + android:layout_alignParentLeft="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + </RelativeLayout> - <FrameLayout + <RelativeLayout android:layout_weight="1" - style="?containerStyle" android:layout_width="0dp" android:layout_height="match_parent"> - <com.android.internal.widget.LockPatternView - android:id="@+id/lockPattern" - android:layout_gravity="center" - android:layout_width="@dimen/biometric_auth_pattern_view_size" - android:layout_height="@dimen/biometric_auth_pattern_view_size"/> - - <TextView - android:id="@+id/error" - style="?errorTextAppearance" + <FrameLayout + style="?containerStyle" + android:layout_above="@id/emergencyCallButton" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|bottom"/> + android:layout_height="match_parent"> + + <com.android.internal.widget.LockPatternView + android:id="@+id/lockPattern" + android:layout_gravity="center" + android:layout_width="@dimen/biometric_auth_pattern_view_size" + android:layout_height="@dimen/biometric_auth_pattern_view_size"/> + </FrameLayout> - </FrameLayout> + <Button + android:id="@+id/emergencyCallButton" + style="@style/AuthCredentialEmergencyButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="35dp" + android:visibility="gone" + android:layout_alignParentBottom="true" + android:layout_centerHorizontal="true" + android:text="@string/work_challenge_emergency_button_text"/> + </RelativeLayout> </com.android.systemui.biometrics.ui.CredentialPatternView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml index 33f1b10b123b..9336845f20f7 100644 --- a/packages/SystemUI/res/layout/auth_credential_password_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml @@ -65,29 +65,46 @@ </ScrollView> - <LinearLayout + <FrameLayout android:id="@+id/auth_credential_input" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> - <ImeAwareEditText - android:id="@+id/lockPassword" - style="?passwordTextAppearance" - android:layout_width="208dp" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" - android:inputType="textPassword" - android:minHeight="48dp" /> + android:layout_gravity="center_horizontal|top" + android:orientation="vertical"> - <TextView - android:id="@+id/error" - style="?errorTextAppearance" - android:layout_gravity="center_horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> + <ImeAwareEditText + android:id="@+id/lockPassword" + style="?passwordTextAppearance" + android:layout_width="208dp" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii" + android:inputType="textPassword" + android:minHeight="48dp"/> - </LinearLayout> + <TextView + android:id="@+id/error" + style="?errorTextAppearance" + android:layout_gravity="center_horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + </LinearLayout> + + <Button + android:id="@+id/emergencyCallButton" + style="@style/AuthCredentialEmergencyButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layout_gravity="center_horizontal|bottom" + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp" + android:text="@string/work_challenge_emergency_button_text"/> + </FrameLayout> </com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml index 81ca37189ac4..59828fde309f 100644 --- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml @@ -58,24 +58,42 @@ android:layout_height="wrap_content"/> </RelativeLayout> - <FrameLayout + <RelativeLayout android:id="@+id/auth_credential_container" - style="?containerStyle" android:layout_width="match_parent" android:layout_height="match_parent"> - <com.android.internal.widget.LockPatternView - android:id="@+id/lockPattern" - android:layout_gravity="center" - android:layout_width="@dimen/biometric_auth_pattern_view_size" - android:layout_height="@dimen/biometric_auth_pattern_view_size"/> + <FrameLayout + android:layout_centerInParent="true" + android:layout_above="@id/emergencyCallButton" + style="?containerStyle" + android:layout_width="wrap_content" + android:layout_height="match_parent"> + + <com.android.internal.widget.LockPatternView + android:id="@+id/lockPattern" + android:layout_gravity="center" + android:layout_width="@dimen/biometric_auth_pattern_view_size" + android:layout_height="@dimen/biometric_auth_pattern_view_size"/> - <TextView - android:id="@+id/error" - style="?errorTextAppearance" - android:layout_width="match_parent" + <TextView + android:id="@+id/error" + style="?errorTextAppearance" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal|bottom"/> + </FrameLayout> + + <Button + android:id="@+id/emergencyCallButton" + style="@style/AuthCredentialEmergencyButtonStyle" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|bottom"/> - </FrameLayout> + android:layout_alignParentBottom="true" + android:visibility="gone" + android:layout_marginBottom="35dp" + android:layout_centerHorizontal="true" + android:text="@string/work_challenge_emergency_button_text"/> + </RelativeLayout> </com.android.systemui.biometrics.ui.CredentialPatternView> diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 12f13e9a2138..3a15ae4f17ff 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -127,6 +127,8 @@ frame when animating QS <-> QQS transition android:gravity="center_vertical" android:paddingStart="@dimen/shade_header_system_icons_padding_start" android:paddingEnd="@dimen/shade_header_system_icons_padding_end" + android:paddingTop="@dimen/shade_header_system_icons_padding_top" + android:paddingBottom="@dimen/shade_header_system_icons_padding_bottom" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/privacy_container" app:layout_constraintTop_toTopOf="@id/clock"> diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml index 9af46c5b739c..37964158a4aa 100644 --- a/packages/SystemUI/res/layout/screen_share_dialog.xml +++ b/packages/SystemUI/res/layout/screen_share_dialog.xml @@ -64,8 +64,7 @@ android:layout_height="wrap_content" android:text="@string/screenrecord_permission_dialog_warning_entire_screen" style="@style/TextAppearance.Dialog.Body.Message" - android:gravity="start" - android:lineHeight="@dimen/screenrecord_warning_line_height"/> + android:gravity="start"/> <!-- Buttons --> <LinearLayout diff --git a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml index 78cd7184b485..39ec09b14157 100644 --- a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml +++ b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml @@ -34,8 +34,8 @@ android:layout_height="@dimen/overlay_dismiss_button_tappable_size" android:contentDescription="@string/screenshot_dismiss_work_profile"> <ImageView - android:layout_width="16dp" - android:layout_height="16dp" + android:layout_width="24dp" + android:layout_height="24dp" android:layout_gravity="center" android:background="@drawable/circular_background" android:backgroundTint="?androidprv:attr/materialColorSurfaceContainerHigh" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 2355341232c0..fe8035535a5f 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g>-limiet"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> waarskuwing"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Werkprogramme"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Onderbreek"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Aandbeligting"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan by sonsondergang"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot sonsopkoms"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Maak vergrotinginstellings oop"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Maak vergrotinginstellings toe"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Sleep hoek om grootte te verander"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Laat diagonale rollees toe"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Verander grootte"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Maak <xliff:g id="APPNAME">%1$s</xliff:g> oop"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Om die Wallet-app as ’n kortpad by te voeg, moet jy seker maak dat die app geïnstalleer is"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Om die Wallet-app as ’n kortpad by te voeg, moet jy seker maak dat minstens een kaart bygevoeg is"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Om die QR-kodeskandeerder as ’n kortpad by te voeg, moet jy seker maak dat ’n kamera-app geïnstalleer is"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Om die Home-app as ’n kortpad by te voeg, moet jy seker maak dat die app geïnstalleer is"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Minstens een toestel beskikbaar is"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Kies ’n versteknotasapp om die notaneemkortpad te gebruik"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 0fd3f116b3de..63f176511b4d 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ገደብ"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"የ<xliff:g id="DATA_LIMIT">%s</xliff:g> ማስጠንቀቂያ"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"የሥራ መተግበሪያዎች"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"ባለበት ቆሟል"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"የምሽት ብርሃን"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ፀሐይ ስትጠልቅ ይበራል"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ፀሐይ እስክትወጣ ድረስ"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"የማጉያ ቅንብሮችን ክፈት"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"የማጉላት ቅንብሮችን ዝጋ"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"መጠን ለመቀየር ጠርዙን ይዘው ይጎትቱ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ሰያፍ ሽብለላን ፍቀድ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"መጠን ቀይር"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ይክፈቱ"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"የWallet መተግበሪያን እንደ አቋራጭ ለማከል መተግበሪያው መጫኑን ያረጋግጡ"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"የWallet መተግበሪያን እንደ አቋራጭ ለማከል ቢያንስ አንድ ካርድ መታከሉን ያረጋግጡ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"የQR ኮድ መቃኛውን እንደ አቋራጭ ለማከል የካሜራ መተግበሪያ መጫኑን ያረጋግጡ"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"የHome መተግበሪያውን እንደ አቋራጭ ለማከል መተግበሪያው እንደተጫነ ያረጋግጡ"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ቢያንስ አንድ መሣሪያ ይገኛል"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"የማስታወሻ አያያዝ አቋራጭን ለመጠቀም ነባሪ የማስታወሻ መተግበሪያ ይምረጡ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index bb99e2c8ce0f..8128faeb479a 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"قيد <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"تحذير <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"تطبيقات العمل"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"متوقّف مؤقَّتًا"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"الإضاءة الليلية"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"تفعيل عند غروب الشمس"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"حتى شروق الشمس"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"فتح إعدادات التكبير"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"إغلاق إعدادات التكبير"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"اسحب الزاوية لتغيير الحجم."</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"السماح بالتمرير القطري"</string> <string name="accessibility_resize" msgid="5733759136600611551">"تغيير الحجم"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"فتح \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"لإضافة تطبيق \"محفظة Google\" كاختصار، تأكَّد من أنّ التطبيق مثبَّت."</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"لإضافة تطبيق \"محفظة Google\" كاختصار، تأكَّد من إضافة بطاقة واحدة على الأقل."</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"لإضافة تطبيق الماسح الضوئي لرمز الاستجابة السريعة كاختصار، تأكَّد من أنّ تطبيق الكاميرا مثبَّت."</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"لإضافة تطبيق Home كاختصار، تأكَّد من أنّ التطبيق مثبَّت."</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• توفُّر جهاز واحد على الأقل"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"اختَر تطبيقًا تلقائيًا لتدوين الملاحظات لاستخدام اختصار تدوين الملاحظات."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index d52301ce4443..4915257c542c 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সীমা"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সকীয়নি"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"কৰ্মস্থানৰ এপ্"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"পজ হৈ আছে"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ৰাতিৰ পোহৰ"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"সূৰ্যাস্তত অন কৰক"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"সূৰ্যোদয়ৰ লৈকে"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"বিবৰ্ধন কৰাৰ ছেটিং খোলক"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"বিবৰ্ধনৰ ছেটিং বন্ধ কৰক"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"আকাৰ সলনি কৰিবলৈ চুককেইটা টানি আনি এৰক"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"কোণীয়াকৈ স্ক্ৰ’ল কৰাৰ অনুমতি দিয়ক"</string> <string name="accessibility_resize" msgid="5733759136600611551">"আকাৰ সলনি কৰক"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> খোলক"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet এপ্টোক এটা শ্বৰ্টকাট হিচাপে যোগ দিবলৈ, এপ্টো ইনষ্টল কৰি থোৱাটো নিশ্চিত কৰক"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet এপ্টোক এটা শ্বৰ্টকাট হিচাপে যোগ দিবলৈ, কমেও এখন কাৰ্ড যোগ দিয়াটো নিশ্চিত কৰক"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"কিউআৰ ক’ড স্কেনাৰক এটা শ্বৰ্টকাট হিচাপে যোগ দিবলৈ, কেমেৰা এপ্টো ইনষ্টল কৰি থোৱাটো নিশ্চিত কৰক"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home এপ্টোক এটা শ্বৰ্টকাট হিচাপে যোগ দিবলৈ, এপ্টো ইনষ্টল কৰি থোৱাটো নিশ্চিত কৰক"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• অতি কমেও এটা ডিভাইচ উপলব্ধ"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"টোকা গ্ৰহণৰ শ্বৰ্টকাটটো ব্যৱহাৰ কৰিবলৈ এটা ডিফ’ল্ট টোকা গ্ৰহণৰ এপ্ বাছনি কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 85220a6f6e0b..8d9fcbd58cfd 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> xəbərdarlığı"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"İş tətbiqləri"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Durdurulub"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Gecə işığı"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Qürubda aktiv ediləcək"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Şəfəq vaxtına qədər"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Böyütmə ayarlarını açın"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Böyütmə ayarlarını bağlayın"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Ölçüsünü dəyişmək üçün küncündən sürüşdürün"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diaqonal sürüşdürməyə icazə verin"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ölçüsünü dəyişin"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> tətbiqini açın"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Pulqabı tətbiqini qısayol kimi əlavə etmək üçün tətbiq quraşdırılmalıdır"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Pulqabı tətbiqini qısayol kimi əlavə etmək üçün kart əlavə edilməlidir"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR kod skanerini qısayol kimi əlavə etmək üçün kamera tətbiqi quraşdırılmalıdır"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home tətbiqini qısayol kimi əlavə etmək üçün tətbiq quraşdırılmalıdır"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Ən azı bir cihaz əlçatandır"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Qeydgötürmə qısayolu üçün defolt qeyd tətbiqi seçin"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 37903006f4d9..e1bd89dc31b4 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ograničenje od <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozorenje za <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Poslovne aplikacije"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pauzirano"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noćno svetlo"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Uključuje se po zalasku sunca"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do izlaska sunca"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvori podešavanja uvećanja"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zatvori podešavanja uvećanja"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Zatvori režim izmene"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Prevucite ugao da biste promenili veličinu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dozvoli dijagonalno skrolovanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Promeni veličinu"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvorite: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Da biste dodali aplikaciju Novčanik kao prečicu, uverite se da je aplikacija instalirana"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Da biste dodali aplikaciju Novčanik kao prečicu, uverite se da je dodata bar jedna kartica"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Da biste dodali Skener QR koda kao prečicu, uverite se da je aplikacija za kameru instalirana"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Da biste dodali aplikaciju Home kao prečicu, uverite se da je aplikacija instalirana"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Dostupan je bar jedan uređaj"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Izaberite podrazumevanu aplikaciju za beleške da biste koristili prečicu za pravljenje beleški"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index def25b4a4a3a..5d1b65cec639 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ліміт <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Папярэджанне: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Працоўныя праграмы"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Профіль прыпынены"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Начная падсветка"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Уключаць увечары"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Да ўсходу сонца"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Адкрыць налады павелічэння"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрыць налады павелічэння"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Каб змяніць памер, перацягніце вугал"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дазволіць прагортванне па дыяганалі"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Змяніць памер"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Адкрыць праграму \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Вы можаце дадаць ярлык праграмы \"Кашалёк\", толькі калі яна ўсталявана"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Вы можаце дадаць ярлык праграмы \"Кашалёк\", толькі калі дададзена хаця б адна картка"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Вы можаце дадаць ярлык сканера QR-кодаў, толькі калі ўсталявана праграма камеры"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Вы можаце дадаць ярлык праграмы Home, толькі калі яна ўсталявана"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Даступная хаця б адна прылада"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Выберыце стандартную праграму для нататак, якая будзе адкрывацца пры націсканні на ярлык"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 224ff1fffc8d..94ab2c989d1a 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ограничение от <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Служебни приложения"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"На пауза"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Нощно осветление"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ще се вкл. по залез"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До изгрев"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Отваряне на настройките за увеличението"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затваряне на настройките за увеличение"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Изход от режима на редактиране"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Плъзнете ъгъла за преоразмеряване"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Разрешаване на диагонално превъртане"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Преоразмеряване"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отваряне на <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"За да добавите пряк път към приложението Wallet, уверете се, че то е инсталирано"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"За да добавите пряк път към приложението Wallet, уверете се, че е добавена поне една карта"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"За да добавите пряк път към скенера за QR кодове, уверете се, че е инсталирано приложение за камера"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"За да добавите пряк път към приложението Home, уверете се, че то е инсталирано"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Налице е поне едно устройство."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Изберете стандартно приложение за бележки, за да използвате прекия път за водене на бележки"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index aca09311ec9c..f80c301ed95c 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -295,6 +295,8 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"সীমা <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সতর্কতা"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"অফিসের অ্যাপ"</string> + <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) --> + <skip /> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"নাইট লাইট"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"সূর্যাস্তে চালু হবে"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"সূর্যোদয় পর্যন্ত"</string> @@ -864,6 +866,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"বড় করে দেখার সেটিংস খুলুন"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"\'বড় করে দেখা\' সেটিংস বন্ধ করুন"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ছোট বড় করার জন্য কোণ টেনে আনুন"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"কোনাকুনি স্ক্রল করার অনুমতি দেওয়া"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ছোট বড় করা"</string> @@ -1133,7 +1137,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> খুলুন"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet অ্যাপ, শর্টকাট হিসেবে যোগ করতে, অ্যাপ ইনস্টল করা আছে কিনা তা ভালভাবে দেখে নিন"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet অ্যাপ, শর্টকাট হিসেবে যোগ করতে, অন্তত একটি কার্ড যোগ করা হয়েছে কিনা তা ভালভাবে দেখে নিন"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR কোড স্ক্যানার, শর্টকাট হিসেবে যোগ করতে, ক্যামেরা অ্যাপ ইনস্টল করা আছে কিনা তা ভালভাবে দেখে নিন"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home অ্যাপ, শর্টকাট হিসেবে যোগ করতে, অ্যাপ ইনস্টল করা আছে কিনা তা ভালভাবে দেখে নিন"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• অন্তত একটি ডিভাইস উপলভ্য আছে"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"নোট নেওয়ার শর্টকাট ব্যবহার করতে, ডিফল্ট কোনও নোট অ্যাপ বেছে নিন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 1e4588da0f73..364e84742600 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ograničenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Poslovne aplikacije"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pauzirano"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noćno svjetlo"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Uključuje se u sumrak"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do svitanja"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvori postavke uvećavanja"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zatvori postavke uvećavanja"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Izađi iz načina rada za uređivanje"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Prevucite ugao da promijenite veličinu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dozvoli dijagonalno klizanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Promijeni veličinu"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvori aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Da dodate aplikaciju Novčanik kao prečicu, instalirajte aplikaciju"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Da dodate aplikaciju Novčanik kao prečicu, dodajte barem jednu karticu"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Da dodate skener QR koda kao prečicu, instalirajte aplikaciju kamere"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Da dodate aplikaciju Home kao prečicu, instalirajte aplikaciju"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Dostupan je najmanje jedan uređaj"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Odaberite zadanu aplikaciju za bilješke da koristite prečicu za zapisivanje bilješki"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index a25cda60010c..557a7e6a55ad 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertiment: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplicacions de treball"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pausa"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Llum nocturna"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al vespre"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Fins a l\'alba"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Obre la configuració de l\'ampliació"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tanca la configuració de l\'ampliació"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrossega el cantó per canviar la mida"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permet el desplaçament en diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Canvia la mida"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Obre <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Per afegir l\'aplicació Wallet com a drecera, assegura\'t que estigui instal·lada"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Per afegir l\'aplicació Wallet com a drecera, assegura\'t que s\'hagi afegit almenys una targeta"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Per afegir l\'escàner de codis QR com a drecera, assegura\'t que hi hagi una aplicació de càmera instal·lada"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Per afegir l\'aplicació Home com a drecera, assegura\'t que estigui instal·lada"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Almenys un dispositiu està disponible."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecciona una aplicació de notes predeterminada per utilitzar la drecera de presa de notes"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 638c26a6ac82..0a83c867965e 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozornění při <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Pracovní aplikace"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pozastaveno"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noční režim"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Při soumraku"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do svítání"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otevřít nastavení zvětšení"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zavřít nastavení zvětšení"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Ukončit režim úprav"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Velikost změníte přetažením rohu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Povolit diagonální posouvání"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Změnit velikost"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otevřít <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Pokud chcete přidat aplikaci Peněženka jako zkratku, nainstalujte ji"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Pokud chcete přidat aplikaci Peněženka jako zkratku, přidejte alespoň jednu kartu"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Pokud chcete přidat skener QR kódů jako zkratku, ujistěte se, že je nainstalována aplikace k focení a natáčení"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Pokud chcete přidat aplikaci Home jako zkratku, nainstalujte ji"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Je k dispozici alespoň jedno zařízení"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Vyberte výchozí aplikaci k psaní poznámek, ke které přidružíte zkratku pro poznámky"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 663aac4fcf79..9ebbebb2c3cc 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Grænse: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advarsel ved <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Arbejdsapps"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Sat på pause"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattelys"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Tænd ved solnedgang"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Indtil solopgang"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Åbn indstillinger for forstørrelse"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Luk indstillinger for forstørrelse"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Træk i hjørnet for at justere størrelsen"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillad diagonal rulning"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Juster"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Åbn <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Hvis du vil tilføje en genvej til Wallet-appen, skal du sørge for, at appen er installeret"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Hvis du vil tilføje en genvej til Wallet-appen, skal du sørge for, at du har tilføjet mindst ét kort"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Hvis du vil tilføje en genvej til QR-kodescanneren, skal du sørge for, at kameraappen er installeret"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Hvis du vil tilføje en genvej til Home-appen, skal du sørge for, at appen er installeret"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Mindst én enhed er tilgængelig"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Vælg en standardapp til noter for at bruge genvejen til notetagning"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index ead500131383..206e832a9204 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> Datenlimit"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Warnung für <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Geschäftliche Apps"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pausiert"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nachtlicht"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"An bei Sonnenuntergang"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Bis Sonnenaufgang"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Vergrößerungseinstellungen öffnen"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Vergrößerungseinstellungen schließen"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Zum Anpassen der Größe Ecke ziehen"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonales Scrollen erlauben"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Größe ändern"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> öffnen"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Damit du die Wallet App als Verknüpfung hinzufügen kannst, muss die App installiert sein"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Damit du die Wallet App als Verknüpfung hinzufügen kannst, muss mindestens eine Karte hinzugefügt sein"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Damit du den QR-Code-Scanner als Verknüpfung hinzufügen kannst, muss eine Kamera-App installiert sein"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Damit du die Home App als Verknüpfung hinzufügen kannst, muss die App installiert sein"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Mindestens ein Gerät ist verfügbar"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Wähle eine Standard-App für Notizen aus, die du für die Verknüpfung verwenden möchtest"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 7a74831658ec..c32cd171fb60 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Όριο <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Προειδοποίηση για <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Εφαρμογές εργασιών"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Σε παύση"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Νυχτερινός φωτισμός"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Κατά τη δύση"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Μέχρι την ανατολή"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Άνοιγμα ρυθμίσεων μεγιστοποίησης"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Κλείσιμο ρυθμίσεων μεγιστοποίησης"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Σύρετε τη γωνία για αλλαγή μεγέθους"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Να επιτρέπεται η διαγώνια κύλιση"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Αλλαγή μεγέθους"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Άνοιγμα <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Για να προσθέσετε ως συντόμευση την εφαρμογή Πορτοφόλι, βεβαιωθείτε ότι έχει εγκατασταθεί η εφαρμογή"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Για να προσθέσετε ως συντόμευση την εφαρμογή Πορτοφόλι, βεβαιωθείτε ότι έχει προστεθεί τουλάχιστον μία κάρτα"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Για να προσθέσετε ως συντόμευση τη Σάρωση κωδικών QR, βεβαιωθείτε ότι η εφαρμογή κάμερας είναι εγκατεστημένη"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Για να προσθέσετε την εφαρμογή Home ως συντόμευση, βεβαιωθείτε ότι η εφαρμογή είναι εγκατεστημένη"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Είναι διαθέσιμη τουλάχιστον μία συσκευή"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Επιλέξτε μια προεπιλεγμένη εφαρμογή σημειώσεων για να χρησιμοποιήσετε τη συντόμευση δημιουργίας σημειώσεων"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 5d713bbc6a34..313f6170aaf2 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -295,6 +295,8 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string> + <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) --> + <skip /> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string> @@ -864,6 +866,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1133,7 +1137,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"To add the Wallet app as a shortcut, make sure that the app is installed"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"To add the Wallet app as a shortcut, make sure that at least one card has been added"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"To add the QR code scanner as a shortcut, make sure that a camera app is installed"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"To add the Home app as a shortcut, make sure that the app is installed"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• At least one device is available"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Select a default notes app to use the note-taking shortcut"</string> @@ -1166,7 +1169,7 @@ <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant attention on"</string> <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string> <string name="install_app" msgid="5066668100199613936">"Install app"</string> - <string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone & Camera"</string> + <string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone and Camera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app use"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"See recent access"</string> <string name="privacy_dialog_done_button" msgid="4504330708531434263">"Done"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 9d4a4febf3c9..157570ab02e4 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Paused"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Exit edit mode"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"To add the Wallet app as a shortcut, make sure the app is installed"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"To add the Wallet app as a shortcut, make sure at least one card has been added"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"To add the QR code scanner as a shortcut, make sure a camera app is installed"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"To add the Home app as a shortcut, make sure the app is installed"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• At least one device is available"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Select a default notes app to use the notetaking shortcut"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 5d713bbc6a34..313f6170aaf2 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -295,6 +295,8 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string> + <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) --> + <skip /> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string> @@ -864,6 +866,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1133,7 +1137,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"To add the Wallet app as a shortcut, make sure that the app is installed"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"To add the Wallet app as a shortcut, make sure that at least one card has been added"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"To add the QR code scanner as a shortcut, make sure that a camera app is installed"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"To add the Home app as a shortcut, make sure that the app is installed"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• At least one device is available"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Select a default notes app to use the note-taking shortcut"</string> @@ -1166,7 +1169,7 @@ <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant attention on"</string> <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string> <string name="install_app" msgid="5066668100199613936">"Install app"</string> - <string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone & Camera"</string> + <string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone and Camera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app use"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"See recent access"</string> <string name="privacy_dialog_done_button" msgid="4504330708531434263">"Done"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 5d713bbc6a34..313f6170aaf2 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -295,6 +295,8 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string> + <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) --> + <skip /> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string> @@ -864,6 +866,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1133,7 +1137,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"To add the Wallet app as a shortcut, make sure that the app is installed"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"To add the Wallet app as a shortcut, make sure that at least one card has been added"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"To add the QR code scanner as a shortcut, make sure that a camera app is installed"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"To add the Home app as a shortcut, make sure that the app is installed"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• At least one device is available"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Select a default notes app to use the note-taking shortcut"</string> @@ -1166,7 +1169,7 @@ <string name="assistant_attention_content_description" msgid="6830215897604642875">"Assistant attention on"</string> <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string> <string name="install_app" msgid="5066668100199613936">"Install app"</string> - <string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone & Camera"</string> + <string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone and Camera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app use"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"See recent access"</string> <string name="privacy_dialog_done_button" msgid="4504330708531434263">"Done"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 6a172f0c333d..db7133cfb0b3 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Paused"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Exit edit mode"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"To add the Wallet app as a shortcut, make sure the app is installed"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"To add the Wallet app as a shortcut, make sure at least one card has been added"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"To add the QR code scanner as a shortcut, make sure a camera app is installed"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"To add the Home app as a shortcut, make sure the app is installed"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• At least one device is available"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Select a default notes app to use the notetaking shortcut"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 4d0ed14f4348..23d2edc96da7 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Apps de trabajo"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pausa"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al atardecer"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hasta el amanecer"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir la configuración de ampliación"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Cerrar configuración de ampliación"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Salir del modo de edición"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastra la esquina para cambiar el tamaño"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Desplazamiento en diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Para agregar la app de Billetera como acceso directo, asegúrate de haber instalado la app"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Para agregar la app de Billetera como acceso directo, asegúrate de haber agregado al menos una tarjeta"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Para agregar el escáner de código QR como acceso directo, asegúrate de haber instalado la app de la cámara"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Para agregar la app de Home como acceso directo, asegúrate de haber instalado la app"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Hay al menos un dispositivo disponible"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecciona una app de notas predeterminada para usar el acceso directo de toma de notas"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 953171a5f4f6..1eb0abce9b5d 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplicaciones de trabajo"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pausa"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al atardecer"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hasta el amanecer"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir ajustes de ampliación"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Cerrar ajustes de ampliación"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastra la esquina para cambiar el tamaño"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir ir en diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Para añadir la aplicación Wallet como acceso directo, asegúrate de que está instalada"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Para añadir la aplicación Wallet como acceso directo, asegúrate de haber añadido al menos una tarjeta"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Para añadir el escáner de códigos QR como acceso directo, asegúrate de que hay instalada una aplicación de cámara"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Para añadir la aplicación Home como acceso directo, asegúrate de que está instalada"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Al menos un dispositivo debe estar disponible"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecciona una aplicación de notas predeterminada para usar el acceso directo de toma de notas"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 1e7bbf44e5d9..38816356028e 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limiit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> hoiatus"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Töörakendused"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Peatatud"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Öövalgus"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Sissel. päikeselooj."</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Kuni päikesetõusuni"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ava suurendamisseaded"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Sule suurendamisseaded"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Suuruse muutmiseks lohistage nurka"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Luba diagonaalne kerimine"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Muuda suurust"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ava <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Rahakotirakenduse otsetee lisamiseks veenduge, et rakendus oleks installitud"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Rahakotirakenduse otsetee lisamiseks veenduge, et vähemalt üks kaart oleks lisatud"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR-koodi skanneri otsetee lisamiseks veenduge, et kaamerarakendus oleks installitud"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Rakenduse Home otsetee lisamiseks veenduge, et rakendus oleks installitud"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Vähemalt üks seade on saadaval"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Valige märkmete tegemise vaikerakendus, et kasutada märkmete tegemise otseteed"</string> @@ -1167,7 +1169,7 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Määrake seadetes märkmete vaikerakendus."</string> <string name="install_app" msgid="5066668100199613936">"Installi rakendus"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon ja kaamera"</string> - <string name="privacy_dialog_summary" msgid="2458769652125995409">"Rakenduse hiljutine kasutamine"</string> + <string name="privacy_dialog_summary" msgid="2458769652125995409">"Rakenduste hiljutine kasutamine"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Kuva hiljutine juurdepääs"</string> <string name="privacy_dialog_done_button" msgid="4504330708531434263">"Valmis"</string> <string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Laiendamine ja valikute kuvamine"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 3ce64b28f4d1..0121abc535ee 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Muga: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Abisua: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Laneko aplikazioak"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pausatuta"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Gaueko argia"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ilunabarrean"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Ilunabarrera arte"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ireki luparen ezarpenak"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Itxi luparen ezarpenak"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastatu izkina bat tamaina aldatzeko"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Eman diagonalki gora eta behera egiteko aukera erabiltzeko baimena"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Aldatu tamaina"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ireki <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Diru-zorroa aplikazioa lasterbide gisa gehitzeko, ziurtatu aplikazioa instalatuta dagoela."</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Diru-zorroa aplikazioa lasterbide gisa gehitzeko, ziurtatu gutxienez txartel bat gehitu dela."</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR kodeen eskanerra lasterbide gisa gehitzeko, ziurtatu kameraren aplikazioa instalatuta dagoela."</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home aplikazioa lasterbide gisa gehitzeko, ziurtatu aplikazioa instalatuta dagoela."</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Gutxienez gailu bat erabilgarri dago."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Oharrak idazteko lasterbidea erabiltzeko, hautatu oharretarako aplikazio lehenetsia."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 33bd9f47fb2d..a22f6dab73a4 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> محدودیت"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"هشدار <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"برنامههای کاری"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"موقتاً متوقف شد"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"نور شب"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"غروب روشن میشود"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"تا طلوع"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"باز کردن تنظیمات درشتنمایی"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"بستن تنظیمات درشتنمایی"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"برای تغییر اندازه، گوشه را بکشید"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"اجازه دادن برای پیمایش قطری"</string> <string name="accessibility_resize" msgid="5733759136600611551">"تغییر اندازه"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"باز کردن <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"برای افزودن برنامه «کیف پول» بهعنوان میانبر، مطمئن شوید این برنامه نصب شده باشد"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"برای افزودن برنامه «کیف پول» بهعنوان میانبر، مطمئن شوید حداقل یک کارت اضافه شده باشد"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"برای افزودن «کدخوان پاسخسریع» بهعنوان میانبر، مطمئن شوید برنامه دوربین نصب شده باشد"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"برای افزودن برنامه Home بهعنوان میانبر، مطمئن شوید این برنامه نصب شده باشد"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• حداقل یک دستگاه دردسترس باشد"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"برای استفاده از میانبر یادداشتبرداری، برنامه یادداشت پیشفرضی را انتخاب کنید"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 6da1262b04d4..d87f2f66ff7a 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"kiintiö <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> – varoitus"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Työsovellukset"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Keskeytetty"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Yövalo"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Auringon laskiessa"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Auringonnousuun"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Avaa suurennusasetukset"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Sulje suurennusasetukset"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Muuta kokoa vetämällä kulmaa"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Salli diagonaalinen vierittäminen"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Muuta kokoa"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Avaa <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Jos haluat lisätä Wallet-sovelluksen pikakuvakkeena, varmista, että se on asennettuna"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Jos haluat lisätä Wallet-sovelluksen pikakuvakkeena, varmista, että lisättynä on vähintään yksi kortti"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Jos haluat lisätä QR-koodiskannerin pikakuvakkeena, varmista, että kamerasovellus on asennettuna"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Jos haluat lisätä Home-sovelluksen pikakuvakkeena, varmista, että se on asennettuna"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Ainakin yksi laite on käytettävissä"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Valitse muistiinpanojen tekemisen oletussovellus, jota käytetään pikakuvakkeen avulla"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 2f8fcfbccdfa..1c6b50834043 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Applications professionnelles"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pause"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éclairage nocturne"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activé la nuit"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Jusqu\'à l\'aube"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ouvrir les paramètres d\'agrandissement"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fermer les paramètres d\'agrandissement"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Faire glisser le coin pour redimensionner"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Autoriser défilement diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionner"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ouvrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Pour ajouter l\'application portefeuille sous forme de raccourci, assurez-vous que l\'application est installée"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Pour ajouter l\'application Portefeuille sous forme de raccourci, assurez-vous qu\'au moins une carte a été ajoutée"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Pour ajouter le numériseur de code QR sous forme de raccourci, assurez-vous qu\'une application d\'appareil photo est installée"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Pour ajouter l\'application Home sous forme de raccourci, assurez-vous que l\'application est installée"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• qu\'au moins un appareil est utilisable;"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Sélectionnez une application de prise de notes par défaut pour utiliser le raccourci de prise de notes"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 3a77d37107fe..420fd85d9725 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> au maximum"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Applis pro"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pause"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éclairage nocturne"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activé la nuit"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Jusqu\'à l\'aube"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ouvrir les paramètres d\'agrandissement"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fermer les paramètres d\'agrandissement"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Faire glisser le coin pour redimensionner"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Autoriser le défilement diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionner"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ouvrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Pour ajouter l\'appli Wallet comme raccourci, vérifiez que l\'appli est installée"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Pour ajouter l\'appli Wallet comme raccourci, assurez-vous qu\'au moins une carte a été ajoutée"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Pour ajouter le lecteur de code QR comme raccourci, assurez-vous qu\'une appli d\'appareil photo est installée"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Pour ajouter l\'application Home comme raccourci, vérifiez que l\'appli est installée"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Au moins un appareil est disponible"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Sélectionnez une appli de notes par défaut pour utiliser le raccourci de prise de notes"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 6390dc81b271..3389dd83b270 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplicacións do traballo"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"En pausa"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activación ao solpor"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Ata o amencer"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir configuración da ampliación"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Pechar configuración de ampliación"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastrar a esquina para cambiar o tamaño"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir desprazamento diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Para engadir a aplicación Wallet como atallo, asegúrate de que estea instalada"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Para engadir a aplicación Wallet como atallo, asegúrate de que se incluíse polo menos unha tarxeta"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Para engadir o escáner de códigos QR como atallo, asegúrate de que a aplicación de cámara estea instalada"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Para engadir a aplicación Google Home como atallo, asegúrate de que estea instalada"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Ten que haber polo menos un dispositivo dispoñible"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecciona unha aplicación de notas predeterminada para usar o atallo de tomar notas"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 6a4fa7fe0784..92ab77fa00f7 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> મર્યાદા"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ચેતવણી"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ઑફિસ માટેની ઍપ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"થોભાવ્યું"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"રાત્રિ પ્રકાશ"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"સૂર્યાસ્ત વખતે"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"સૂર્યોદય સુધી"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"મોટા કરવાના સેટિંગ ખોલો"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"મોટા કરવાના સેટિંગ બંધ કરો"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"કદ બદલવા માટે ખૂણો ખેંચો"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ડાયગોનલ સ્ક્રોલિંગને મંજૂરી આપો"</string> <string name="accessibility_resize" msgid="5733759136600611551">"કદ બદલો"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ખોલો"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet ઍપને શૉર્ટકટ તરીકે ઉમેરવા માટે, ખાતરી કરો કે ઍપ ઇન્સ્ટૉલ કરેલી હોય"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet ઍપને શૉર્ટકટ તરીકે ઉમેરવા માટે, ખાતરી કરો કે ઓછામાં ઓછું એક કાર્ડ ઉમેરવામાં આવ્યું હોય"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR કોડ સ્કૅનરને શૉર્ટકટ તરીકે ઉમેરવા માટે, ખાતરી કરો કે કૅમેરા ઍપ ઇન્સ્ટૉલ કરેલી હોય"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home ઍપને શૉર્ટકટ તરીકે ઉમેરવા માટે, ખાતરી કરો કે ઍપ ઇન્સ્ટૉલ કરેલી હોય"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ઓછામાં ઓછું એક ડિવાઇસ ઉપલબ્ધ છે"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"નોંધ લેવાના શૉર્ટકટનો ઉપયોગ કરવા માટે, નોંધ માટેની ડિફૉલ્ટ ઍપ પસંદ કરો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 091dd75c69a4..29f80aa7e70a 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -295,6 +295,8 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"वर्क ऐप्लिकेशन"</string> + <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) --> + <skip /> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"नाइट लाइट"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"शाम को चालू की जाएगी"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सुबह तक चालू रहेगी"</string> @@ -864,6 +866,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ज़ूम करने की सुविधा वाली सेटिंग खोलें"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ज़ूम करने की सुविधा वाली सेटिंग को बंद करें"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"साइज़ बदलने के लिए, कोने को खींचें और छोड़ें"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"तिरछी दिशा में स्क्रोल करने की अनुमति दें"</string> <string name="accessibility_resize" msgid="5733759136600611551">"साइज़ बदलें"</string> @@ -1133,7 +1137,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> खोलें"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet ऐप्लिकेशन को शॉर्टकट के तौर पर जोड़ने के लिए, पक्का करें कि ऐप्लिकेशन इंस्टॉल किया गया हो"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet ऐप्लिकेशन को शॉर्टकट के तौर पर जोड़ने के लिए, पक्का करें कि कम से कम एक कार्ड जोड़ा गया हो"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"क्यूआर कोड स्कैनर को शॉर्टकट के तौर पर जोड़ने के लिए, पक्का करें कि कैमरा ऐप्लिकेशन इंस्टॉल किया गया हो"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home ऐप्लिकेशन को शॉर्टकट के तौर पर जोड़ने के लिए, पक्का करें कि ऐप्लिकेशन इंस्टॉल किया गया हो"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• कम से कम एक डिवाइस उपलब्ध है"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"नोट लेने से जुड़ा शॉर्टकट इस्तेमाल करने के लिए, नोट लेने का डिफ़ॉल्ट ऐप्लिकेशन चुनें"</string> @@ -1168,7 +1171,7 @@ <string name="install_app" msgid="5066668100199613936">"ऐप्लिकेशन इंस्टॉल करें"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"माइक्रोफ़ोन और कैमरा"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"हाल ही में इस्तेमाल करने वाला ऐप्लिकेशन"</string> - <string name="privacy_dialog_more_button" msgid="7610604080293562345">"हाल ही का ऐक्सेस देखें"</string> + <string name="privacy_dialog_more_button" msgid="7610604080293562345">"हाल में ऐक्सेस करने वाले ऐप"</string> <string name="privacy_dialog_done_button" msgid="4504330708531434263">"हो गया"</string> <string name="privacy_dialog_expand_action" msgid="9129262348628331377">"बड़ा करें और विकल्प दिखाएं"</string> <string name="privacy_dialog_collapse_action" msgid="277419962019466347">"छोटा करें"</string> @@ -1179,7 +1182,7 @@ <string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"फ़ोन कॉल पर इस्तेमाल किया जा रहा है"</string> <string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"हाल ही में, फ़ोन कॉल में इस्तेमाल किया गया"</string> <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर इस्तेमाल किया जा रहा है"</string> - <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"हाल ही में, <xliff:g id="APP_NAME">%1$s</xliff:g> ने इस्तेमाल किया"</string> + <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"हाल ही में <xliff:g id="APP_NAME">%1$s</xliff:g> पर इस्तेमाल किया गया"</string> <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) पर इस्तेमाल किया जा रहा है"</string> <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"हाल ही में, <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ने इस्तेमाल किया"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<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> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index baf74d29ef57..e58bc767b666 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ograničenje od <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Poslovne aplikacije"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pauzirano"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Noćno svjetlo"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Uključuje se u suton"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do izlaska sunca"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvori postavke povećavanja"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zatvori postavke povećavanja"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Zatvaranje načina uređivanja"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Povucite kut da biste promijenili veličinu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dopusti dijagonalno pomicanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Promijeni veličinu"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvorite <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Za dodavanje aplikacije Wallet kao prečaca provjerite je li instalirana"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Provjerite je li dodana barem jedna kartica kako biste dodali aplikaciju Wallet kao prečac"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Provjerite je li instalirana aplikacija kamere kako biste dodali čitač QR koda kao prečac"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Provjerite je li aplikacija Home instalirana kako biste je dodali kao prečac"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Dostupan je najmanje jedan uređaj"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Odaberite zadanu aplikaciju za bilješke da biste koristili prečac za pisanje bilježaka"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index e2256822a82a..49ba82bf6b02 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> korlát"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Figyelem! <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Munkahelyi alkalmazások"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Szüneteltetve"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Éjszakai fény"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Be: naplemente"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Napfelkeltéig"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Nagyítási beállítások megnyitása"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Nagyítási beállítások bezárása"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Az átméretezéshez húzza a kívánt sarkot"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Átlós görgetés engedélyezése"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Átméretezés"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> megnyitása"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Ha szeretné felvenni Wallet alkalmazást gyorsparancsként, gondoskodjon az app telepítéséről"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Ha szeretné felvenni Wallet alkalmazást gyorsparancsként, győződjön meg róla, hogy hozzáadott legalább egy kártyát a szolgáltatáshoz"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Ha szeretné felvenni a QR-kód-szkennelőt gyorsparancsként, győződjön meg róla, hogy van az eszközre telepítve kameraalkalmazás"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Ha szeretné felvenni Home appot gyorsparancsként, gondoskodjon az alkalmazás telepítéséről"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Legalább egy eszköz rendelkezésre áll"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Válassza ki az alapértelmezett jegyzetkészítő alkalmazást, amelyet a jegyzetelési gyorsparancshoz szeretne használni"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index e573682a4443..7b72456a1a5e 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Սահմանաչափ՝ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> զգուշացում"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Աշխատանքային հավելվածներ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Դադարեցված է"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Գիշերային ռեժիմ"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Կմիացվի մայրամուտին"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Մինչև լուսաբաց"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Բացել խոշորացման կարգավորումները"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Փակել խոշորացման կարգավորումները"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Քաշեք անկյունը՝ չափը փոխելու համար"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Թույլատրել անկյունագծով ոլորումը"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Փոխել չափը"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Բացել <xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet հավելվածի դյուրանցումն ավելացնելու համար համոզվեք, որ հավելվածը տեղադրված է"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet հավելվածի դյուրանցումն ավելացնելու համար համոզվեք, որ առնվազն մեկ քարտ ավելացված է"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR կոդերի սկաների դյուրանցումն ավելացնելու համար համոզվեք, որ տեսախցիկի հավելվածը տեղադրված է"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home հավելվածի դյուրանցումն ավելացնելու համար համոզվեք, որ հավելվածը տեղադրված է"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Հասանելի է առնվազն մեկ սարք"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Ընտրեք նշումների կանխադրված հավելված՝ նշումների ստեղծման դյուրանցումն օգտագործելու համար"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 0e8c3a17ba9e..fc12a14ee8bb 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Batas <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Peringatan <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplikasi kerja"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Dijeda"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Cahaya Malam"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aktif saat malam"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Sampai pagi"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buka setelan pembesaran"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tutup setelan pembesaran"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Tarik pojok persegi untuk mengubah ukuran"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Izinkan scrolling diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ubah ukuran"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buka <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Untuk menambahkan aplikasi Wallet sebagai pintasan, pastikan aplikasi sudah diinstal"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Untuk menambahkan aplikasi Wallet sebagai pintasan, pastikan minimal satu kartu telah ditambahkan"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Untuk menambahkan pemindai kode QR sebagai pintasan, pastikan aplikasi kamera sudah diinstal"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Untuk menambahkan aplikasi Home sebagai pintasan, pastikan aplikasi sudah diinstal"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Tersedia minimal satu perangkat"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Pilih aplikasi catatan default untuk menggunakan pintasan pembuatan catatan"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 8411db497f84..2c784a6f8dba 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> hámark"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> viðvörun"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Vinnuforrit"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Hlé"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Næturljós"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Kveikt við sólsetur"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Til sólarupprásar"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Opna stillingar stækkunar"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Loka stillingum stækkunar"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dragðu horn til að breyta stærð"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Leyfa skáflettingu"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Breyta stærð"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Opna <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Til að bæta Veskisforritinu við sem flýtileið skaltu ganga úr skugga um að forritið sé uppsett"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Til að bæta Veskisforritinu við sem flýtileið skaltu ganga úr skugga um að hafa bætt að minnsta kosti einu korti við"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Til að bæta QR-kóðaskanna við sem flýtileið skaltu ganga úr skugga um að myndavélarforrit sé uppsett"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Til að bæta Home-forritinu við sem flýtileið skaltu ganga úr skugga um að forritið sé uppsett"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Að minnsta kosti eitt tæki er tiltækt"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Veldu sjálfgefið glósuforrit til að nota flýtileið fyrir glósugerð"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 561da813e75e..7c222977c764 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite di <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avviso <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"App di lavoro"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"In pausa"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luminosità notturna"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Attivata al tramonto"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Fino all\'alba"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Apri le impostazioni di ingrandimento"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Chiudi impostazioni di ingrandimento"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Trascina l\'angolo per ridimensionare"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Scorrimento diagonale"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ridimensiona"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Apri <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Per aggiungere l\'app Wallet come scorciatoia, assicurati che l\'app sia installata"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Per aggiungere l\'app Wallet come scorciatoia, assicurati che sia stata aggiunta almeno una carta"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Per aggiungere lo scanner di codici QR come scorciatoia, assicurati che ci sia un\'app fotocamera installata"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Per aggiungere l\'app Home come scorciatoia, assicurati che l\'app sia installata"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Ci sia almeno un dispositivo disponibile"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Seleziona un\'app per le note predefinita per usare la scorciatoia per l\'aggiunta di note"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index c905b047ad35..f1dd4972db63 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"הגבלה של <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"אזהרה – <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"אפליקציות לעבודה"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"בהשהיה"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"תאורת לילה"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"התכונה מופעלת בשקיעה"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"עד הזריחה"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"פתיחת הגדרות ההגדלה"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"סגירת הגדרות ההגדלה"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"צריך לגרור את הפינה כדי לשנות את הגודל"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"הפעלת גלילה באלכסון"</string> <string name="accessibility_resize" msgid="5733759136600611551">"שינוי גודל"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"פתיחת <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"כדי להוסיף את אפליקציית Wallet כקיצור דרך, צריך לוודא שהאפליקציה מותקנת"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"כדי להוסיף את אפליקציית Wallet כקיצור דרך, צריך לוודא שנוסף לפחות כרטיס אחד"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"כדי להוסיף את סורק קודי ה-QR כקיצור דרך, צריך לוודא שמותקנת אפליקציית מצלמה"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"כדי להוסיף את אפליקציית Home כקיצור דרך, צריך לוודא שהאפליקציה מותקנת"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• יש לפחות מכשיר אחד זמין"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"צריך לבחור אפליקציית פתקים שתיפתח כברירת מחדל כשייעשה שימוש במקש הקיצור לכתיבת פתקים"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 8a40d718903a..5d3919e99e48 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"警告: 上限は<xliff:g id="DATA_LIMIT">%s</xliff:g>です"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"仕事用アプリ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"一時停止中"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"夜間モード"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"日の入りに ON"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"日の出まで"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"画面の拡大設定を開く"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"拡大の設定を閉じる"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"編集モードを終了"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"サイズを変更するには角をドラッグ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"斜めスクロールを許可"</string> <string name="accessibility_resize" msgid="5733759136600611551">"サイズ変更"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> を開く"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"ウォレット アプリをショートカットとして追加するには、アプリがインストールされていることを確認してください"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"ウォレット アプリをショートカットとして追加するには、カードが 1 枚以上追加されていることを確認してください"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR コードスキャナをショートカットとして追加するには、カメラアプリがインストールされていることを確認してください"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Google Home アプリをショートカットとして追加するには、アプリがインストールされていることを確認してください"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• 利用できるデバイスが 1 台以上ある"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"メモのショートカットを使用するデフォルトのメモアプリを選択してください"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 0c5c76efabd4..daa0ae7a4989 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ლიმიტი: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> გაფრთხილება"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"სამსახურის აპები"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"დაპაუზებულია"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ღამის განათება"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ჩაირთოს მზის ჩასვლისას"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"მზის ამოსვლამდე"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"გახსენით გადიდების პარამეტრები"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"გადიდების პარამეტრების დახურვა"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"რედაქტირების რეჟიმიდან გასვლა"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ჩავლებით გადაიტანეთ კუთხე ზომის შესაცვლელად"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"დიაგონალური გადაადგილების დაშვება"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ზომის შეცვლა"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> აპის გახსნა"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"საფულის აპის მალსახმობის დასამატებლად დარწმუნდით, რომ აპი დაინსტალირებულია"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"საფულის აპის მალსახმობის დასამატებლად დარწმუნდით, რომ დამატებულია მინიმუმ ერთი ბარათი"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR კოდის სკანერის მალსახმობის დასამატებლად დარწმუნდით, რომ დაინსტალირებულია კამერის აპი"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"მთავარი აპის მალსახმობის დასამატებლად დარწმუნდით, რომ აპი დაინსტალირებულია"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ხელმისაწვდომია მინიმუმ ერთი მოწყობილობა"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"აირჩიეთ ჩანიშვნების ნაგულისხმევი აპი, რათა ჩანიშვნების შექმნის მალსახმობი გამოიყენოთ"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 953292d743cf..18f78552655d 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> шегі"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> туралы ескерту"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Жұмыс қолданбалары"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Кідіртілген"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Түнгі жарық"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Күн батқанда қосу"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Күн шыққанға дейін"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ұлғайту параметрлерін ашу"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Ұлғайту параметрлерін жабу"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Өлшемін өзгерту үшін бұрышынан сүйреңіз."</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Диагональ бойынша айналдыруға рұқсат беру"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Өлшемін өзгерту"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ашу"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet қолданбасын таңбаша ретінде қосу үшін қолданбаның орнатылғанын тексеріңіз."</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet қолданбасын таңбаша ретінде қосу үшін кемінде бір картаның қосылғанын тексеріңіз."</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR кодының сканерін таңбаша ретінде қосу үшін камера қолданбасының орнатылғанын тексеріңіз."</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home қолданбасын таңбаша ретінде қосу үшін қолданбаның орнатылғанын тексеріңіз."</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Кемінде бір құрылғы қолжетімді."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Жазба жазу таңбашасын пайдалану үшін әдепкі жазба қолданбаны таңдаңыз."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 535400f85afe..d5ef4b5b4734 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ដែនកំណត់ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ការព្រមាន"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"កម្មវិធីការងារ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"បានផ្អាក"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ពន្លឺពេលយប់"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"បើកនៅពេលថ្ងៃលិច"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"រហូតដល់ពេលថ្ងៃរះ"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"បើកការកំណត់ការពង្រីក"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"បិទការកំណត់ការពង្រីក"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"អូសជ្រុងដើម្បីប្ដូរទំហំ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"អនុញ្ញាតការរំកិលបញ្ឆិត"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ប្ដូរទំហំ"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"បើក <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"ដើម្បីបញ្ចូលកម្មវិធី Wallet ជាផ្លូវកាត់ សូមប្រាកដថាកម្មវិធីត្រូវបានដំឡើង"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"ដើម្បីបញ្ចូលកម្មវិធី Wallet ជាផ្លូវកាត់ សូមប្រាកដថាបានបញ្ចូលកាតយ៉ាងហោចណាស់មួយ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"ដើម្បីបញ្ចូលកម្មវិធីស្កេនកូដ QR ជាផ្លូវកាត់ សូមប្រាកដថាកម្មវិធីកាមេរ៉ាត្រូវបានដំឡើង"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"ដើម្បីបញ្ចូលកម្មវិធី Home ជាផ្លូវកាត់ សូមប្រាកដថាកម្មវិធីត្រូវបានដំឡើង"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ឧបករណ៍យ៉ាងតិចមួយអាចប្រើបាន"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"ជ្រើសរើសកម្មវិធីកំណត់ចំណាំលំនាំដើម ដើម្បីប្រើផ្លូវកាត់សម្រាប់ការកត់ចំណាំ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 0a2bb6496646..0b82b5589f70 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಮಿತಿ"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಎಚ್ಚರಿಕೆ"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳು"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ನೈಟ್ ಲೈಟ್"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ಸೂರ್ಯಾಸ್ತದಲ್ಲಿ"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ಹಿಗ್ಗಿಸುವಿಕೆ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ಮ್ಯಾಗ್ನಿಫಿಕೇಶನ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಮುಚ್ಚಿರಿ"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ಎಡಿಟ್ ಮೋಡ್ನಿಂದ ನಿರ್ಗಮಿಸಿ"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ಮರುಗಾತ್ರಗೊಳಿಸಲು ಮೂಲೆಯನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ಡಯಾಗನಲ್ ಸ್ಕ್ರೋಲಿಂಗ್ ಅನ್ನು ಅನುಮತಿಸಿ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string> @@ -1038,7 +1040,7 @@ <string name="game_status" msgid="1340694320630973259">"ಪ್ಲೇ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="empty_user_name" msgid="3389155775773578300">"ಸ್ನೇಹಿತರು"</string> <string name="empty_status" msgid="5938893404951307749">"ರಾತ್ರಿ ಚಾಟ್ ಮಾಡೋಣ!"</string> - <string name="status_before_loading" msgid="1500477307859631381">"ವಿಷಯ ಶೀಘ್ರದಲ್ಲೇ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ"</string> + <string name="status_before_loading" msgid="1500477307859631381">"ಕಂಟೆಂಟ್ ಶೀಘ್ರದಲ್ಲೇ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ"</string> <string name="missed_call" msgid="4228016077700161689">"ಮಿಸ್ಡ್ ಕಾಲ್"</string> <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string> <string name="people_tile_description" msgid="8154966188085545556">"ಇತ್ತೀಚಿನ ಸಂದೇಶಗಳು, ಮಿಸ್ಡ್ ಕಾಲ್ಗಳು ಮತ್ತು ಸ್ಥಿತಿ ಅಪ್ಡೇಟ್ಗಳು"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"ವಾಲೆಟ್ ಆ್ಯಪ್ ಅನ್ನು ಶಾರ್ಟ್ಕಟ್ ಆಗಿ ಸೇರಿಸಲು, ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"ವಾಲೆಟ್ ಆ್ಯಪ್ ಅನ್ನು ಶಾರ್ಟ್ಕಟ್ ಆಗಿ ಸೇರಿಸಲು, ಕನಿಷ್ಠ ಒಂದು ಕಾರ್ಡ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನರ್ ಅನ್ನು ಶಾರ್ಟ್ಕಟ್ ಆಗಿ ಸೇರಿಸಲು, ಕ್ಯಾಮರಾ ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home ಆ್ಯಪ್ ಅನ್ನು ಶಾರ್ಟ್ಕಟ್ ಆಗಿ ಸೇರಿಸಲು, ಆ್ಯಪ್ ಇನ್ಸ್ಟಾಲ್ ಆಗಿದೆಯೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ಕನಿಷ್ಠ ಒಂದು ಸಾಧನ ಲಭ್ಯವಿದೆ"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"ನೋಟ್ಸ್ ಮಾಡಿಕೊಳ್ಳುವಿಕೆ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಬಳಸಲು ಡೀಫಾಲ್ಟ್ ಟಿಪ್ಪಣಿಗಳ ಆ್ಯಪ್ ಆಯ್ಕೆಮಾಡಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index d267fed73940..ea7d5c26295e 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"한도: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 경고"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"직장 앱"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"일시중지됨"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"야간 조명"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"일몰에"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"일출까지"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"확대 설정 열기"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"확대 설정 닫기"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"모서리를 드래그하여 크기 조절"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"대각선 스크롤 허용"</string> <string name="accessibility_resize" msgid="5733759136600611551">"크기 조절"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> 열기"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"월렛 앱을 바로가기로 추가하려면 앱이 설치되어 있는지 확인하세요."</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"월렛 앱을 바로가기로 추가하려면 하나 이상의 카드가 추가되어 있는지 확인하세요."</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR 코드 스캐너를 바로가기로 추가하려면 카메라 앱이 설치되어 있는지 확인하세요."</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home 앱을 바로가기로 추가하려면 앱이 설치되어 있는지 확인하세요."</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• 1대 이상의 기기를 사용할 수 있습니다."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"메모 바로가기를 사용하려면 기본 메모 앱을 선택합니다."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 768e8a559f56..cb9e67a76ef2 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> чектөө"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> эскертүү"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Жумуш колдонмолору"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Тындырылды"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Түнкү режим"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Күн батканда күйөт"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Күн чыкканга чейин"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Чоңойтуу параметрлерин ачуу"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Чоңойтуу параметрлерин жабуу"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Өлчөмүн өзгөртүү үчүн бурчун сүйрөңүз"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Диагональ боюнча сыдырууга уруксат берүү"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Өлчөмүн өзгөртүү"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ачуу"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Капчык колдонмосун ыкчам баскыч катары кошуу үчүн колдонмону орнотуу керек"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Капчык колдонмосун ыкчам баскыч катары кошуу үчүн кеминде бир картаны кошуу керек"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR кодунун сканерин ыкчам баскыч катары кошуу үчүн камера колдонмосун орнотуу керек"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home колдонмосун ыкчам баскыч катары кошуу үчүн колдонмону орнотуу керек"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Кеминде бир түзмөк жеткиликтүү"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Эскертме жазуу ыкчам баскычын колдонуу үчүн демейки эскертме жазуу колдонмосун тандаңыз"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index e8b400393934..3e26137d0c92 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ຈຳກັດ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"ຄຳເຕືອນ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ແອັບບ່ອນເຮັດວຽກ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"ຢຸດໄວ້ຊົ່ວຄາວ"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ແສງກາງຄືນ"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ເປີດຕອນຕາເວັນຕົກ"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ຈົນກວ່າຕາເວັນຂຶ້ນ"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ເປີດການຕັ້ງຄ່າການຂະຫຍາຍ"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ປິດການຕັ້ງຄ່າການຂະຫຍາຍ"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ລາກຢູ່ມຸມເພື່ອປັບຂະໜາດ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ອະນຸຍາດໃຫ້ເລື່ອນທາງຂວາງ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ປ່ຽນຂະໜາດ"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"ເປີດ <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"ເພື່ອເພີ່ມແອັບ Wallet ເປັນທາງລັດ, ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າໄດ້ຕິດຕັ້ງແອັບແລ້ວ"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"ເພື່ອເພີ່ມແອັ Wallet ເປັນທາງລັດ, ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າໄດ້ເພີ່ມຢ່າງໜ້ອຍ 1 ບັດແລ້ວ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"ເພື່ອເພີ່ມຕົວສະແກນລະຫັດ QR ເປັນທາງລັດ, ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າໄດ້ຕິດຕັ້ງແອັບກ້ອງຖ່າຍຮູບແລ້ວ"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"ເພື່ອເພີ່ມແອັບ Home ເປັນທາງລັດ, ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າໄດ້ຕິດຕັ້ງແອັບແລ້ວ"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ມີຢ່າງໜ້ອຍ 1 ອຸປະກອນພ້ອມໃຫ້ນຳໃຊ້"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"ເລືອກແອັບບັນທຶກເລີ່ມຕົ້ນເພື່ອໃຊ້ທາງລັດການຈົດບັນທຶກ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 24265338487c..a7d874c74362 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limitas: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> įspėjimas"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Darbo programos"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pristabdyta"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nakties šviesa"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Per saulėlydį"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Iki saulėtekio"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Atidaryti didinimo nustatymus"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Uždaryti didinimo nustatymus"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Norėdami keisti dydį, vilkite kampą"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Slinkimo įstrižai leidimas"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Pakeisti dydį"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Atidaryti „<xliff:g id="APPNAME">%1$s</xliff:g>“"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Jei norite pridėti programą „Wallet“ kaip šaukinį, įsitikinkite, kad programa įdiegta"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Jei norite pridėti programą „Wallet“ kaip šaukinį, įsitikinkite, kad pridėta bent viena kortelė"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Jei norite pridėti QR kodų skaitytuvą kaip šaukinį, įsitikinkite, kad fotoaparato programa įdiegta"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Jei norite pridėti programą „Home“ kaip šaukinį, įsitikinkite, kad programa įdiegta"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Pasiekiamas bent vienas įrenginys"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Pasirinkite numatytąją užrašų programą, kuriai norite naudoti užrašų kūrimo šaukinį"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 9bda2b817e21..972380cb37b7 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ierobežojums: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> brīdinājums"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Darba lietotnes"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Darbība apturēta"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nakts režīms"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Saulrietā"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Līdz saullēktam"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Atvērt palielinājuma iestatījumus"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Aizvērt palielinājuma iestatījumus"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Velciet stūri, lai mainītu izmērus"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Atļaut ritināšanu pa diagonāli"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Mainīt lielumu"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Atvērt lietotni <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Lai varētu pievienot lietotni Maks kā saīsni, lietotnei ir jābūt instalētai."</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Lai varētu pievienot lietotni Maks kā saīsni, ir jābūt pievienotai vismaz vienai kartei."</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Lai varētu pievienot lietotni Kvadrātkoda skeneris kā saīsni, ir jābūt instalētai kameras lietotnei."</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Lai varētu pievienot lietotni Home kā saīsni, lietotnei ir jābūt instalētai."</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Ir pieejama vismaz viena ierīce."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Atlasiet noklusējuma piezīmju lietotni, lai izmantotu piezīmju pierakstīšanas saīsni."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 9b8b04cd58e1..4b1e5a81d74d 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Лимит: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Предупредување: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Работни апликации"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Паузирано"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ноќно светло"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Вклуч. на зајдисонце"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До изгрејсонце"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголемувајте дел од екранот"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Отвори поставки за зголемување"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затворете ги поставките за зголемување"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Повлечете на аголот за да ја промените големината"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволете дијагонално лизгање"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Промени големина"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отворете ја <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"За да ја додадете апликацијата Wallet како кратенка, апликацијата мора да е инсталирана"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"За да ја додадете апликацијата Wallet како кратенка, мора да имате додадено најмалку една картичка"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"За да го додадете скенерот на QR-кодови како кратенка, погрижете се дека имате инсталирано апликација за камера"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"За да ја додадете апликацијата Home како кратенка, апликацијата мора да е инсталирана"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• достапен е најмалку еден уред"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Изберете стандардна апликација за белешки за да ја користите кратенката за фаќање белешки"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index b7a03d8bf358..7202f5e69bea 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> പരിധി"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> മുന്നറിയിപ്പ്"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ഔദ്യോഗിക ആപ്പുകൾ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"തൽക്കാലം നിർത്തി"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"നൈറ്റ് ലൈറ്റ്"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"സൂര്യാസ്തമയത്തിന്"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"സൂര്യോദയം വരെ"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"മാഗ്നിഫിക്കേഷൻ ക്രമീകരണം തുറക്കുക"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"മാഗ്നിഫിക്കേഷൻ ക്രമീകരണം അടയ്ക്കുക"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"എഡിറ്റ് മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"വലുപ്പം മാറ്റാൻ മൂല വലിച്ചിടുക"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ഡയഗണൽ സ്ക്രോളിംഗ് അനുവദിക്കുക"</string> <string name="accessibility_resize" msgid="5733759136600611551">"വലുപ്പം മാറ്റുക"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> തുറക്കുക"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"കുറുക്കുവഴിയായി Wallet ആപ്പ് ചേർക്കാൻ, ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"കുറുക്കുവഴിയായി Wallet ആപ്പ് ചേർക്കാൻ, ഒരു കാർഡെങ്കിലും ചേർത്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"കുറുക്കുവഴിയായി QR കോഡ് സ്കാനർ ചേർക്കാൻ, ക്യാമറാ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"കുറുക്കുവഴിയായി Home ആപ്പ് ചേർക്കാൻ, ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ഒരു ഉപകരണമെങ്കിലും ലഭ്യമാണ്"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"കുറിപ്പ് രേഖപ്പെടുത്തൽ കുറുക്കുവഴി ഉപയോഗിക്കുന്നതിന് ഒരു ഡിഫോൾട്ട് കുറിപ്പ് ആപ്പ് തിരഞ്ഞെടുക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 7a2ca614c0e5..470e0fdbe24f 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> хязгаар"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> анхааруулга"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Ажлын аппууд"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Түр зогсоосон"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Шөнийн гэрэл"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Нар жаргах үед"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Нар мандах хүртэл"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Томруулах тохиргоог нээх"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Томруулах тохиргоог хаах"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Хэмжээг өөрчлөхийн тулд булангаас чирнэ үү"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Хөндлөн гүйлгэхийг зөвшөөрөх"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Хэмжээг өөрчлөх"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g>-г нээх"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet аппыг товчлолоор нэмэхийн тулд уг аппыг суулгасан эсэхийг шалгана уу"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet аппыг товчлолоор нэмэхийн тулд дор хаяж нэг карт нэмсэн эсэхийг шалгана уу"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR код сканнерыг товчлолоор нэмэхийн тулд камерын аппыг суулгасан эсэхийг шалгана уу"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home аппыг товчлолоор нэмэхийн тулд уг аппыг суулгасан эсэхийг шалгана уу"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Дор хаяж нэг төхөөрөмж боломжтой"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Тэмдэглэл хөтлөх товчлолыг ашиглахын тулд тэмдэглэлийн өгөгдмөл аппыг сонгоно уу"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index ccbc2099b41e..f5abe7834768 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> मर्यादा"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावणी"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"कामाशी संबंधित अॅप्स"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"थांबवले आहे"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"रात्रीचा प्रकाश"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"संध्याकाळी सुरू असते"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सूर्योदयापर्यंत"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"मॅग्निफिकेशन सेटिंग्ज उघडा"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"मॅग्निफिकेशन सेटिंग्ज बंद करा"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"आकार बदलण्यासाठी कोपरा ड्रॅग करा"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"तिरपे स्क्रोल करण्याची अनुमती द्या"</string> <string name="accessibility_resize" msgid="5733759136600611551">"आकार बदला"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> उघडा"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet ॲप शॉर्टकट म्हणून जोडण्यासाठी ॲप इंस्टॉल केले असल्याची खात्री करा"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet ॲप शॉर्टकट म्हणून जोडण्यासाठी किमान एक कार्ड जोडले असल्याची खात्री करा"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR कोड स्कॅनर शॉर्टकट म्हणून जोडण्यासाठी कॅमेरा ॲप इंस्टॉल केले असल्याची खात्री करा"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home अॅप शॉर्टकट म्हणून जोडण्यासाठी ॲप इंस्टॉल केले असल्याची खात्री करा"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• किमान एक डिव्हाइस उपलब्ध करणे"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"टिपा घेण्यासंबंधित शॉर्टकट वापरण्याकरिता टिपांसाठीचे डीफॉल्ट अॅप निवडा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 586c96e48fb8..6b74c43c26e1 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> had"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Amaran <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Apl kerja"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Dijeda"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Cahaya Malam"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Dihidupkan pd senja"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hingga matahari terbit"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buka tetapan pembesaran"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tutup tetapan pembesaran"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Keluar daripada mod edit"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Seret sudut untuk mengubah saiz"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Benarkan penatalan pepenjuru"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ubah saiz"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buka <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Untuk menambahkan apl Wallet sebagai pintasan, pastikan apl telah dipasang"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Untuk menambahkan apl Wallet sebagai pintasan, pastikan sekurang-kurangnya satu kad telah ditambahkan"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Untuk menambahkan pengimbas kod QR sebagai pintasan, pastikan apl kamera telah dipasang"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Untuk menambahkan apl Home sebagai pintasan, pastikan apl telah dipasang"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Sekurang-kurangnya satu peranti tersedia"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Pilih apl nota lalai untuk menggunakan pintasan pengambilan nota"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index aa5853fa8833..2e2567dcaa42 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ကန့်သတ်ချက်"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> သတိပေးချက်"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"အလုပ်သုံးအက်ပ်များ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"ခဏရပ်ထားသည်"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ညအလင်းရောင်"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"နေဝင်ချိန်၌ ဖွင့်ရန်"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"နေထွက်ချိန် အထိ"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ချဲ့ခြင်း ဆက်တင်များ ဖွင့်ရန်"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ချဲ့ခြင်း ဆက်တင်များ ပိတ်ရန်"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ပြင်ဆင်မုဒ်မှ ထွက်ရန်"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"အရွယ်အစားပြန်ပြုပြင်ရန် ထောင့်စွန်းကို ဖိဆွဲပါ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ထောင့်ဖြတ် လှိမ့်ခွင့်ပြုရန်"</string> <string name="accessibility_resize" msgid="5733759136600611551">"အရွယ်အစားပြန်ပြုပြင်ရန်"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ဖွင့်ရန်"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet အက်ပ်ကို ဖြတ်လမ်းလင့်ခ်အဖြစ်ထည့်ရန် ၎င်းအားထည့်သွင်းထားကြောင်း သေချာပါစေ"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet အက်ပ်ကို ဖြတ်လမ်းလင့်ခ်အဖြစ်ထည့်ရန် အနည်းဆုံး ကတ်တစ်ခုထည့်ထားကြောင်း သေချာပါစေ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR ကုဒ် စကင်ဖတ်စနစ်ကို ဖြတ်လမ်းလင့်ခ်အဖြစ်ထည့်ရန် ကင်မရာအက်ပ်အားထည့်သွင်းထားကြောင်း သေချာပါစေ"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home အက်ပ်ကို ဖြတ်လမ်းလင့်ခ်အဖြစ်ထည့်ရန် ၎င်းအားထည့်သွင်းထားကြောင်း သေချာပါစေ"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• အနည်းဆုံး စက်တစ်ခုသုံးနိုင်ရမည်"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"မှတ်စုရေးသည့် ဖြတ်လမ်းလင့်ခ်သုံးရန်အတွက် မူရင်းမှတ်စုများအက်ပ် ရွေးရန်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 90025c6fed68..8ef779459b53 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Grense på <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advarsel for <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Jobbapper"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Satt på pause"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattlys"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"På ved solnedgang"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Til soloppgang"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Åpne innstillinger for forstørring"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Lukk forstørringsinnstillingene"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dra hjørnet for å endre størrelse"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillat diagonal rulling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Endre størrelse"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Åpne <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"For å legge til Wallet-appen som snarvei, sørg for at appen er installert"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"For å legge til Wallet-appen som snarvei, sørg for at minst ett kort er lagt til"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"For å legge til QR-kodeskanneren som snarvei, sørg for at du har en kameraapp installert"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"For å legge til Home-appen som snarvei, sørg for at appen er installert"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• minst én enhet er tilgjengelig"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Velg en standard notatapp du vil bruke med notatsnarveien"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index dd153137b9ff..c56ebb6bfabc 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी दिँदै"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"कामसम्बन्धी एपहरू"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"पज गरिएको छ"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"सूर्यास्तमा सक्रिय"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सूर्योदयसम्म"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"जुम इनसम्बन्धी सेटिङ खोल्नुहोस्"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"जुम इन गर्ने सुविधाको सेटिङ बन्द गर्नुहोस्"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"आकार बदल्न कुनाबाट ड्र्याग गर्नुहोस्"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"डायगोनल तरिकाले स्क्रोल गर्ने अनुमति दिनुहोस्"</string> <string name="accessibility_resize" msgid="5733759136600611551">"आकार बदल्नुहोस्"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> खोल्नुहोस्"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"सर्टकटका रूपमा Wallet एप हाल्न उक्त एप इन्स्टल गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"सर्टकटका रूपमा Wallet एप हाल्न कम्तीमा एउटा कार्ड हालिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"सर्टकटका रूपमा QR कोड स्क्यानर हाल्न क्यामेरा एप इन्स्टल गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home एपलाई सर्टकटका रूपमा हाल्न उक्त एप इन्स्टल गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• कम्तीमा एउटा डिभाइस उपलब्ध छ"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"नोट बनाउनेसम्बन्धी सर्टकट प्रयोग गर्न नोट बनाउने डिफल्ट एप चयन गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 1b657c9cb58f..088bcbd4a611 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limiet van <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Waarschuwing voor <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Werk-apps"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Onderbroken"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nachtverlichting"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan bij zonsondergang"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot zonsopkomst"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Instellingen voor vergroting openen"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Instellingen voor vergroting sluiten"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Bewerkingsmodus sluiten"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Sleep een hoek om het formaat te wijzigen"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonaal scrollen toestaan"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Formaat aanpassen"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> openen"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Als je de Wallet-app wilt toevoegen als sneltoets, zorg je dat de app is geïnstalleerd"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Als je de Wallet-app wilt toevoegen als sneltoets, zorg je dat er minstens één kaart is toegevoegd"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Als je de QR-codescanner wilt toevoegen als sneltoets, zorg je dat er een camera-app is geïnstalleerd"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Als je de Home-app wilt toevoegen als sneltoets, zorg je dat de app is geïnstalleerd"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Er is minstens één apparaat beschikbaar"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecteer een standaard notitie-app om de sneltoets voor notities maken te gebruiken"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 3da07a8db18a..64955e44a815 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ସୀମା"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ଚେତାବନୀ"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ୱାର୍କ ଆପ୍ସ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"ବିରତ କରାଯାଇଛି"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ନାଇଟ୍ ଲାଇଟ୍"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ସୂର୍ଯ୍ୟାସ୍ତ ବେଳେ ଅନ୍ ହେବ"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ସୂର୍ଯ୍ୟୋଦୟ ପର୍ଯ୍ୟନ୍ତ"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ମାଗ୍ନିଫିକେସନ ସେଟିଂସ ଖୋଲନ୍ତୁ"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ମେଗ୍ନିଫିକେସନ ସେଟିଂସକୁ ବନ୍ଦ କରନ୍ତୁ"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ରିସାଇଜ କରିବା ପାଇଁ କୋଣକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ଡାଏଗୋନାଲ ସ୍କ୍ରୋଲିଂକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ରିସାଇଜ କରନ୍ତୁ"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ଖୋଲନ୍ତୁ"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"ଏକ ସର୍ଟକଟ ଭାବେ Wallet ଆପ ଯୋଗ କରିବା ପାଇଁ ଏହି ଆପ ଇନଷ୍ଟଲ କରାଯାଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"ଏକ ସର୍ଟକଟ ଭାବେ Wallet ଆପ ଯୋଗ କରିବାକୁ ଅତିକମରେ ଗୋଟିଏ ଆପ ଯୋଗ କରାଯାଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"ଏକ ସର୍ଟକଟ ଭାବେ QR କୋଡ ସ୍କାନର ଯୋଗ କରିବାକୁ ଏକ କେମେରା ଆପ ଇନଷ୍ଟଲ କରାଯାଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"ଏକ ସର୍ଟକଟ ଭାବେ Home ଆପ ଯୋଗ କରିବା ପାଇଁ ଏହି ଆପ ଇନଷ୍ଟଲ କରାଯାଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ଅତିକମରେ ଗୋଟିଏ ଡିଭାଇସ ଉପଲବ୍ଧ ଅଛି"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"ନୋଟଟେକିଂ ସର୍ଟକଟ ବ୍ୟବହାର କରିବାକୁ ଏକ ଡିଫଲ୍ଟ ନୋଟ୍ସ ଆପ୍ସ ଚୟନ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index b8a92c23eb12..b3d44828576f 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਸੀਮਾ"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਚਿਤਾਵਨੀ"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"ਰੋਕਿਆ ਗਿਆ"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ਰਾਤ ਦੀ ਰੋਸ਼ਨੀ"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ਸੂਰਜ ਛਿਪਣ \'ਤੇ ਚਾਲੂ"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸੈਟਿੰਗਾਂ ਬੰਦ ਕਰੋ"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ਆਕਾਰ ਬਦਲਣ ਲਈ ਕੋਨਾ ਘਸੀਟੋ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ਟੇਡੀ ਦਿਸ਼ਾ ਵਿੱਚ ਸਕ੍ਰੋਲ ਕਰਨ ਦਿਓ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ਆਕਾਰ ਬਦਲੋ"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ਖੋਲ੍ਹੋ"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet ਐਪ ਨੂੰ ਸ਼ਾਰਟਕੱਟ ਵਜੋਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਐਪ ਸਥਾਪਤ ਹੈ"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet ਐਪ ਨੂੰ ਸ਼ਾਰਟਕੱਟ ਵਜੋਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਕਾਰਡ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR ਕੋਡ ਸਕੈਨਰ ਨੂੰ ਸ਼ਾਰਟਕੱਟ ਵਜੋਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਕੈਮਰਾ ਐਪ ਸਥਾਪਤ ਹੈ"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home ਐਪ ਨੂੰ ਸ਼ਾਰਟਕੱਟ ਵਜੋਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ ਐਪ ਸਥਾਪਤ ਹੈ"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• ਘੱਟੋ-ਘੱਟ ਇੱਕ ਡੀਵਾਈਸ ਉਪਲਬਧ ਹੈ"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"ਨੋਟ ਬਣਾਉਣ ਵਾਲੇ ਸ਼ਾਰਟਕੱਟ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਨੋਟ ਐਪ ਚੁਣੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 8c0a78e32e5b..e3beef0b634c 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Ostrzeżenie: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplikacje służbowe"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Wstrzymano"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Podświetlenie nocne"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Włącz o zachodzie"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do wschodu słońca"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otwórz ustawienia powiększenia"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zamknij ustawienia powiększenia"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Przeciągnij róg, aby zmienić rozmiar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Zezwalaj na przewijanie poprzeczne"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Zmień rozmiar"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otwórz: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Aby dodać aplikację Portfel jako skrót, upewnij się, że jest zainstalowana"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Aby dodać aplikację Portfel jako skrót, upewnij się, że została dodana co najmniej 1 karta"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Aby dodać Skaner kodów QR jako skrót, upewnij się, że jest zainstalowana aplikacja aparatu"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Aby dodać aplikację Home jako skrót, upewnij się, że jest zainstalowana"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Dostępne jest co najmniej 1 urządzenie."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Wybierz domyślną aplikację do obsługi notatek, której skrótu będziesz używać do funkcji notowania"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index bf976f7149fa..8f5d7faed76f 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Apps de trabalho"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pausado"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir as configurações de ampliação"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar configurações de ampliação"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Sair do modo de edição"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arraste o canto para redimensionar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir rolagem diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Para adicionar o app Carteira como um atalho, verifique se ele está instalado"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Para adicionar o app Carteira como um atalho, verifique se pelo menos um cartão foi adicionado"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Para adicionar o leitor de código QR como um atalho, verifique se algum app de câmera está instalado"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Para adicionar o app Home como um atalho, verifique se ele está instalado"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Pelo menos um dispositivo está disponível"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecione um app de notas padrão para usar o atalho de anotações"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 9974df1e0ee9..a4f17b6b6fff 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Apps trabalho"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pausado"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz noturna"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr-do-sol"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até ao amanhecer"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir definições de ampliação"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar definições de ampliação"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Sair do modo de edição"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastar o canto para redimensionar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir deslocamento da página na diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Para adicionar a app Carteira como um atalho, certifique-se de que a app está instalada"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Para adicionar a app Carteira como um atalho, certifique-se de que foi adicionado, pelo menos, um cartão"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Para adicionar o Leitor de códigos QR como um atalho, certifique-se de que está instalada uma app de câmara"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Para adicionar a app Home como um atalho, certifique-se de que a app está instalada"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Está disponível, pelo menos, um dispositivo"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecione uma app de notas predefinida para usar o atalho de anotação"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index bf976f7149fa..8f5d7faed76f 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Apps de trabalho"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pausado"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir as configurações de ampliação"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar configurações de ampliação"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Sair do modo de edição"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arraste o canto para redimensionar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir rolagem diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Para adicionar o app Carteira como um atalho, verifique se ele está instalado"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Para adicionar o app Carteira como um atalho, verifique se pelo menos um cartão foi adicionado"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Para adicionar o leitor de código QR como um atalho, verifique se algum app de câmera está instalado"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Para adicionar o app Home como um atalho, verifique se ele está instalado"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Pelo menos um dispositivo está disponível"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selecione um app de notas padrão para usar o atalho de anotações"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 1ed3a3757318..749106b80e8a 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limită de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Avertizare: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplicații pentru lucru"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Întreruptă"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Lumină de noapte"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Activată la apus"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Până la răsărit"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Mărește o parte a ecranului"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Deschide setările pentru mărire"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Închide setările de mărire"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Trage de colț pentru a redimensiona"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permite derularea pe diagonală"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionează"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Deschide <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Pentru a adăuga aplicația Portofel drept comandă rapidă, asigură-te că aplicația este instalată"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Pentru a adăuga aplicația Portofel drept comandă rapidă, asigură-te că ai adăugat cel puțin un card"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Pentru a adăuga Scanner de coduri QR drept comandă rapidă, asigură-te că este instalată o aplicație pentru camera foto"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Pentru a adăuga aplicația Home drept comandă rapidă, asigură-te că aplicația este instalată"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Este disponibil cel puțin un dispozitiv"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Selectează o aplicație prestabilită pentru note ca să folosești comanda rapidă de luat note"</string> @@ -1167,7 +1169,7 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setează aplicația prestabilită de note din Setări"</string> <string name="install_app" msgid="5066668100199613936">"Instalează aplicația"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfon și cameră"</string> - <string name="privacy_dialog_summary" msgid="2458769652125995409">"Folosit recent de aplicații"</string> + <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilizare recentă în aplicații"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Vezi accesarea recentă"</string> <string name="privacy_dialog_done_button" msgid="4504330708531434263">"Gata"</string> <string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Extinde și afișează opțiunile"</string> @@ -1178,10 +1180,10 @@ <string name="privacy_dialog_manage_permissions" msgid="2543451567190470413">"Gestionează accesul"</string> <string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Folosit de un apel telefonic"</string> <string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Folosit recent într-un apel telefonic"</string> - <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Folosit de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> - <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> - <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Folosit de <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> + <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Se folosește pentru <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"S-a folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Se folosește pentru <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> - <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Folosit de <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="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Se folosește pentru <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="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Folosit recent de <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> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 400db7bc3da8..5f7445aff112 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ограничение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Рабочие приложения"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Приостановлен"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ночная подсветка"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Вкл. на закате"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До рассвета"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Открыть настройки увеличения"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрыть настройки увеличения"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Потяните за угол, чтобы изменить размер"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Разрешить прокручивать по диагонали"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Изменить размер"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Открыть \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Вы можете добавить ярлык приложения \"Кошелек\", только если оно установлено."</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Вы можете добавить ярлык приложения \"Кошелек\", только если добавлена хотя бы одна карта."</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Вы можете добавить ярлык сканера QR-кодов, только если установлено приложение камеры."</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Вы можете добавить ярлык приложения Home, только если оно установлено."</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Доступно хотя бы одно устройство."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Выберите стандартное приложение для заметок, которое будет открываться при нажатии на ярлык."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 37a14f947e87..5cc1fa983238 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> සීමිත"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> අවවාද කිරීම"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"කාර්යාල යෙදුම්"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"විරාමයි"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"රාත්රී ආලෝකය"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"හිරු බැසීමේදී ක්රි."</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"හිරු නගින තෙක්"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"විශාලන සැකසීම් විවෘත කරන්න"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"විශාලන සැකසීම් වසන්න"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ප්රමාණය වෙනස් කිරීමට කොන අදින්න"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"විකර්ණ අනුචලනයට ඉඩ දෙන්න"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ප්රතිප්රමාණය කරන්න"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> විවෘත කරන්න"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"කෙටිමඟක් ලෙස Wallet යෙදුම එක් කිරීම සඳහා, යෙදුම ස්ථාපනය කර ඇති බවට වග බලා ගන්න"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"කෙටිමඟක් ලෙස Wallet යෙදුම එක් කිරීම සඳහා, අවම වශයෙන් එක් කාඩ්පතක් එක් කර ඇති බවට වග බලා ගන්න"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"කෙටිමඟක් ලෙස QR කේත ස්කෑනරය එක් කිරීම සඳහා, කැමරා යෙදුමක් ස්ථාපනය කර ඇති බවට වග බලා ගන්න"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home යෙදුම කෙටිමඟක් ලෙස එක් කිරීම සඳහා, යෙදුම ස්ථාපනය කර ඇති බව සහතික කර ගන්න"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• අවම වශයෙන් එක උපාංගයක් ලැබේ"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"සටහන් ගැනීමේ කෙටිමඟ භාවිතා කිරීමට පෙරනිමි සටහන් යෙදුමක් තෝරන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index baa9b4fe17df..1da7aca420f4 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozornenie pri <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Pracovné aplikácie"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pozastavené"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nočný režim"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Zapne sa pri západe slnka"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do východu slnka"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvoriť nastavenia zväčšenia"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zavrieť nastavenia zväčšenia"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Veľkosť zmeníte presunutím rohu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Povoliť diagonálne posúvanie"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Zmeniť veľkosť"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvoriť <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Ak chcete pridať aplikáciu Peňaženka ako odkaz, uistite sa, že je nainštalovaná"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Ak chcete pridať aplikáciu Peňaženka ako odkaz, uistite sa, že bola pridaná aspoň jedna karta"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Ak chcete pridať skener QR kódov ako odkaz, uistite, že je nainštalovaná aplikácia fotoaparátu"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Ak chcete pridať aplikáciu Home ako odkaz, uistite sa, že je nainštalovaná"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• K dispozícii je minimálne jedno zariadenie"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Ak chcete používať odkaz na písanie poznámok, vyberte predvolenú aplikáciu na písanie poznámok"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 3608293fd631..79e54374031a 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Omejitev: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Opozorilo – <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Delovne aplikacije"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Začasno zaustavljeno"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nočna svetloba"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ob sončnem zahodu"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do sončnega vzhoda"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Odpri nastavitve povečave"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zapri nastavitve povečave"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Zapri način za urejanje"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Povlecite vogal, da spremenite velikost."</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dovoli diagonalno pomikanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Spremeni velikost"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Odpri aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Če želite aplikacijo Denarnica dodati kot bližnjico, poskrbite, da je aplikacija nameščena."</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Če želite aplikacijo Denarnica dodati kot bližnjico, poskrbite, da je dodana vsaj ena kartica."</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Če želite optični bralnik kod QR dodati kot bližnjico, poskrbite, da je nameščena fotografska aplikacija."</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Če želite aplikacijo Home dodati kot bližnjico, poskrbite, da je aplikacija nameščena."</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Na voljo mora biti vsaj ena naprava."</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Izberite privzeto aplikacijo za zapiske, ki jo želite povezati z bližnjico do ustvarjanja zapiskov."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 6e1edff13863..5b58fc7356d9 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Kufiri: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Paralajmërim për kufirin prej <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Aplikacionet e punës"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Vendosur në pauzë"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Drita e natës"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Në perëndim të diellit"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Deri në lindje të diellit"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Hap cilësimet e zmadhimit"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Mbyll cilësimet e zmadhimit"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Zvarrit këndin për të ndryshuar përmasat"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Lejo lëvizjen diagonale"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ndrysho përmasat"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Hap \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Për të shtuar aplikacionin \"Portofoli\" si një shkurtore, sigurohu që aplikacioni të jetë i instaluar"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Për të shtuar aplikacionin \"Portofoli\" si një shkurtore, sigurohu që të jetë shtuar të paktën një kartë"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Për të shtuar skanerin e kodeve QR si një shkurtore, sigurohu që aplikacioni i kamerës të jetë i instaluar"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Për të shtuar aplikacionin Home si një shkurtore, sigurohu që aplikacioni të jetë i instaluar"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Ofrohet të paktën një pajisje"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Zgjidh një aplikacion të parazgjedhur shënimesh për të përdorur shkurtoren e mbajtjes së shënimeve"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 0a73cf5b1c3a..eee0fe63666c 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Ограничење од <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Упозорење за <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Пословне апликације"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Паузирано"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ноћно светло"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Укључује се по заласку сунца"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До изласка сунца"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Отвори подешавања увећања"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затвори подешавања увећања"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Затвори режим измене"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Превуците угао да бисте променили величину"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволи дијагонално скроловање"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Промени величину"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отворите: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Да бисте додали апликацију Новчаник као пречицу, уверите се да је апликација инсталирана"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Да бисте додали апликацију Новчаник као пречицу, уверите се да је додата бар једна картица"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Да бисте додали Скенер QR кода као пречицу, уверите се да је апликација за камеру инсталирана"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Да бисте додали апликацију Home као пречицу, уверите се да је апликација инсталирана"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Доступан је бар један уређај"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Изаберите подразумевану апликацију за белешке да бисте користили пречицу за прављење белешки"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 9329e19fc01d..b99664902f31 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Gräns: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Varning <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Jobbappar"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pausad"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattljus"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"På från solnedgången"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Till soluppgången"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Öppna inställningarna för förstoring"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Stäng inställningarna för förstoring"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dra i hörnet för att ändra storlek"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillåt diagonal scrollning"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ändra storlek"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Öppna <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Se till att Wallet-appen är installerad om du vill lägga till den som genväg"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Se till att minst ett kort har lagts till om du vill lägga till Wallet-appen som genväg"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Se till att en kameraapp är installerad om du vill lägga till QR-skannern som genväg"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Se till att Home-appen är installerad om du vill lägga till den som genväg"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• minst en enhet är tillgänglig"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Välj en standardapp för anteckningar om du vill använda genvägen för anteckningar"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 7c0df30cf707..b00f9b695d6f 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"kikomo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Onyo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Programu za kazini"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Umesitishwa"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Mwanga wa Usiku"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Itawashwa machweo"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hadi macheo"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Fungua mipangilio ya ukuzaji"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Funga mipangilio ya ukuzaji"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Buruta kona ili ubadilishe ukubwa"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Ruhusu usogezaji wa kimshazari"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Badilisha ukubwa"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Fungua <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Ili uweke programu ya Pochi kuwa njia ya mkato, hakikisha umesakinisha programu hiyo"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Ili uweke programu ya Pochi kuwa njia ya mkato, hakikisha umeweka angalau kadi moja"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Ili uweke kichanganuzi cha msimbo wa QR kuwa njia ya mkato, hakikisha umesakinisha programu ya kamera"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Ili uweke programu ya Google Home kuwa njia ya mkato, hakikisha umesakinisha programu hiyo"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Angalau kifaa kimoja kinapatikana"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Chagua programu chaguomsingi ya madokezo ili utumie njia ya mkato ya kuandika madokezo"</string> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index d85e0122f2b4..915dcdb9755f 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -82,6 +82,8 @@ <!-- start padding is smaller to account for status icon margins coming from drawable itself --> <dimen name="shade_header_system_icons_padding_start">3dp</dimen> <dimen name="shade_header_system_icons_padding_end">4dp</dimen> + <dimen name="shade_header_system_icons_padding_top">2dp</dimen> + <dimen name="shade_header_system_icons_padding_bottom">2dp</dimen> <!-- Lockscreen shade transition values --> <dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 4c6816b4a9e7..57f537fc9652 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> வரம்பு"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> எச்சரிக்கை"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"பணி ஆப்ஸ்"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"இடைநிறுத்தப்பட்டது"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"நைட் லைட்"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"மாலையில் ஆன் செய்"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"காலை வரை"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"பெரிதாக்கல் அமைப்புகளைத் திற"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"பெரிதாக்கல் அமைப்புகளை மூடுக"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"அளவை மாற்ற மூலையை இழுக்கவும்"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"குறுக்கே ஸ்க்ரோல் செய்வதை அனுமதி"</string> <string name="accessibility_resize" msgid="5733759136600611551">"அளவை மாற்று"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸைத் திற"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet ஆப்ஸை ஷார்ட்கட்டாகச் சேர்க்க, அந்த ஆப்ஸ் நிறுவப்பட்டுள்ளதை உறுதிசெய்துகொள்ளவும்"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet ஆப்ஸை ஷார்ட்கட்டாகச் சேர்க்க, அதில் குறைந்தது ஒரு கார்டாவது சேர்க்கப்பட்டுள்ளதை உறுதிசெய்துகொள்ளவும்"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR குறியீடு ஸ்கேனரை ஷார்ட்கட்டாகச் சேர்க்க, கேமரா ஆப்ஸ் நிறுவப்பட்டுள்ளதை உறுதிசெய்துகொள்ளவும்"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home ஆப்ஸை ஷார்ட்கட்டாகச் சேர்க்க, அந்த ஆப்ஸ் நிறுவப்பட்டுள்ளதை உறுதிசெய்துகொள்ளவும்"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• குறைந்தபட்சம் ஒரு சாதனமாவது இருக்கிறது"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"குறிப்பெடுத்தல் ஷார்ட்கட்டைப் பயன்படுத்த, குறிப்பெடுப்பதற்கான இயல்புநிலை ஆப்ஸைத் தேர்ந்தெடுக்கவும்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index f501d32e5b28..124f2739e205 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> పరిమితి"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> హెచ్చరిక"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"వర్క్ యాప్లు"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"పాజ్ చేయబడింది"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"రాత్రి కాంతి"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"సూర్యాస్తమయానికి"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"సూర్యోదయం వరకు"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మ్యాగ్నిఫై చేయండి"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"మ్యాగ్నిఫికేషన్ సెట్టింగ్లను తెరవండి"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"మాగ్నిఫికేషన్ సెట్టింగ్లను మూసివేయండి"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"సవరణ మోడ్ నుండి ఎగ్జిట్ అవ్వండి"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"సైజ్ మార్చడానికి మూలను లాగండి"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"డయాగనల్ స్క్రోలింగ్ను అనుమతించండి"</string> <string name="accessibility_resize" msgid="5733759136600611551">"సైజ్ మార్చండి"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g>ను తెరవండి"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet యాప్ను షార్ట్కట్గా జోడించడానికి, యాప్ ఇన్స్టాల్ చేయబడిందని నిర్ధారించుకోండి"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet యాప్ను షార్ట్కట్గా జోడించడానికి, కనీసం ఒక కార్డ్ జోడించబడిందని నిర్ధారించుకోండి"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR కోడ్ స్కానర్ను షార్ట్కట్గా జోడించడానికి, కెమెరా యాప్ ఇన్స్టాల్ చేయబడిందని నిర్ధారించుకోండి"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home యాప్ను షార్ట్కట్గా జోడించడానికి, యాప్ ఇన్స్టాల్ చేయబడిందని నిర్ధారించుకోండి"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• కనీసం ఒక పరికరమైనా అందుబాటులో ఉందని"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"నోట్టేకింగ్ షార్ట్కట్ను ఉపయోగించడానికి ఆటోమేటిక్ సెట్టింగ్ నోట్స్ యాప్ను ఎంచుకోండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 7c524d9f08e5..46fec04c250a 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"ขีดจำกัด <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"คำเตือน <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"แอปงาน"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"หยุดชั่วคราว"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"แสงตอนกลางคืน"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"เปิดตอนพระอาทิตย์ตก"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"จนพระอาทิตย์ขึ้น"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"เปิดการตั้งค่าการขยาย"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ปิดการตั้งค่าการขยาย"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ออกจากโหมดแก้ไข"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ลากที่มุมเพื่อปรับขนาด"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"อนุญาตการเลื่อนแบบทแยงมุม"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ปรับขนาด"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"เปิด <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"หากต้องการเพิ่มแอป Wallet เป็นทางลัด โปรดตรวจสอบว่าได้ติดตั้งแอปแล้ว"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"หากต้องการเพิ่มแอป Wallet เป็นทางลัด โปรดตรวจสอบว่าได้เพิ่มบัตรอย่างน้อย 1 ใบแล้ว"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"หากต้องการเพิ่มเครื่องมือสแกนคิวอาร์โค้ดเป็นทางลัด โปรดตรวจสอบว่าได้ติดตั้งแอปกล้องแล้ว"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"หากต้องการเพิ่มแอป Home เป็นทางลัด โปรดตรวจสอบว่าได้ติดตั้งแอปแล้ว"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• มีอุปกรณ์พร้อมใช้งานอย่างน้อย 1 รายการ"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"เลือกแอปโน้ตเริ่มต้นเพื่อใช้ทางลัดการจดบันทึก"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index fc2d8cfa920c..079bd9b3de79 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ang limitasyon"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Babala sa <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Mga app para sa trabaho"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Naka-pause"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Mao-on sa sunset"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hanggang sunrise"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buksan ang mga setting ng pag-magnify"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Isara ang mga setting ng pag-magnify"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"I-drag ang sulok para i-resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Payagan ang diagonal na pag-scroll"</string> <string name="accessibility_resize" msgid="5733759136600611551">"I-resize"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buksan ang <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Para maidagdag ang Wallet app bilang shortcut, siguraduhing naka-install ang app"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Para maidagdag ang Wallet app bilang shortcut, siguraduhing may naidagdag na kahit isang card man lang"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Para maidagdag ang scanner ng QR code bilang shortcut, siguraduhing may naka-install na camera app"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Para maidagdag ang Home app bilang shortcut, siguraduhing naka-install ang app"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• May kahit isang device na available"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Pumili ng default na app ng mga tala para magamit ang shortcut sa paggawa ng tala"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 803acaed8883..7ba95ec8b427 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Sınır: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> uyarısı"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"İş uygulamaları"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Duraklatıldı"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Gece Işığı"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Gün batımı açılacak"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Sabaha kadar"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Büyütme ayarlarını aç"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Büyütme ayarlarını kapat"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Yeniden boyutlandırmak için köşeyi sürükleyin"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Çapraz kaydırmaya izin ver"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Yeniden boyutlandır"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> uygulamasını aç"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Cüzdan uygulamasını kısayol olarak eklemek için uygulamanın yüklü olduğundan emin olun"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Cüzdan uygulamasını kısayol olarak eklemek için en az bir kart eklendiğinden emin olun"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR kodu tarayıcıyı kısayol olarak eklemek için bir kamera uygulamasının yüklü olduğundan emin olun"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home uygulamasını kısayol olarak eklemek için uygulamanın yüklü olduğundan emin olun"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• En az bir cihaz mevcut olmalıdır"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Not alma kısayolunu kullanmak için varsayılan bir notlar uygulaması seçin"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 668fb59760ac..3feefef5a639 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Обмеження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Застереження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Робочі додатки"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Призупинено"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Нічний екран"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Вмикається ввечері"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"До сходу сонця"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Відкрити налаштування збільшення"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрити налаштування збільшення"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Потягніть кут, щоб змінити розмір"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволити прокручування по діагоналі"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Змінити розмір"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Відкрити <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Щоб додати ярлик для запуску додатка Google Гаманець, переконайтеся, що додаток установлено"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Щоб додати ярлик для запуску додатка Google Гаманець, переконайтеся, що він містить дані принаймні однієї картки"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Щоб додати ярлик для запуску сканера QR-коду, переконайтеся, що встановлено додаток для камери"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Щоб додати ярлик для запуску додатка Google Home, переконайтеся, що додаток установлено"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Принаймні один пристрій доступний"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Виберіть стандартний додаток для нотаток, щоб створювати їх за допомогою ярлика"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 33d11d97cc34..29d37622cee6 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> حد"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> وارننگ"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"دفتری ایپس"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"موقوف ہے"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"نائٹ لائٹ"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"غروب آفتاب کے وقت آن ہوگی"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"طلوع آفتاب تک"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"میگنیفکیشن کی ترتیبات کھولیں"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"میگنیفکیشن کی ترتیبات بند کریں"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"سائز تبدیل کرنے کے لیے کونے کو گھسیٹیں"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"وتری سکرولنگ کی اجازت دیں"</string> <string name="accessibility_resize" msgid="5733759136600611551">"سائز تبدیل کریں"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> کھولیں"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"والٹ ایپ کو شارٹ کٹ کے طور پر شامل کرنے کے لیے، یقینی بنائیں کہ ایپ انسٹال ہے"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"والٹ ایپ کو شارٹ کٹ کے طور پر شامل کرنے کے لیے، یقینی بنائیں کہ کم از کم ایک کارڈ شامل کیا گیا ہے"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR کوڈ اسکینر کو شارٹ کٹ کے طور پر شامل کرنے کے لیے، یقینی بنائیں کہ کیمرا ایپ انسٹال ہے"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"ہوم ایپ کو شارٹ کٹ کے طور پر شامل کرنے کے لیے، یقینی بنائیں کہ ایپ انسٹال ہے"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• کم از کم ایک آلہ دستیاب ہے"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"نوٹ لینے والے شارٹ کٹ کا استعمال کرنے کے لیے ڈیفالٹ نوٹس ایپ منتخب کریں"</string> @@ -1178,8 +1180,8 @@ <string name="privacy_dialog_manage_permissions" msgid="2543451567190470413">"رسائی کا نظم کریں"</string> <string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"فون کال کے زیر استعمال"</string> <string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"فون کال میں حال ہی میں استعمال کیا گیا"</string> - <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے زیر استعمال"</string> - <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے ذریعے حال ہی میں استعمال کیا گیا"</string> + <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے زیر استعمال"</string> + <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"حال ہی میں <xliff:g id="APP_NAME">%1$s</xliff:g> نے استعمال کیا"</string> <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) کے زیر استعمال"</string> <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) کے ذریعے حال ہی میں استعمال کیا گیا"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<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> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 65f56533448e..b7defbb101eb 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Cheklov: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Ogohlantirish: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Ish ilovalari"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Pauzada"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Tungi rejim"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Kunbotarda yoqish"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Quyosh chiqqunicha"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Kattalashtirish sozlamalarini ochish"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Kattalashtirish sozlamalarini yopish"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Oʻlchamini oʻzgartirish uchun burchakni torting"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonal aylantirishga ruxsat berish"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Oʻlchamini oʻzgartirish"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ochish: <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Wallet ilovasini yorliq sifatida qoʻshish uchun ilova oʻrnatilganiga ishonch hosil qiling"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Wallet ilovasini yorliq sifatida qoʻshish uchun kamida bitta karta kiritilganiga ishonch hosil qiling"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"QR kod skanerini yorliq sifatida qoʻshish uchun kamera ilovasi oʻrnatilganiga ishonch hosil qiling"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Home ilovasini yorliq sifatida qoʻshish uchun ilova oʻrnatilganiga ishonch hosil qiling"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Kamida bitta qurilma mavjud"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Qayd yozish yorligʻidan foydalanish uchun birlamchi qayd ilovasini tanlang"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 8be1dd564cb4..2d49b0225727 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Giới hạn <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Cảnh báo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Ứng dụng công việc"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Đã tạm dừng"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ánh sáng đêm"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Bật khi trời tối"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Cho đến khi trời sáng"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Mở chế độ cài đặt phóng to"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Đóng bảng cài đặt tính năng phóng to"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Kéo góc để thay đổi kích thước"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Cho phép cuộn chéo"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Đổi kích thước"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Mở <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Để tạo lối tắt cho ứng dụng Wallet, hãy đảm bảo bạn đã cài đặt ứng dụng đó"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Để tạo lối tắt cho ứng dụng Wallet, hãy đảm bảo bạn đã thêm ít nhất một thẻ"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Để tạo lối tắt cho Trình quét mã QR, hãy đảm bảo rằng bạn đã cài đặt một ứng dụng máy ảnh"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Để tạo lối tắt cho ứng dụng Home, hãy đảm bảo bạn đã cài đặt ứng dụng đó"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Có ít nhất một thiết bị đang hoạt động"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Chọn một ứng dụng ghi chú mặc định để dùng lối tắt ghi chú"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 299ebf910ac3..8d2b89b164d3 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限为<xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g>警告"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"工作应用"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"已暂停"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"护眼模式"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"在日落时开启"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"在日出时关闭"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"打开放大功能设置"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"关闭放大设置"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"退出修改模式"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"拖动一角即可调整大小"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"允许沿对角线滚动"</string> <string name="accessibility_resize" msgid="5733759136600611551">"调整大小"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"打开<xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"若要将 Google 钱包应用添加为快捷方式,请确保已安装该应用"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"若要将 Google 钱包应用添加为快捷方式,请确保至少已添加一张银行卡"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"若要将二维码扫描器添加为快捷方式,请确保已安装相机应用"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"若要将 Home 应用添加为快捷方式,请确保已安装该应用"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• 至少有一台设备可用"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"选择默认记事应用即可使用记事快捷方式"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index fb40f32960ec..9e408f28c1c7 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限為 <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"工作應用程式"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"已暫停"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"夜間模式"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"在日落時開啟"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"在日出時關閉"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"開啟放大設定"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"關閉放大設定"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"結束編輯模式"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"拖曳角落即可調整大小"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"允許斜角捲動"</string> <string name="accessibility_resize" msgid="5733759136600611551">"調整大小"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"開啟「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"如要將「錢包」應用程式新增為捷徑,請確認已安裝該應用程式"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"如要將「錢包」應用程式新增為捷徑,請確認已新增至少一張付款卡"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"如要將 QR 碼掃瞄器新增為捷徑,請確認已安裝相機應用程式"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"如要將 Home 應用程式新增為捷徑,請確認已安裝該應用程式"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• 至少一部裝置可用"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"選取預設的筆記應用程式,即可使用筆記捷徑"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 4750b5b9c671..5831ac525dcd 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"上限為 <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"工作應用程式"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"已暫停"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"夜燈"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"於日落時開啟"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"於日出時關閉"</string> @@ -864,6 +865,7 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"開啟放大功能設定"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"關閉放大設定"</string> + <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"結束編輯模式"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"拖曳角落即可調整大小"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"允許沿對角線捲動"</string> <string name="accessibility_resize" msgid="5733759136600611551">"調整大小"</string> @@ -1133,7 +1135,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"開啟「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"如要將錢包應用程式新增為捷徑,請確認已安裝該應用程式"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"如要將錢包應用程式新增為捷徑,請確認已新增至少一張卡片"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"如要將 QR code 掃描器新增為捷徑,請確認已安裝相機應用程式"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"如要將 Google Home 應用程式新增為捷徑,請確認已安裝該應用程式"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• 至少要有一部可用裝置"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"選取預設的記事應用程式,即可使用筆記捷徑"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index e22d1d782905..5013f8d17bee 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -295,6 +295,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> umkhawulo"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> isexwayiso"</string> <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Ama-app omsebenzi"</string> + <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Kumisiwe"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ukukhanya kwasebusuku"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Kuvulwe ekushoneni kwelanga"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Kuze kube sekuphumeni kwelanga"</string> @@ -864,6 +865,8 @@ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Vula amasethingi okukhuliswa"</string> <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Vala amasethingi okukhuliswa"</string> + <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) --> + <skip /> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Hudula ikhona ukuze usayize kabusha"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Vumela ukuskrola oku-diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Shintsha usayizi"</string> @@ -1133,7 +1136,6 @@ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Vula i-<xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="wallet_quick_affordance_unavailable_install_the_app" msgid="7298552910007208368">"Ukuze ungeze i-app ye-Wallet njengesinqamuleli, qinisekisa ukuthi i-app ifakiwe"</string> <string name="wallet_quick_affordance_unavailable_configure_the_app" msgid="4387433357429873258">"Ukuze ungeze i-app ye-Wallet njengesinqamuleli, qinisekisa ukuthi okungenani ikhadi elilodwa lingeziwe"</string> - <string name="qr_scanner_quick_affordance_unavailable_explanation" msgid="3049582306241150946">"Ukuze ungeze Iskena sekhodi ye-QR njengesinqamuleli, qinisekisa ukuthi i-app yekhamera ifakiwe"</string> <string name="home_quick_affordance_unavailable_install_the_app" msgid="6187820778998446168">"Ukuze ungeze i-App yasekhaya njengesinqamuleli, qinisekisa ukuthi i-app ifakiwe"</string> <string name="home_quick_affordance_unavailable_configure_the_app" msgid="7953595390775156839">"• Okungenani idivayisi eyodwa iyatholakala"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"Khetha i-app yamanothi azenzakalelayo ukuze usebenzise isinqamuleli sokubhala amanothi"</string> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index d693631080af..8bc3eed59062 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -221,6 +221,7 @@ <attr name="descriptionTextAppearance" format="reference" /> <attr name="passwordTextAppearance" format="reference" /> <attr name="errorTextAppearance" format="reference"/> + <attr name="errorTextAppearanceLand" format="reference"/> </declare-styleable> <declare-styleable name="LogAccessPermissionGrantDialog"> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 5c42e45c7c6b..eb9d0b346418 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -923,4 +923,7 @@ "$packageName" part that will be replaced by the code with the package name of the target app. --> <string name="config_appStoreAppLinkTemplate" translatable="false"></string> + + <!-- Flag controlling whether visual query attention detection has been enabled. --> + <bool name="config_enableVisualQueryAttentionDetection">false</bool> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8310b9548b66..2dc1b45af41f 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -496,9 +496,10 @@ <dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen> <dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen> <dimen name="shade_header_system_icons_height">@dimen/large_screen_shade_header_min_height</dimen> - <dimen name="shade_header_system_icons_height_large_screen">32dp</dimen> <dimen name="shade_header_system_icons_padding_start">0dp</dimen> <dimen name="shade_header_system_icons_padding_end">0dp</dimen> + <dimen name="shade_header_system_icons_padding_top">0dp</dimen> + <dimen name="shade_header_system_icons_padding_bottom">0dp</dimen> <!-- The top margin of the panel that holds the list of notifications. On phones it's always 0dp but it's overridden in Car UI diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 261b08d4356f..0d45422f7369 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -39,7 +39,7 @@ <bool name="flag_battery_shield_icon">false</bool> <!-- Whether face auth will immediately stop when the display state is OFF --> - <bool name="flag_stop_face_auth_on_display_off">false</bool> + <bool name="flag_stop_face_auth_on_display_off">true</bool> <!-- Whether we want to stop pulsing while running the face scanning animation --> <bool name="flag_stop_pulsing_face_scanning_animation">true</bool> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index d2cb475ad2b0..4e72518bc613 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -219,4 +219,7 @@ <!-- Privacy dialog --> <item type="id" name="privacy_dialog_close_app_button" /> <item type="id" name="privacy_dialog_manage_app_button" /> + + <!-- Communal mode --> + <item type="id" name="communal_widget_wrapper" /> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cddfda2f9ce7..2003fa32615a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -386,6 +386,8 @@ <string name="biometric_dialog_wrong_password">Wrong password</string> <!-- Error string shown when the user enters too many incorrect attempts [CHAR LIMIT=120]--> <string name="biometric_dialog_credential_too_many_attempts">Too many incorrect attempts.\nTry again in <xliff:g id="number">%d</xliff:g> seconds.</string> + <!-- Button text shown on an authentication screen giving the user the option to make an emergency call without unlocking their device [CHAR LIMIT=20] --> + <string name="work_challenge_emergency_button_text">Emergency</string> <!-- Error string shown when the user enters an incorrect PIN/pattern/password and it counts towards the max attempts before the data on the device is wiped. [CHAR LIMIT=NONE]--> <string name="biometric_dialog_credential_attempts_before_wipe">Try again. Attempt <xliff:g id="attempts" example="1">%1$d</xliff:g> of <xliff:g id="max_attempts" example="3">%2$d</xliff:g>.</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 10340c6847f7..6991b964c504 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -236,6 +236,13 @@ <item name="android:gravity">center</item> </style> + <style name="TextAppearance.AuthNonBioCredential.ErrorLand"> + <item name="android:layout_marginTop">20dp</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?android:attr/colorError</item> + <item name="android:gravity">start</item> + </style> + <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:gravity">center</item> <item name="android:paddingTop">28dp</item> @@ -276,6 +283,17 @@ <item name="android:minWidth">200dp</item> </style> + <style name="AuthCredentialEmergencyButtonStyle"> + <item name="android:background">@drawable/auth_credential_emergency_button_background</item> + <item name="android:textColor">@android:color/system_accent3_900</item> + <item name="android:outlineProvider">none</item> + <item name="android:paddingTop">15dp</item> + <item name="android:paddingBottom">15dp</item> + <item name="android:paddingLeft">30dp</item> + <item name="android:paddingRight">30dp</item> + <item name="android:textSize">16sp</item> + </style> + <style name="DeviceManagementDialogTitle"> <item name="android:gravity">center</item> <item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item> @@ -353,6 +371,7 @@ <item name="descriptionTextAppearance">@style/TextAppearance.AuthNonBioCredential.Description</item> <item name="passwordTextAppearance">@style/TextAppearance.AuthCredential.PasswordEntry</item> <item name="errorTextAppearance">@style/TextAppearance.AuthNonBioCredential.Error</item> + <item name="errorTextAppearanceLand">@style/TextAppearance.AuthNonBioCredential.ErrorLand</item> </style> <style name="LockPatternViewStyle" > diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml index 2ec6180513bf..fe61c46e341d 100644 --- a/packages/SystemUI/res/xml/large_screen_shade_header.xml +++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml @@ -56,7 +56,7 @@ <Constraint android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="wrap_content" - android:layout_height="@dimen/shade_header_system_icons_height_large_screen" + android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/privacy_container" app:layout_constraintTop_toTopOf="parent" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index c844db7b770d..77f6d03a03fb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -28,8 +28,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public final class InteractionJankMonitorWrapper { - private static final String TAG = "JankMonitorWrapper"; - // Launcher journeys. public static final int CUJ_APP_LAUNCH_FROM_RECENTS = InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS; @@ -37,6 +35,8 @@ public final class InteractionJankMonitorWrapper { InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON; public static final int CUJ_APP_CLOSE_TO_HOME = InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME; + public static final int CUJ_APP_CLOSE_TO_HOME_FALLBACK = + InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; public static final int CUJ_APP_CLOSE_TO_PIP = InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP; public static final int CUJ_QUICK_SWITCH = @@ -68,6 +68,7 @@ public final class InteractionJankMonitorWrapper { CUJ_APP_LAUNCH_FROM_RECENTS, CUJ_APP_LAUNCH_FROM_ICON, CUJ_APP_CLOSE_TO_HOME, + CUJ_APP_CLOSE_TO_HOME_FALLBACK, CUJ_APP_CLOSE_TO_PIP, CUJ_QUICK_SWITCH, CUJ_APP_LAUNCH_FROM_WIDGET, diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt new file mode 100644 index 000000000000..899cad89a0be --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt @@ -0,0 +1,85 @@ +/* + * 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.keyguard + +import android.app.Presentation +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.view.Display +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import com.android.keyguard.dagger.KeyguardStatusViewComponent +import com.android.systemui.R +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +/** [Presentation] shown in connected displays while on keyguard. */ +class ConnectedDisplayKeyguardPresentation +@AssistedInject +constructor( + @Assisted display: Display, + context: Context, + private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory, +) : + Presentation( + context, + display, + R.style.Theme_SystemUI_KeyguardPresentation, + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG + ) { + + private lateinit var keyguardStatusViewController: KeyguardStatusViewController + private lateinit var clock: KeyguardStatusView + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView( + LayoutInflater.from(context) + .inflate(R.layout.keyguard_clock_presentation, /* root= */ null) + ) + val window = window ?: error("no window available.") + + // Logic to make the lock screen fullscreen + window.decorView.systemUiVisibility = + (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_LAYOUT_STABLE) + window.attributes.fitInsetsTypes = 0 + window.isNavigationBarContrastEnforced = false + window.navigationBarColor = Color.TRANSPARENT + + clock = findViewById(R.id.clock) + keyguardStatusViewController = + keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply { + setDisplayedOnSecondaryDisplay() + init() + } + } + + /** [ConnectedDisplayKeyguardPresentation] factory. */ + @AssistedFactory + interface Factory { + /** Creates a new [Presentation] for the given [display]. */ + fun create( + display: Display, + ): ConnectedDisplayKeyguardPresentation + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index b81e08183cdc..e3f9de11bf98 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -102,6 +102,12 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey super.onViewAttached(); mView.setKeyDownListener(mKeyDownListener); mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback); + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); + if (shouldLockout(deadline)) { + handleAttemptLockout(deadline); + } } @Override @@ -278,12 +284,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey @Override public void onResume(int reason) { mResumed = true; - // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser()); - if (shouldLockout(deadline)) { - handleAttemptLockout(deadline); - } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 30b8ed0f1750..06b66922466c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,10 +1,5 @@ package com.android.keyguard; -import static android.view.View.ALPHA; -import static android.view.View.SCALE_X; -import static android.view.View.SCALE_Y; -import static android.view.View.TRANSLATION_Y; - import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN; import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN; import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE; @@ -44,6 +39,7 @@ import java.lang.annotation.RetentionPolicy; public class KeyguardClockSwitch extends RelativeLayout { private static final String TAG = "KeyguardClockSwitch"; + public static final String MISSING_CLOCK_ID = "CLOCK_MISSING"; private static final long CLOCK_OUT_MILLIS = 133; private static final long CLOCK_IN_MILLIS = 167; @@ -149,6 +145,13 @@ public class KeyguardClockSwitch extends RelativeLayout { updateStatusArea(/* animate= */false); } + /** Sets whether the large clock is being shown on a connected display. */ + public void setLargeClockOnSecondaryDisplay(boolean onSecondaryDisplay) { + if (mClock != null) { + mClock.getLargeClock().getEvents().onSecondaryDisplayChanged(onSecondaryDisplay); + } + } + /** * Enable or disable split shade specific positioning */ @@ -194,6 +197,14 @@ public class KeyguardClockSwitch extends RelativeLayout { return mLogBuffer; } + /** Returns the id of the currently rendering clock */ + public String getClockId() { + if (mClock == null) { + return MISSING_CLOCK_ID; + } + return mClock.getConfig().getId(); + } + void setClock(ClockController clock, int statusBarState) { mClock = clock; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 3d48f3cc5359..05ace74306bc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -104,6 +104,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private boolean mShownOnSecondaryDisplay = false; private boolean mOnlyClock = false; private boolean mIsActiveDreamLockscreenHosted = false; private FeatureFlags mFeatureFlags; @@ -185,8 +186,18 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** + * When set, limits the information shown in an external display. + */ + public void setShownOnSecondaryDisplay(boolean shownOnSecondaryDisplay) { + mShownOnSecondaryDisplay = shownOnSecondaryDisplay; + } + + /** * Mostly used for alternate displays, limit the information shown + * + * @deprecated use {@link KeyguardClockSwitchController#setShownOnSecondaryDisplay} */ + @Deprecated public void setOnlyClock(boolean onlyClock) { mOnlyClock = onlyClock; } @@ -211,8 +222,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view); mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large); - mDumpManager.unregisterDumpable(getClass().getSimpleName()); // unregister previous clocks - mDumpManager.registerDumpable(getClass().getSimpleName(), this); + if (!mOnlyClock) { + mDumpManager.unregisterDumpable(getClass().getSimpleName()); // unregister previous + mDumpManager.registerDumpable(getClass().getSimpleName(), this); + } if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { mStatusArea = mView.findViewById(R.id.keyguard_status_area); @@ -221,6 +234,15 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } + private void hideSliceViewAndNotificationIconContainer() { + View ksv = mView.findViewById(R.id.keyguard_slice_view); + ksv.setVisibility(View.GONE); + + View nic = mView.findViewById( + R.id.left_aligned_notification_icon_container); + nic.setVisibility(View.GONE); + } + @Override protected void onViewAttached() { mClockRegistry.registerClockChangeListener(mClockChangedListener); @@ -234,13 +256,15 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mKeyguardDateWeatherViewInvisibility = mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility); - if (mOnlyClock) { - View ksv = mView.findViewById(R.id.keyguard_slice_view); - ksv.setVisibility(View.GONE); + if (mShownOnSecondaryDisplay) { + mView.setLargeClockOnSecondaryDisplay(true); + displayClock(LARGE, /* animate= */ false); + hideSliceViewAndNotificationIconContainer(); + return; + } - View nic = mView.findViewById( - R.id.left_aligned_notification_icon_container); - nic.setVisibility(View.GONE); + if (mOnlyClock) { + hideSliceViewAndNotificationIconContainer(); return; } updateAodIcons(); @@ -293,6 +317,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS setClock(null); mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver); + mSecureSettings.unregisterContentObserver(mShowWeatherObserver); mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener( mKeyguardUnlockAnimationListener); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 9f21a31bd2c0..1c5a5758c2a4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -43,16 +43,19 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.policy.KeyguardStateController; +import dagger.Lazy; + import java.util.concurrent.Executor; import javax.inject.Inject; -import dagger.Lazy; @SysUISingleton public class KeyguardDisplayManager { @@ -64,6 +67,9 @@ public class KeyguardDisplayManager { private final DisplayTracker mDisplayTracker; private final Lazy<NavigationBarController> mNavigationBarControllerLazy; private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + private final ConnectedDisplayKeyguardPresentation.Factory + mConnectedDisplayKeyguardPresentationFactory; + private final FeatureFlags mFeatureFlags; private final Context mContext; private boolean mShowing; @@ -105,7 +111,10 @@ public class KeyguardDisplayManager { @Main Executor mainExecutor, @UiBackground Executor uiBgExecutor, DeviceStateHelper deviceStateHelper, - KeyguardStateController keyguardStateController) { + KeyguardStateController keyguardStateController, + ConnectedDisplayKeyguardPresentation.Factory + connectedDisplayKeyguardPresentationFactory, + FeatureFlags featureFlags) { mContext = context; mNavigationBarControllerLazy = navigationBarControllerLazy; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; @@ -115,6 +124,8 @@ public class KeyguardDisplayManager { mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor); mDeviceStateHelper = deviceStateHelper; mKeyguardStateController = keyguardStateController; + mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory; + mFeatureFlags = featureFlags; } private boolean isKeyguardShowable(Display display) { @@ -185,8 +196,12 @@ public class KeyguardDisplayManager { return false; } - KeyguardPresentation createPresentation(Display display) { - return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory); + Presentation createPresentation(Display display) { + if (mFeatureFlags.isEnabled(Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION)) { + return mConnectedDisplayKeyguardPresentationFactory.create(display); + } else { + return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory); + } } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt index f23ae67992a1..4160ae1a3ca3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt @@ -42,7 +42,6 @@ data class KeyguardFingerprintListenModel( var keyguardIsVisible: Boolean = false, var keyguardOccluded: Boolean = false, var occludingAppRequestingFp: Boolean = false, - var shouldListenSfpsState: Boolean = false, var shouldListenForFingerprintAssistant: Boolean = false, var strongerAuthRequired: Boolean = false, var switchingUser: Boolean = false, @@ -74,7 +73,6 @@ data class KeyguardFingerprintListenModel( keyguardIsVisible.toString(), keyguardOccluded.toString(), occludingAppRequestingFp.toString(), - shouldListenSfpsState.toString(), shouldListenForFingerprintAssistant.toString(), strongerAuthRequired.toString(), switchingUser.toString(), @@ -115,7 +113,6 @@ data class KeyguardFingerprintListenModel( keyguardIsVisible = model.keyguardIsVisible keyguardOccluded = model.keyguardOccluded occludingAppRequestingFp = model.occludingAppRequestingFp - shouldListenSfpsState = model.shouldListenSfpsState shouldListenForFingerprintAssistant = model.shouldListenForFingerprintAssistant strongerAuthRequired = model.strongerAuthRequired switchingUser = model.switchingUser diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index 20e465693108..42dbc487d774 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -30,13 +30,13 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.bouncer.ui.BouncerMessageView; +import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; -import com.android.systemui.bouncer.ui.BouncerMessageView; -import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder; import com.android.systemui.log.BouncerLogger; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.util.ViewController; @@ -95,6 +95,12 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> @CallSuper protected void onViewAttached() { updateMessageAreaVisibility(); + if (TextUtils.isEmpty(mMessageAreaController.getMessage()) + && getInitialMessageResId() != 0) { + mMessageAreaController.setMessage( + mView.getResources().getString(getInitialMessageResId()), + /* animate= */ false); + } } private void updateMessageAreaVisibility() { @@ -147,12 +153,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> } public void startAppearAnimation() { - if (TextUtils.isEmpty(mMessageAreaController.getMessage()) - && getInitialMessageResId() != 0) { - mMessageAreaController.setMessage( - mView.getResources().getString(getInitialMessageResId()), - /* animate= */ false); - } mView.startAppearAnimation(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 49f788ce8f75..a30b4479fe95 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -238,6 +238,12 @@ public class KeyguardPatternViewController } mView.onDevicePostureChanged(mPostureController.getDevicePosture()); mPostureController.addCallback(mPostureCallback); + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); + if (deadline != 0) { + handleAttemptLockout(deadline); + } } @Override @@ -268,12 +274,6 @@ public class KeyguardPatternViewController @Override public void onResume(int reason) { super.onResume(reason); - // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser()); - if (deadline != 0) { - handleAttemptLockout(deadline); - } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index b3e08c0bc69f..574a0591bd51 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -79,6 +79,10 @@ public class KeyguardPinViewController mPasswordEntry.setUserActivityListener(this::onUserInput); mView.onDevicePostureChanged(mPostureController.getDevicePosture()); mPostureController.addCallback(mPostureCallback); + if (mFeatureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)) { + mPasswordEntry.setUsePinShapes(true); + updateAutoConfirmationState(); + } } protected void onUserInput() { @@ -100,10 +104,6 @@ public class KeyguardPinViewController @Override public void startAppearAnimation() { - if (mFeatureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)) { - mPasswordEntry.setUsePinShapes(true); - updateAutoConfirmationState(); - } super.startAppearAnimation(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index d9a1dc66928c..9f3908a4ab92 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -27,6 +27,7 @@ import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY; import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER; import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES; import android.app.ActivityManager; @@ -84,6 +85,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.domain.interactor.UserInteractor; @@ -146,8 +148,19 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private int mLastOrientation; private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; + private int mCurrentUser = UserHandle.USER_NULL; private UserSwitcherController.UserSwitchCallback mUserSwitchCallback = - () -> showPrimarySecurityScreen(false); + new UserSwitcherController.UserSwitchCallback() { + @Override + public void onUserSwitched() { + if (mCurrentUser == KeyguardUpdateMonitor.getCurrentUser()) { + return; + } + mCurrentUser = KeyguardUpdateMonitor.getCurrentUser(); + showPrimarySecurityScreen(false); + reinflateViewFlipper((l) -> {}); + } + }; @VisibleForTesting final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() { @@ -343,7 +356,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @Override public void onThemeChanged() { reloadColors(); - reset(); } @Override @@ -359,8 +371,12 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @Override public void onOrientationChanged(int orientation) { - KeyguardSecurityContainerController.this + if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) { + // TODO(b/295603468) + // Fix reinflation of views when flag is enabled. + KeyguardSecurityContainerController.this .onDensityOrFontScaleOrOrientationChanged(); + } } }; private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = @@ -401,6 +417,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final UserInteractor mUserInteractor; private final Provider<AuthenticationInteractor> mAuthenticationInteractor; private final Provider<JavaAdapter> mJavaAdapter; + private final DeviceProvisionedController mDeviceProvisionedController; @Nullable private Job mSceneTransitionCollectionJob; @Inject @@ -429,6 +446,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard BouncerMessageInteractor bouncerMessageInteractor, Provider<JavaAdapter> javaAdapter, UserInteractor userInteractor, + DeviceProvisionedController deviceProvisionedController, FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, KeyguardTransitionInteractor keyguardTransitionInteractor, Provider<AuthenticationInteractor> authenticationInteractor @@ -463,6 +481,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAuthenticationInteractor = authenticationInteractor; mJavaAdapter = javaAdapter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; + mDeviceProvisionedController = deviceProvisionedController; } @Override @@ -847,9 +866,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); boolean isLockscreenDisabled = mLockPatternUtils.isLockScreenDisabled( - KeyguardUpdateMonitor.getCurrentUser()); - if (securityMode == SecurityMode.None || isLockscreenDisabled) { - finish = isLockscreenDisabled; + KeyguardUpdateMonitor.getCurrentUser()) + || !mDeviceProvisionedController.isUserSetup(targetUserId); + + if (securityMode == SecurityMode.None && isLockscreenDisabled) { + finish = true; eventSubtype = BOUNCER_DISMISS_SIM; uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; } else { @@ -1164,7 +1185,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } private void reloadColors() { - reinflateViewFlipper(controller -> mView.reloadColors()); + mView.reloadColors(); } /** Handles density or font scale changes. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 757022dd153b..c314586e4a21 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -187,6 +187,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mConfigurationController.removeCallback(mConfigurationListener); } + /** Sets the StatusView as shown on an external display. */ + public void setDisplayedOnSecondaryDisplay() { + mKeyguardClockSwitchController.setShownOnSecondaryDisplay(true); + } + /** * Called in notificationPanelViewController to avoid leak */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 853a62a09f83..0ba6c0576663 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3128,18 +3128,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && !strongerAuthRequired && userDoesNotHaveTrust); - boolean shouldListenSideFpsState = true; - if (isSideFps) { - final boolean interactiveToAuthEnabled = - mFingerprintInteractiveToAuthProvider != null && - mFingerprintInteractiveToAuthProvider.isEnabled(getCurrentUser()); - shouldListenSideFpsState = - interactiveToAuthEnabled ? isDeviceInteractive() && !mGoingToSleep : true; - } boolean shouldListen = shouldListenKeyguardState && shouldListenUserState - && shouldListenBouncerState && shouldListenUdfpsState - && shouldListenSideFpsState; + && shouldListenBouncerState && shouldListenUdfpsState; logListenerModelData( new KeyguardFingerprintListenModel( System.currentTimeMillis(), @@ -3160,7 +3151,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab isKeyguardVisible(), mKeyguardOccluded, mOccludingAppRequestingFp, - shouldListenSideFpsState, shouldListenForFingerprintAssistant, strongerAuthRequired, mSwitchingUser, @@ -3314,6 +3304,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFingerprintDetectionCallback, new FingerprintAuthenticateOptions.Builder() .setUserId(userId) + .setVendorReason( + mFingerprintInteractiveToAuthProvider.getVendorExtension( + getCurrentUser())) .build()); } else { mLogger.v("startListeningForFingerprint"); @@ -3322,6 +3315,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab null /* handler */, new FingerprintAuthenticateOptions.Builder() .setUserId(userId) + .setVendorReason( + mFingerprintInteractiveToAuthProvider.getVendorExtension( + getCurrentUser())) .build() ); } @@ -4497,14 +4493,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } else if (isSfpsSupported()) { pw.println(" sfpsEnrolled=" + isSfpsEnrolled()); pw.println(" shouldListenForSfps=" + shouldListenForFingerprint(false)); - if (isSfpsEnrolled()) { - final boolean interactiveToAuthEnabled = - mFingerprintInteractiveToAuthProvider != null && - mFingerprintInteractiveToAuthProvider - .isEnabled(getCurrentUser()); - pw.println(" interactiveToAuthEnabled=" - + interactiveToAuthEnabled); - } } new DumpsysTableLogger( "KeyguardFingerprintListen", diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index c28a9ebb98e9..eec16e6dc301 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -34,7 +34,6 @@ import android.os.UserHandle; import android.permission.PermissionManager; import android.util.ArraySet; import android.util.Log; -import android.util.Slog; import android.util.SparseArray; import androidx.annotation.WorkerThread; @@ -103,7 +102,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_MICROPHONE, AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, - AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO + AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, + AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO }; protected static final int[] OPS_CAMERA = new int[] { @@ -118,12 +118,10 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon }; protected static final int[] OPS_OTHERS = new int[] { - AppOpsManager.OP_SYSTEM_ALERT_WINDOW, - AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO + AppOpsManager.OP_SYSTEM_ALERT_WINDOW }; - - protected static final int[] OPS = concatOps(OPS_MIC, OPS_CAMERA, OPS_LOC, OPS_OTHERS); + protected static final int[] OPS = concatOps(OPS_MIC, OPS_CAMERA, OPS_LOC, OPS_OTHERS); /** * @param opArrays the given op arrays. diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 2b83e6b05bbc..590056f2f8c2 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -23,6 +23,8 @@ import android.service.voice.VoiceInteractionSession; import android.util.Log; import com.android.internal.app.AssistUtils; +import com.android.internal.app.IVisualQueryDetectionAttentionListener; +import com.android.internal.app.IVisualQueryRecognitionStatusListener; import com.android.internal.app.IVoiceInteractionSessionListener; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -39,10 +41,13 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.settings.SecureSettings; -import javax.inject.Inject; - import dagger.Lazy; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + /** * Class to manage everything related to assist in SystemUI. */ @@ -78,6 +83,18 @@ public class AssistManager { void hide(); } + /** + * An interface for a listener that receives notification that visual query attention has + * either been gained or lost. + */ + public interface VisualQueryAttentionListener { + /** Called when visual query attention has been gained. */ + void onAttentionGained(); + + /** Called when visual query attention has been lost. */ + void onAttentionLost(); + } + private static final String TAG = "AssistManager"; // Note that VERBOSE logging may leak PII (e.g. transcription contents). @@ -127,6 +144,23 @@ public class AssistManager { private final SecureSettings mSecureSettings; private final DeviceProvisionedController mDeviceProvisionedController; + + private final List<VisualQueryAttentionListener> mVisualQueryAttentionListeners = + new ArrayList<>(); + + private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener = + new IVisualQueryDetectionAttentionListener.Stub() { + @Override + public void onAttentionGained() { + mVisualQueryAttentionListeners.forEach(VisualQueryAttentionListener::onAttentionGained); + } + + @Override + public void onAttentionLost() { + mVisualQueryAttentionListeners.forEach(VisualQueryAttentionListener::onAttentionLost); + } + }; + private final CommandQueue mCommandQueue; protected final AssistUtils mAssistUtils; @@ -157,6 +191,7 @@ public class AssistManager { mSecureSettings = secureSettings; registerVoiceInteractionSessionListener(); + registerVisualQueryRecognitionStatusListener(); mUiController = defaultUiController; @@ -266,6 +301,24 @@ public class AssistManager { mAssistUtils.hideCurrentSession(); } + /** + * Add the given {@link VisualQueryAttentionListener} to the list of listeners awaiting + * notification of gaining/losing visual query attention. + */ + public void addVisualQueryAttentionListener(VisualQueryAttentionListener listener) { + if (!mVisualQueryAttentionListeners.contains(listener)) { + mVisualQueryAttentionListeners.add(listener); + } + } + + /** + * Remove the given {@link VisualQueryAttentionListener} from the list of listeners awaiting + * notification of gaining/losing visual query attention. + */ + public void removeVisualQueryAttentionListener(VisualQueryAttentionListener listener) { + mVisualQueryAttentionListeners.remove(listener); + } + private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent, boolean isService) { if (isService) { @@ -326,6 +379,27 @@ public class AssistManager { null, null); } + private void registerVisualQueryRecognitionStatusListener() { + if (!mContext.getResources() + .getBoolean(R.bool.config_enableVisualQueryAttentionDetection)) { + return; + } + + mAssistUtils.subscribeVisualQueryRecognitionStatus( + new IVisualQueryRecognitionStatusListener.Stub() { + @Override + public void onStartPerceiving() { + mAssistUtils.enableVisualQueryDetection( + mVisualQueryDetectionAttentionListener); + } + + @Override + public void onStopPerceiving() { + mAssistUtils.disableVisualQueryDetection(); + } + }); + } + public void launchVoiceAssistFromKeyguard() { mAssistUtils.launchVoiceAssistFromKeyguard(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java index 902bb18d17b2..4bc07e825d65 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import android.hardware.biometrics.common.AuthenticateReason; + /** Provides the status of the interactive to auth feature. */ public interface FingerprintInteractiveToAuthProvider { /** @@ -24,4 +26,11 @@ public interface FingerprintInteractiveToAuthProvider { * @return true if the InteractiveToAuthFeature is enabled, false if disabled. */ boolean isEnabled(int userId); + + /** + * + * @param userId the user Id. + * @return Vendor extension if needed for authentication. + */ + AuthenticateReason.Vendor getVendorExtension(int userId); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt index caebc30d1c07..a3ee220d3c3f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt @@ -16,6 +16,7 @@ sealed class BiometricPromptRequest( val description: String, val userInfo: BiometricUserInfo, val operationInfo: BiometricOperationInfo, + val showEmergencyCallButton: Boolean, ) { /** Prompt using one or more biometrics. */ class Biometric( @@ -29,7 +30,8 @@ sealed class BiometricPromptRequest( subtitle = info.subtitle?.toString() ?: "", description = info.description?.toString() ?: "", userInfo = userInfo, - operationInfo = operationInfo + operationInfo = operationInfo, + showEmergencyCallButton = info.isShowEmergencyCallButton ) { val negativeButtonText: String = info.negativeButtonText?.toString() ?: "" } @@ -46,6 +48,7 @@ sealed class BiometricPromptRequest( description = (info.deviceCredentialDescription ?: info.description)?.toString() ?: "", userInfo = userInfo, operationInfo = operationInfo, + showEmergencyCallButton = info.isShowEmergencyCallButton ) { /** PIN prompt. */ diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt index 9292bd7ecd60..4ac9f967920f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt @@ -2,6 +2,7 @@ package com.android.systemui.biometrics.ui.binder import android.view.View import android.view.ViewGroup +import android.widget.Button import android.widget.ImageView import android.widget.TextView import androidx.lifecycle.Lifecycle @@ -46,6 +47,7 @@ object CredentialViewBinder { val descriptionView: TextView = view.requireViewById(R.id.description) val iconView: ImageView? = view.findViewById(R.id.icon) val errorView: TextView = view.requireViewById(R.id.error) + val emergencyButtonView: Button = view.requireViewById(R.id.emergencyCallButton) var errorTimer: Job? = null @@ -75,6 +77,13 @@ object CredentialViewBinder { iconView?.setImageDrawable(header.icon) + if (header.showEmergencyCallButton) { + emergencyButtonView.visibility = View.VISIBLE + emergencyButtonView.setOnClickListener { + viewModel.doEmergencyCall(view.context) + } + } + // Only animate this if we're transitioning from a biometric view. if (viewModel.animateContents.value) { view.animateCredentialViewIn() diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt index 3257f20815d8..c6d90855e7d2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt @@ -10,4 +10,5 @@ interface CredentialHeaderViewModel { val subtitle: String val description: String val icon: Drawable + val showEmergencyCallButton: Boolean } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt index a3b23ca89cad..6212ef000ff1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt @@ -41,6 +41,7 @@ constructor( subtitle = request.subtitle, description = request.description, icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId), + showEmergencyCallButton = request.showEmergencyCallButton ) } @@ -136,6 +137,18 @@ constructor( } } } + + fun doEmergencyCall(context: Context) { + val intent = + context + .getSystemService(android.telecom.TelecomManager::class.java)!! + .createLaunchEmergencyDialerIntent(null) + .setFlags( + android.content.Intent.FLAG_ACTIVITY_NEW_TASK or + android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP + ) + context.startActivity(intent) + } } private fun Context.asBadCredentialErrorMessage(prompt: BiometricPromptRequest?): String = @@ -174,6 +187,7 @@ private class BiometricPromptHeaderViewModelImpl( override val subtitle: String, override val description: String, override val icon: Drawable, + override val showEmergencyCallButton: Boolean, ) : CredentialHeaderViewModel private fun CredentialHeaderViewModel.asRequest(): BiometricPromptRequest.Credential = diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt index 1817ea9024fc..64bf688b3c5d 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt @@ -36,7 +36,6 @@ import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINL import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R.string.bouncer_face_not_recognized import com.android.systemui.R.string.keyguard_enter_password import com.android.systemui.R.string.keyguard_enter_pattern @@ -80,13 +79,14 @@ import com.android.systemui.R.string.kg_wrong_pin_try_again import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import javax.inject.Inject @SysUISingleton class BouncerMessageFactory @Inject constructor( - private val updateMonitor: KeyguardUpdateMonitor, + private val biometricSettingsRepository: BiometricSettingsRepository, private val securityModel: KeyguardSecurityModel, ) { @@ -99,7 +99,7 @@ constructor( getBouncerMessage( reason, securityModel.getSecurityMode(userId), - updateMonitor.isUnlockingWithFingerprintAllowed + biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value ) return pair?.let { BouncerMessageModel( diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt index 6fb0d4cc5e13..97c1bdb180a1 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt @@ -123,20 +123,11 @@ constructor( fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, ) : BouncerMessageRepository { - private val isFaceEnrolledAndEnabled = - and( - biometricSettingsRepository.isFaceAuthenticationEnabled, - biometricSettingsRepository.isFaceEnrolled - ) - - private val isFingerprintEnrolledAndEnabled = - and( - biometricSettingsRepository.isFingerprintEnabledByDevicePolicy, - biometricSettingsRepository.isFingerprintEnrolled - ) - private val isAnyBiometricsEnabledAndEnrolled = - or(isFaceEnrolledAndEnabled, isFingerprintEnrolledAndEnabled) + or( + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, + biometricSettingsRepository.isFingerprintEnrolledAndEnabled, + ) private val wasRebootedForMainlineUpdate get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE @@ -335,8 +326,5 @@ constructor( } } -private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) = - flow.combine(anotherFlow) { a, b -> a && b } - private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) = flow.combine(anotherFlow) { a, b -> a || b } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index 98ae54b1340e..9a7fec1daae0 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -83,9 +83,7 @@ constructor( fun canShowAlternateBouncerForFingerprint(): Boolean { return bouncerRepository.alternateBouncerUIAvailable.value && - biometricSettingsRepository.isFingerprintEnrolled.value && - biometricSettingsRepository.isStrongBiometricAllowed.value && - biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value && + biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value && !keyguardUpdateMonitor.isFingerprintLockedOut && !keyguardStateController.isUnlocked && !statusBarStateController.isDozing diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java index 566a74ae3e07..d58fab45093d 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java @@ -27,7 +27,8 @@ import android.text.TextUtils; import com.android.systemui.R; class IntentCreator { - private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard"; + private static final String EXTRA_EDIT_SOURCE = "edit_source"; + private static final String EDIT_SOURCE_CLIPBOARD = "clipboard"; private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY"; static Intent getTextEditorIntent(Context context) { @@ -74,7 +75,7 @@ class IntentCreator { editIntent.setDataAndType(uri, "image/*"); editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - editIntent.putExtra(EXTRA_EDIT_SOURCE_CLIPBOARD, true); + editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD); return editIntent; } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt new file mode 100644 index 000000000000..e2a7d077a32c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt @@ -0,0 +1,170 @@ +/* + * 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.communal.data.repository + +import android.appwidget.AppWidgetHost +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetProviderInfo +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.os.UserManager +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.communal.shared.CommunalAppWidgetInfo +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger +import com.android.systemui.log.dagger.CommunalLog +import com.android.systemui.settings.UserTracker +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.map + +/** Encapsulates the state of widgets for communal mode. */ +interface CommunalWidgetRepository { + /** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */ + val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> +} + +@SysUISingleton +class CommunalWidgetRepositoryImpl +@Inject +constructor( + private val appWidgetManager: AppWidgetManager, + private val appWidgetHost: AppWidgetHost, + broadcastDispatcher: BroadcastDispatcher, + private val packageManager: PackageManager, + private val userManager: UserManager, + private val userTracker: UserTracker, + @CommunalLog logBuffer: LogBuffer, + featureFlags: FeatureFlags, +) : CommunalWidgetRepository { + companion object { + const val TAG = "CommunalWidgetRepository" + const val WIDGET_LABEL = "Stopwatch" + } + + private val logger = Logger(logBuffer, TAG) + + // Whether the [AppWidgetHost] is listening for updates. + private var isHostListening = false + + // Widgets that should be rendered in communal mode. + private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf() + + private val isUserUnlocked: Flow<Boolean> = callbackFlow { + if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) { + awaitClose() + } + + fun isUserUnlockingOrUnlocked(): Boolean { + return userManager.isUserUnlockingOrUnlocked(userTracker.userHandle) + } + + fun send() { + trySendWithFailureLogging(isUserUnlockingOrUnlocked(), TAG) + } + + if (isUserUnlockingOrUnlocked()) { + send() + awaitClose() + } else { + val receiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + send() + } + } + + broadcastDispatcher.registerReceiver( + receiver, + IntentFilter(Intent.ACTION_USER_UNLOCKED), + ) + + awaitClose { broadcastDispatcher.unregisterReceiver(receiver) } + } + } + + override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = + isUserUnlocked.map { isUserUnlocked -> + if (!isUserUnlocked) { + clearWidgets() + stopListening() + return@map null + } + + startListening() + + val providerInfo = + appWidgetManager.installedProviders.find { + it.loadLabel(packageManager).equals(WIDGET_LABEL) + } + + if (providerInfo == null) { + logger.w("Cannot find app widget: $WIDGET_LABEL") + return@map null + } + + return@map addWidget(providerInfo) + } + + private fun startListening() { + if (isHostListening) { + return + } + + appWidgetHost.startListening() + isHostListening = true + } + + private fun stopListening() { + if (!isHostListening) { + return + } + + appWidgetHost.stopListening() + isHostListening = false + } + + private fun addWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo { + val existing = widgets.values.firstOrNull { it.providerInfo == providerInfo } + if (existing != null) { + return existing + } + + val appWidgetId = appWidgetHost.allocateAppWidgetId() + val widget = + CommunalAppWidgetInfo( + providerInfo, + appWidgetId, + ) + widgets[appWidgetId] = widget + return widget + } + + private fun clearWidgets() { + widgets.keys.forEach { appWidgetId -> appWidgetHost.deleteAppWidgetId(appWidgetId) } + widgets.clear() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt new file mode 100644 index 000000000000..3d1185b79275 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt @@ -0,0 +1,49 @@ +/* + * 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.communal.data.repository + +import android.appwidget.AppWidgetHost +import android.appwidget.AppWidgetManager +import android.content.Context +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import dagger.Binds +import dagger.Module +import dagger.Provides + +@Module +interface CommunalWidgetRepositoryModule { + companion object { + private const val APP_WIDGET_HOST_ID = 116 + + @SysUISingleton + @Provides + fun provideAppWidgetManager(@Application context: Context): AppWidgetManager { + return AppWidgetManager.getInstance(context) + } + + @SysUISingleton + @Provides + fun provideAppWidgetHost(@Application context: Context): AppWidgetHost { + return AppWidgetHost(context, APP_WIDGET_HOST_ID) + } + } + + @Binds + fun communalWidgetRepository(impl: CommunalWidgetRepositoryImpl): CommunalWidgetRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt new file mode 100644 index 000000000000..6dc305e6f826 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -0,0 +1,34 @@ +/* + * 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.communal.domain.interactor + +import com.android.systemui.communal.data.repository.CommunalWidgetRepository +import com.android.systemui.communal.shared.CommunalAppWidgetInfo +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Encapsulates business-logic related to communal mode. */ +@SysUISingleton +class CommunalInteractor +@Inject +constructor( + widgetRepository: CommunalWidgetRepository, +) { + /** A flow of info about the widget to be displayed, or null if widget is unavailable. */ + val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt new file mode 100644 index 000000000000..0803a01b93b8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt @@ -0,0 +1,26 @@ +/* + * 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.communal.shared + +import android.appwidget.AppWidgetProviderInfo + +/** A data class that stores info about an app widget that displays in communal mode. */ +data class CommunalAppWidgetInfo( + val providerInfo: AppWidgetProviderInfo, + val appWidgetId: Int, +) diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt new file mode 100644 index 000000000000..2a08d7f6bc28 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt @@ -0,0 +1,80 @@ +/* + * 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.communal.ui.adapter + +import android.appwidget.AppWidgetHost +import android.appwidget.AppWidgetManager +import android.content.Context +import android.util.SizeF +import com.android.systemui.communal.shared.CommunalAppWidgetInfo +import com.android.systemui.communal.ui.view.CommunalWidgetWrapper +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger +import com.android.systemui.log.dagger.CommunalLog +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** Transforms a [CommunalAppWidgetInfo] to a view that renders the widget. */ +class CommunalWidgetViewAdapter +@Inject +constructor( + @Application private val context: Context, + private val appWidgetManager: AppWidgetManager, + private val appWidgetHost: AppWidgetHost, + @CommunalLog logBuffer: LogBuffer, +) { + companion object { + private const val TAG = "CommunalWidgetViewAdapter" + } + + private val logger = Logger(logBuffer, TAG) + + fun adapt(providerInfoFlow: Flow<CommunalAppWidgetInfo?>): Flow<CommunalWidgetWrapper?> = + providerInfoFlow.map { + if (it == null) { + return@map null + } + + val appWidgetId = it.appWidgetId + val providerInfo = it.providerInfo + + if (appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, providerInfo.provider)) { + logger.d("Success binding app widget id: $appWidgetId") + return@map CommunalWidgetWrapper(context).apply { + addView( + appWidgetHost.createView(context, appWidgetId, providerInfo).apply { + // Set the widget to minimum width and height + updateAppWidgetSize( + appWidgetManager.getAppWidgetOptions(appWidgetId), + listOf( + SizeF( + providerInfo.minResizeWidth.toFloat(), + providerInfo.minResizeHeight.toFloat() + ) + ) + ) + } + ) + } + } else { + logger.w("Failed binding app widget id") + return@map null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt new file mode 100644 index 000000000000..1b6d3a8095f0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt @@ -0,0 +1,70 @@ +/* + * 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.communal.ui.binder + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.R +import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter +import com.android.systemui.communal.ui.view.CommunalWidgetWrapper +import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel +import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.lifecycle.repeatWhenAttached +import kotlinx.coroutines.launch + +/** Binds [CommunalWidgetViewModel] to the keyguard root view. */ +object CommunalWidgetViewBinder { + + @JvmStatic + fun bind( + rootView: KeyguardRootView, + viewModel: CommunalWidgetViewModel, + adapter: CommunalWidgetViewAdapter, + keyguardBlueprintInteractor: KeyguardBlueprintInteractor, + ) { + rootView.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + adapter.adapt(viewModel.appWidgetInfo).collect { + val oldView = + rootView.findViewById<CommunalWidgetWrapper>( + R.id.communal_widget_wrapper + ) + var dirty = false + + if (oldView != null) { + rootView.removeView(oldView) + dirty = true + } + + if (it != null) { + rootView.addView(it) + dirty = true + } + + if (dirty) { + keyguardBlueprintInteractor.refreshBlueprint() + } + } + } + + launch { viewModel.alpha.collect { rootView.alpha = it } } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt new file mode 100644 index 000000000000..560f4fac048f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt @@ -0,0 +1,31 @@ +/* + * 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.communal.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.widget.LinearLayout +import com.android.systemui.R + +/** Wraps around a widget rendered in communal mode. */ +class CommunalWidgetWrapper(context: Context, attrs: AttributeSet? = null) : + LinearLayout(context, attrs) { + init { + id = R.id.communal_widget_wrapper + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt new file mode 100644 index 000000000000..c3369da68821 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt @@ -0,0 +1,42 @@ +/* + * 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.communal.ui.view.layout.blueprints + +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardBlueprint +import javax.inject.Inject + +/** Blueprint for communal mode. */ +@SysUISingleton +@JvmSuppressWildcards +class DefaultCommunalBlueprint +@Inject +constructor( + private val defaultCommunalWidgetSection: DefaultCommunalWidgetSection, +) : KeyguardBlueprint { + override val id: String = COMMUNAL + + override fun apply(constraintSet: ConstraintSet) { + defaultCommunalWidgetSection.apply(constraintSet) + } + + companion object { + const val COMMUNAL = "communal" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt new file mode 100644 index 000000000000..b0e3132a1fc7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt @@ -0,0 +1,39 @@ +/* + * 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.communal.ui.view.layout.sections + +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import com.android.systemui.R +import com.android.systemui.keyguard.data.repository.KeyguardSection +import javax.inject.Inject + +class DefaultCommunalWidgetSection @Inject constructor() : KeyguardSection { + private val widgetAreaViewId = R.id.communal_widget_wrapper + + override fun apply(constraintSet: ConstraintSet) { + constraintSet.apply { + constrainWidth(widgetAreaViewId, WRAP_CONTENT) + constrainHeight(widgetAreaViewId, WRAP_CONTENT) + connect(widgetAreaViewId, BOTTOM, PARENT_ID, BOTTOM) + connect(widgetAreaViewId, END, PARENT_ID, END) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt new file mode 100644 index 000000000000..8fba342c49be --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt @@ -0,0 +1,38 @@ +/* + * 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.communal.ui.viewmodel + +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.shared.CommunalAppWidgetInfo +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class CommunalWidgetViewModel +@Inject +constructor( + communalInteractor: CommunalInteractor, + keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel, +) { + /** An observable for the alpha level for the communal widget area. */ + val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha + + /** A flow of info about the widget to be displayed, or null if widget is unavailable. */ + val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = communalInteractor.appWidgetInfo +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt index 8029ba844850..534832c0992d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt @@ -54,9 +54,10 @@ interface ControlActionCoordinator { /** * When a ToggleRange control is interacting with, a drag event is sent. * + * @param cvh [ControlViewHolder] for the control * @param isEdge did the drag event reach a control edge */ - fun drag(isEdge: Boolean) + fun drag(cvh: ControlViewHolder, isEdge: Boolean) /** * Send a request to update the value of a device using the [FloatAction]. diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index a7e9efd8ab03..00d95c02c172 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -37,6 +37,8 @@ import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.policy.KeyguardStateController @@ -57,6 +59,7 @@ class ControlActionCoordinatorImpl @Inject constructor( private val controlsMetricsLogger: ControlsMetricsLogger, private val vibrator: VibratorHelper, private val controlsSettingsRepository: ControlsSettingsRepository, + private val featureFlags: FeatureFlags, ) : ControlActionCoordinator { private var dialog: Dialog? = null private var pendingAction: Action? = null @@ -119,11 +122,17 @@ class ControlActionCoordinatorImpl @Inject constructor( ) } - override fun drag(isEdge: Boolean) { - if (isEdge) { - vibrate(Vibrations.rangeEdgeEffect) + override fun drag(cvh: ControlViewHolder, isEdge: Boolean) { + if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { + val constant = + if (isEdge) + HapticFeedbackConstants.SEGMENT_TICK + else + HapticFeedbackConstants.SEGMENT_FREQUENT_TICK + vibrator.performHapticFeedback(cvh.layout, constant) } else { - vibrate(Vibrations.rangeMiddleEffect) + val effect = if (isEdge) Vibrations.rangeEdgeEffect else Vibrations.rangeMiddleEffect + vibrate(effect) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index b2c95a63ad72..0d570d2dcc73 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -235,7 +235,7 @@ class ToggleRangeBehavior : Behavior { if (isDragging) { val isEdge = newLevel == MIN_LEVEL || newLevel == MAX_LEVEL if (clipLayer.level != newLevel) { - cvh.controlActionCoordinator.drag(isEdge) + cvh.controlActionCoordinator.drag(cvh, isEdge) clipLayer.level = newLevel } } else if (newLevel != clipLayer.level) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index d9665c5b5047..484be9ce1975 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.statusbar.phone.LockscreenWallpaper import com.android.systemui.statusbar.phone.ScrimController +import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import com.android.systemui.theme.ThemeOverlayController @@ -331,4 +332,11 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(ScrimController::class) abstract fun bindScrimController(impl: ScrimController): CoreStartable + + @Binds + @IntoMap + @ClassKey(StatusBarHeadsUpChangeListener::class) + abstract fun bindStatusBarHeadsUpChangeListener( + impl: StatusBarHeadsUpChangeListener + ): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 970d00b2f4bd..3bf9cc87cf50 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -107,6 +107,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationRowCom import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LetterboxModule; +import com.android.systemui.statusbar.phone.NotificationIconAreaControllerModule; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -185,6 +186,7 @@ import javax.inject.Named; MediaProjectionModule.class, MediaProjectionTaskSwitcherModule.class, MotionToolModule.class, + NotificationIconAreaControllerModule.class, PeopleHubModule.class, PeopleModule.class, PluginModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java index c889ac214cda..4dd97d557b30 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java @@ -16,10 +16,9 @@ package com.android.systemui.dreams.conditions; -import com.android.internal.app.AssistUtils; -import com.android.internal.app.IVisualQueryDetectionAttentionListener; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; import com.android.systemui.dagger.qualifiers.Application; -import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.shared.condition.Condition; import javax.inject.Inject; @@ -30,12 +29,10 @@ import kotlinx.coroutines.CoroutineScope; * {@link AssistantAttentionCondition} provides a signal when assistant has the user's attention. */ public class AssistantAttentionCondition extends Condition { - private final DreamOverlayStateController mDreamOverlayStateController; - private final AssistUtils mAssistUtils; - private boolean mEnabled; + private final AssistManager mAssistManager; - private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener = - new IVisualQueryDetectionAttentionListener.Stub() { + private final VisualQueryAttentionListener mVisualQueryAttentionListener = + new VisualQueryAttentionListener() { @Override public void onAttentionGained() { updateCondition(true); @@ -47,59 +44,26 @@ public class AssistantAttentionCondition extends Condition { } }; - private final DreamOverlayStateController.Callback mCallback = - new DreamOverlayStateController.Callback() { - @Override - public void onStateChanged() { - if (mDreamOverlayStateController.isDreamOverlayStatusBarVisible()) { - enableVisualQueryDetection(); - } else { - disableVisualQueryDetection(); - } - } - }; - @Inject public AssistantAttentionCondition( @Application CoroutineScope scope, - DreamOverlayStateController dreamOverlayStateController, - AssistUtils assistUtils) { + AssistManager assistManager) { super(scope); - mDreamOverlayStateController = dreamOverlayStateController; - mAssistUtils = assistUtils; + mAssistManager = assistManager; } @Override protected void start() { - mDreamOverlayStateController.addCallback(mCallback); + mAssistManager.addVisualQueryAttentionListener(mVisualQueryAttentionListener); } @Override protected void stop() { - disableVisualQueryDetection(); - mDreamOverlayStateController.removeCallback(mCallback); + mAssistManager.removeVisualQueryAttentionListener(mVisualQueryAttentionListener); } @Override protected int getStartStrategy() { return START_EAGERLY; } - - private void enableVisualQueryDetection() { - if (mEnabled) { - return; - } - mEnabled = true; - mAssistUtils.enableVisualQueryDetection(mVisualQueryDetectionAttentionListener); - } - - private void disableVisualQueryDetection() { - if (!mEnabled) { - return; - } - mEnabled = false; - mAssistUtils.disableVisualQueryDetection(); - // Make sure the condition is set to false as well. - updateCondition(false); - } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 6a934c1c1110..1516f3e4b817 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -115,7 +115,7 @@ object Flags { // TODO(b/292213543): Tracking Bug @JvmField val NOTIFICATION_GROUP_EXPANSION_CHANGE = - unreleasedFlag("notification_group_expansion_change", teamfood = false) + unreleasedFlag("notification_group_expansion_change", teamfood = true) // 200 - keyguard/lockscreen // ** Flag retired ** @@ -195,7 +195,7 @@ object Flags { // TODO(b/294110497): Tracking Bug @JvmField val ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS = - unreleasedFlag("enable_wallet_contextual_loyalty_cards", teamfood = true) + releasedFlag("enable_wallet_contextual_loyalty_cards") // TODO(b/242908637): Tracking Bug @JvmField val WALLPAPER_FULLSCREEN_PREVIEW = releasedFlag("wallpaper_fullscreen_preview") @@ -355,7 +355,7 @@ object Flags { // TODO(b/278068252): Tracking Bug @JvmField - val QS_PIPELINE_AUTO_ADD = unreleasedFlag("qs_pipeline_auto_add", teamfood = false) + val QS_PIPELINE_AUTO_ADD = unreleasedFlag("qs_pipeline_auto_add", teamfood = true) // TODO(b/254512383): Tracking Bug @JvmField @@ -386,12 +386,11 @@ object Flags { @JvmField val NEW_BLUETOOTH_REPOSITORY = releasedFlag("new_bluetooth_repository") // TODO(b/292533677): Tracking Bug - val WIFI_TRACKER_LIB_FOR_WIFI_ICON = - unreleasedFlag("wifi_tracker_lib_for_wifi_icon", teamfood = true) + val WIFI_TRACKER_LIB_FOR_WIFI_ICON = releasedFlag("wifi_tracker_lib_for_wifi_icon") // TODO(b/293863612): Tracking Bug @JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON = - unreleasedFlag("incompatible_charging_battery_icon") + releasedFlag("incompatible_charging_battery_icon") // TODO(b/293585143): Tracking Bug val INSTANT_TETHER = unreleasedFlag("instant_tether") @@ -513,11 +512,6 @@ object Flags { val ENABLE_FLING_TO_DISMISS_PIP = sysPropBooleanFlag("persist.wm.debug.fling_to_dismiss_pip", default = true) - @Keep - @JvmField - val ENABLE_PIP_KEEP_CLEAR_ALGORITHM = - sysPropBooleanFlag("persist.wm.debug.enable_pip_keep_clear_algorithm", default = true) - // TODO(b/256873975): Tracking Bug @JvmField @Keep @@ -545,12 +539,6 @@ object Flags { val ENABLE_PIP_SIZE_LARGE_SCREEN = sysPropBooleanFlag("persist.wm.debug.enable_pip_size_large_screen", default = true) - // TODO(b/265998256): Tracking bug - @Keep - @JvmField - val ENABLE_PIP_APP_ICON_OVERLAY = - sysPropBooleanFlag("persist.wm.debug.enable_pip_app_icon_overlay", default = true) - // TODO(b/293252410) : Tracking Bug @JvmField @@ -635,6 +623,10 @@ object Flags { // TODO(b/251205791): Tracking Bug @JvmField val SCREENSHOT_APP_CLIPS = releasedFlag("screenshot_app_clips") + /** TODO(b/295143676): Tracking bug. When enable, captures a screenshot for each display. */ + @JvmField + val MULTI_DISPLAY_SCREENSHOT = unreleasedFlag("multi_display_screenshot") + // 1400 - columbus // TODO(b/254512756): Tracking Bug val QUICK_TAP_IN_PCC = releasedFlag("quick_tap_in_pcc") @@ -776,6 +768,10 @@ object Flags { @JvmField val ONE_WAY_HAPTICS_API_MIGRATION = unreleasedFlag("oneway_haptics_api_migration") + /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */ + @JvmField + val ENABLE_CLOCK_KEYGUARD_PRESENTATION = unreleasedFlag("enable_clock_keyguard_presentation") + /** Enable the Compose implementation of the PeopleSpaceActivity. */ @JvmField val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space") @@ -787,4 +783,8 @@ object Flags { /** Enable the share wifi button in Quick Settings internet dialog. */ @JvmField val SHARE_WIFI_QS_BUTTON = unreleasedFlag("share_wifi_qs_button") + + /** Enable haptic slider component in the brightness slider */ + @JvmField + val HAPTIC_BRIGHTNESS_SLIDER = unreleasedFlag("haptic_brightness_slider") } diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt new file mode 100644 index 000000000000..3f2f67dbba37 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyevent.domain.interactor + +import android.view.KeyEvent +import com.android.systemui.back.domain.interactor.BackActionInteractor +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor +import javax.inject.Inject + +/** + * Sends key events to the appropriate interactors and then acts upon key events that haven't + * already been handled but should be handled by SystemUI. + */ +@SysUISingleton +class KeyEventInteractor +@Inject +constructor( + private val backActionInteractor: BackActionInteractor, + private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor, +) { + fun dispatchKeyEvent(event: KeyEvent): Boolean { + if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) { + return true + } + + when (event.keyCode) { + KeyEvent.KEYCODE_BACK -> { + if (event.handleAction()) { + backActionInteractor.onBackRequested() + } + return true + } + } + return false + } + + fun interceptMediaKey(event: KeyEvent): Boolean { + return keyguardKeyEventInteractor.interceptMediaKey(event) + } + + fun dispatchKeyEventPreIme(event: KeyEvent): Boolean { + return keyguardKeyEventInteractor.dispatchKeyEventPreIme(event) + } + + companion object { + // Most actions shouldn't be handled on the down event and instead handled on subsequent + // key events like ACTION_UP. + fun KeyEvent.handleAction(): Boolean { + return action != KeyEvent.ACTION_DOWN + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 8e323d8140d5..9b323ee9a3f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -24,9 +24,13 @@ import com.android.keyguard.KeyguardStatusViewController import com.android.keyguard.dagger.KeyguardStatusViewComponent import com.android.systemui.CoreStartable import com.android.systemui.R +import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter +import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder +import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder @@ -83,6 +87,9 @@ constructor( private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener, private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel, private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory, + private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor, + private val communalWidgetViewModel: CommunalWidgetViewModel, + private val communalWidgetViewAdapter: CommunalWidgetViewAdapter, ) : CoreStartable { private var rootViewHandle: DisposableHandle? = null @@ -106,6 +113,7 @@ constructor( bindRightShortcut() bindAmbientIndicationArea() bindSettingsPopupMenu() + bindCommunalWidgetArea() KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) keyguardBlueprintCommandListener.start() @@ -279,6 +287,19 @@ constructor( } } + private fun bindCommunalWidgetArea() { + if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) { + return + } + + CommunalWidgetViewBinder.bind( + keyguardRootView, + communalWidgetViewModel, + communalWidgetViewAdapter, + keyguardBlueprintInteractor, + ) + } + /** * Temporary, to allow NotificationPanelViewController to use the same instance while code is * migrated: b/288242803 diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 9a44230cf185..13dfe24e7e19 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -37,6 +37,7 @@ import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingModule; +import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -75,11 +76,12 @@ import com.android.systemui.util.time.SystemClock; import com.android.systemui.wallpapers.data.repository.WallpaperRepository; import com.android.wm.shell.keyguard.KeyguardTransitions; -import java.util.concurrent.Executor; - import dagger.Lazy; import dagger.Module; import dagger.Provides; + +import java.util.concurrent.Executor; + import kotlinx.coroutines.CoroutineDispatcher; /** @@ -91,6 +93,7 @@ import kotlinx.coroutines.CoroutineDispatcher; KeyguardStatusViewComponent.class, KeyguardUserSwitcherComponent.class}, includes = { + CommunalWidgetRepositoryModule.class, FalsingModule.class, KeyguardDataQuickAffordanceModule.class, KeyguardRepositoryModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt index ff3e77c46f1c..682e841c3521 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt @@ -28,6 +28,9 @@ import com.android.internal.widget.LockPatternUtils import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.biometrics.AuthController +import com.android.systemui.biometrics.data.repository.FacePropertyRepository +import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -68,34 +71,32 @@ import kotlinx.coroutines.flow.transformLatest * upstream changes. */ interface BiometricSettingsRepository { - /** Whether any fingerprints are enrolled for the current user. */ - val isFingerprintEnrolled: StateFlow<Boolean> - - /** Whether face authentication is enrolled for the current user. */ - val isFaceEnrolled: Flow<Boolean> - /** - * Whether face authentication is enabled/disabled based on system settings like device policy, - * biometrics setting. + * If the current user can enter the device using fingerprint. This is true if user has enrolled + * fingerprints and fingerprint auth is not disabled through settings/device policy */ - val isFaceAuthenticationEnabled: Flow<Boolean> + val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> /** - * Whether the current user is allowed to use a strong biometric for device entry based on - * Android Security policies. If false, the user may be able to use primary authentication for - * device entry. + * If the current user can enter the device using fingerprint, right now. + * + * This returns true if there are no strong auth flags that restrict the user from using + * fingerprint and [isFingerprintEnrolledAndEnabled] is true */ - val isStrongBiometricAllowed: StateFlow<Boolean> + val isFingerprintAuthCurrentlyAllowed: StateFlow<Boolean> /** - * Whether the current user is allowed to use a convenience biometric for device entry based on - * Android Security policies. If false, the user may be able to use strong biometric or primary - * authentication for device entry. + * If the current user can use face auth to enter the device. This is true when the user has + * face auth enrolled, and is enabled in settings/device policy. */ - val isNonStrongBiometricAllowed: StateFlow<Boolean> + val isFaceAuthEnrolledAndEnabled: Flow<Boolean> - /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */ - val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> + /** + * If the current user can use face auth to enter the device right now. This is true when + * [isFaceAuthEnrolledAndEnabled] is true and strong auth settings allow face auth to run and + * face auth is supported by the current device posture. + */ + val isFaceAuthCurrentlyAllowed: Flow<Boolean> /** * Whether face authentication is supported for the current device posture. Face auth can be @@ -130,6 +131,8 @@ constructor( @Background backgroundDispatcher: CoroutineDispatcher, biometricManager: BiometricManager?, devicePostureRepository: DevicePostureRepository, + facePropertyRepository: FacePropertyRepository, + fingerprintPropertyRepository: FingerprintPropertyRepository, dumpManager: DumpManager, ) : BiometricSettingsRepository, Dumpable { @@ -165,7 +168,9 @@ constructor( } override fun dump(pw: PrintWriter, args: Array<String?>) { - pw.println("isFingerprintEnrolled=${isFingerprintEnrolled.value}") + pw.println("isFingerprintEnrolledAndEnabled=${isFingerprintEnrolledAndEnabled.value}") + pw.println("isFingerprintAuthCurrentlyAllowed=${isFingerprintAuthCurrentlyAllowed.value}") + pw.println("isNonStrongBiometricAllowed=${isNonStrongBiometricAllowed.value}") pw.println("isStrongBiometricAllowed=${isStrongBiometricAllowed.value}") pw.println("isFingerprintEnabledByDevicePolicy=${isFingerprintEnabledByDevicePolicy.value}") } @@ -180,7 +185,7 @@ constructor( user = UserHandle.ALL ) - override val isFingerprintEnrolled: StateFlow<Boolean> = + private val isFingerprintEnrolled: Flow<Boolean> = selectedUserId .flatMapLatest { currentUserId -> conflatedCallbackFlow { @@ -211,7 +216,7 @@ constructor( authController.isFingerprintEnrolled(userRepository.getSelectedUserInfo().id) ) - override val isFaceEnrolled: Flow<Boolean> = + private val isFaceEnrolled: Flow<Boolean> = selectedUserId.flatMapLatest { selectedUserId: Int -> conflatedCallbackFlow { val callback = @@ -245,14 +250,6 @@ constructor( isFaceEnabledByBiometricsManager.map { biometricsEnabledForUser[userInfo.id] ?: false } } - override val isFaceAuthenticationEnabled: Flow<Boolean> - get() = - combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) { - biometricsManagerSetting, - devicePolicySetting -> - biometricsManagerSetting && devicePolicySetting - } - private val isFaceEnabledByDevicePolicy: Flow<Boolean> = combine(selectedUserId, devicePolicyChangedForAllUsers) { userId, _ -> devicePolicyManager.isFaceDisabled(userId) @@ -263,6 +260,13 @@ constructor( .flowOn(backgroundDispatcher) .distinctUntilChanged() + private val isFaceAuthenticationEnabled: Flow<Boolean> = + combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) { + biometricsManagerSetting, + devicePolicySetting -> + biometricsManagerSetting && devicePolicySetting + } + private val isFaceEnabledByBiometricsManager: Flow<Pair<Int, Boolean>> = conflatedCallbackFlow { val callback = @@ -283,7 +287,7 @@ constructor( // being registered. .stateIn(scope, SharingStarted.Eagerly, Pair(0, false)) - override val isStrongBiometricAllowed: StateFlow<Boolean> = + private val isStrongBiometricAllowed: StateFlow<Boolean> = strongAuthTracker.isStrongBiometricAllowed.stateIn( scope, SharingStarted.Eagerly, @@ -293,7 +297,7 @@ constructor( ) ) - override val isNonStrongBiometricAllowed: StateFlow<Boolean> = + private val isNonStrongBiometricAllowed: StateFlow<Boolean> = strongAuthTracker.isNonStrongBiometricAllowed.stateIn( scope, SharingStarted.Eagerly, @@ -303,7 +307,19 @@ constructor( ) ) - override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> = + private val isFingerprintBiometricAllowed: Flow<Boolean> = + fingerprintPropertyRepository.strength.flatMapLatest { + if (it == SensorStrength.STRONG) isStrongBiometricAllowed + else isNonStrongBiometricAllowed + } + + private val isFaceBiometricsAllowed: Flow<Boolean> = + facePropertyRepository.sensorInfo.flatMapLatest { + if (it?.strength == SensorStrength.STRONG) isStrongBiometricAllowed + else isNonStrongBiometricAllowed + } + + private val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> = selectedUserId .flatMapLatest { userId -> devicePolicyChangedForAllUsers @@ -319,6 +335,25 @@ constructor( userRepository.getSelectedUserInfo().id ) ) + + override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> = + isFingerprintEnrolled + .and(isFingerprintEnabledByDevicePolicy) + .stateIn(scope, SharingStarted.Eagerly, false) + + override val isFingerprintAuthCurrentlyAllowed: StateFlow<Boolean> = + isFingerprintEnrolledAndEnabled + .and(isFingerprintBiometricAllowed) + .stateIn(scope, SharingStarted.Eagerly, false) + + override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> + get() = isFaceAuthenticationEnabled.and(isFaceEnrolled) + + override val isFaceAuthCurrentlyAllowed: Flow<Boolean> + get() = + isFaceAuthEnrolledAndEnabled + .and(isFaceBiometricsAllowed) + .and(isFaceAuthSupportedInCurrentPosture) } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index 689414711388..93eb103d74e1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -26,8 +26,6 @@ import com.android.internal.logging.UiEventLogger import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.Dumpable import com.android.systemui.R -import com.android.systemui.biometrics.data.repository.FacePropertyRepository -import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -72,7 +70,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -162,7 +159,6 @@ constructor( @FaceAuthTableLog private val faceAuthLog: TableLogBuffer, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val featureFlags: FeatureFlags, - facePropertyRepository: FacePropertyRepository, dumpManager: DumpManager, ) : DeviceEntryFaceAuthRepository, Dumpable { private var authCancellationSignal: CancellationSignal? = null @@ -182,13 +178,6 @@ constructor( override val detectionStatus: Flow<FaceDetectionStatus> get() = _detectionStatus.filterNotNull() - private val isFaceBiometricsAllowed: Flow<Boolean> = - facePropertyRepository.sensorInfo.flatMapLatest { - if (it?.strength == SensorStrength.STRONG) - biometricSettingsRepository.isStrongBiometricAllowed - else biometricSettingsRepository.isNonStrongBiometricAllowed - } - private val _isLockedOut = MutableStateFlow(false) override val isLockedOut: StateFlow<Boolean> = _isLockedOut @@ -313,8 +302,10 @@ constructor( canFaceAuthOrDetectRun(faceDetectLog), logAndObserve(isBypassEnabled, "isBypassEnabled", faceDetectLog), logAndObserve( - isFaceBiometricsAllowed.isFalse().or(trustRepository.isCurrentUserTrusted), - "biometricIsNotAllowedOrCurrentUserIsTrusted", + biometricSettingsRepository.isFaceAuthCurrentlyAllowed + .isFalse() + .or(trustRepository.isCurrentUserTrusted), + "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted", faceDetectLog ), // We don't want to run face detect if fingerprint can be used to unlock the device @@ -346,13 +337,8 @@ constructor( private fun canFaceAuthOrDetectRun(tableLogBuffer: TableLogBuffer): Flow<Boolean> { return listOf( logAndObserve( - biometricSettingsRepository.isFaceEnrolled, - "isFaceEnrolled", - tableLogBuffer - ), - logAndObserve( - biometricSettingsRepository.isFaceAuthenticationEnabled, - "isFaceAuthenticationEnabled", + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, + "isFaceAuthEnrolledAndEnabled", tableLogBuffer ), logAndObserve(faceAuthPaused.isFalse(), "faceAuthIsNotPaused", tableLogBuffer), @@ -406,7 +392,11 @@ constructor( "currentUserIsNotTrusted", faceAuthLog ), - logAndObserve(isFaceBiometricsAllowed, "isFaceBiometricsAllowed", faceAuthLog), + logAndObserve( + biometricSettingsRepository.isFaceAuthCurrentlyAllowed, + "isFaceAuthCurrentlyAllowed", + faceAuthLog + ), logAndObserve(isAuthenticated.isFalse(), "faceNotAuthenticated", faceAuthLog), ) .reduce(::and) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 271bc38899ec..b05089028037 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -50,6 +50,7 @@ constructor( listenForAlternateBouncerToGone() listenForAlternateBouncerToLockscreenAodOrDozing() listenForAlternateBouncerToPrimaryBouncer() + listenForTransitionToCamera(scope, keyguardInteractor) } private fun listenForAlternateBouncerToLockscreenAodOrDozing() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 888f74601ebd..518ae2f4fe7c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -47,6 +47,7 @@ constructor( override fun start() { listenForAodToLockscreenOrOccluded() listenForAodToGone() + listenForTransitionToCamera(scope, keyguardInteractor) } private fun listenForAodToLockscreenOrOccluded() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 76d9893a8927..712215f06472 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -47,6 +47,7 @@ constructor( override fun start() { listenForDozingToLockscreenOrOccluded() listenForDozingToGone() + listenForTransitionToCamera(scope, keyguardInteractor) } private fun listenForDozingToLockscreenOrOccluded() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 954ff6f23561..75aa4b60f7b6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -51,6 +51,7 @@ constructor( listenForDreamingToOccluded() listenForDreamingToGone() listenForDreamingToDozing() + listenForTransitionToCamera(scope, keyguardInteractor) } fun startToLockscreenTransition() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index aa771fd16af8..9c6a1b1b6cc0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -63,12 +63,12 @@ constructor( listenForLockscreenToGone() listenForLockscreenToGoneDragging() listenForLockscreenToOccluded() - listenForLockscreenToCamera() listenForLockscreenToAodOrDozing() listenForLockscreenToPrimaryBouncer() listenForLockscreenToDreaming() listenForLockscreenToPrimaryBouncerDragging() listenForLockscreenToAlternateBouncer() + listenForLockscreenTransitionToCamera() } /** @@ -128,6 +128,10 @@ constructor( } .distinctUntilChanged() + private fun listenForLockscreenTransitionToCamera() { + listenForTransitionToCamera(scope, keyguardInteractor) + } + private fun listenForLockscreenToDreaming() { val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING) scope.launch { @@ -311,7 +315,7 @@ constructor( keyguardInteractor.isKeyguardOccluded .sample( combine( - transitionInteractor.finishedKeyguardState, + transitionInteractor.startedKeyguardState, keyguardInteractor.isDreaming, ::Pair ), @@ -325,27 +329,6 @@ constructor( } } - /** This signal may come in before the occlusion signal, and can provide a custom transition */ - private fun listenForLockscreenToCamera() { - scope.launch { - keyguardInteractor.onCameraLaunchDetected - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) - .collect { (_, lastStartedStep) -> - // DREAMING/AOD/OFF may trigger on the first power button push, so include this - // state in order to cancel and correct the transition - if ( - lastStartedStep.to == KeyguardState.LOCKSCREEN || - lastStartedStep.to == KeyguardState.DREAMING || - lastStartedStep.to == KeyguardState.DOZING || - lastStartedStep.to == KeyguardState.AOD || - lastStartedStep.to == KeyguardState.OFF - ) { - startTransitionTo(KeyguardState.OCCLUDED) - } - } - } - } - private fun listenForLockscreenToAodOrDozing() { scope.launch { keyguardInteractor.wakefulnessModel diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index c9f32dabe736..143be1dfa760 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -61,6 +61,7 @@ constructor( listenForPrimaryBouncerToAodOrDozing() listenForPrimaryBouncerToLockscreenOrOccluded() listenForPrimaryBouncerToDreamingLockscreenHosted() + listenForTransitionToCamera(scope, keyguardInteractor) } val surfaceBehindVisibility: Flow<Boolean?> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 562c4db44be6..ed84884fca14 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -40,10 +40,10 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.kotlin.sample +import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -53,7 +53,6 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart -import javax.inject.Inject /** * Encapsulates business-logic related to the keyguard but not to a more specific part within it. @@ -68,18 +67,6 @@ constructor( bouncerRepository: KeyguardBouncerRepository, configurationRepository: ConfigurationRepository, ) { - - data class PreviewMode( - val isInPreviewMode: Boolean = false, - val shouldHighlightSelectedAffordance: Boolean = false, - ) - - /** - * Whether this view-model instance is powering the preview experience that renders exclusively - * in the wallpaper picker application. This should _always_ be `false` for the real lock screen - * experience. - */ - val previewMode = MutableStateFlow(PreviewMode()) /** * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at * all. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt new file mode 100644 index 000000000000..e501ece626b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt @@ -0,0 +1,127 @@ +/* + * 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.keyguard.domain.interactor + +import android.content.Context +import android.media.AudioManager +import android.view.KeyEvent +import com.android.systemui.back.domain.interactor.BackActionInteractor +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor.Companion.handleAction +import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.ShadeController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import javax.inject.Inject + +/** Handles key events arriving when the keyguard is showing or device is dozing. */ +@SysUISingleton +class KeyguardKeyEventInteractor +@Inject +constructor( + private val context: Context, + private val statusBarStateController: StatusBarStateController, + private val keyguardInteractor: KeyguardInteractor, + private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, + private val shadeController: ShadeController, + private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper, + private val backActionInteractor: BackActionInteractor, +) { + + fun dispatchKeyEvent(event: KeyEvent): Boolean { + if (statusBarStateController.isDozing) { + when (event.keyCode) { + KeyEvent.KEYCODE_VOLUME_DOWN, + KeyEvent.KEYCODE_VOLUME_UP -> return dispatchVolumeKeyEvent(event) + } + } + + if (event.handleAction()) { + when (event.keyCode) { + KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent() + KeyEvent.KEYCODE_SPACE, + KeyEvent.KEYCODE_ENTER -> + if (isDeviceInteractive()) { + return collapseShadeLockedOrShowPrimaryBouncer() + } + } + } + return false + } + + /** + * While IME is active and a BACK event is detected, check with {@link + * StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event should be + * handled before routing to IME, in order to prevent the user from having to hit back twice to + * exit bouncer. + */ + fun dispatchKeyEventPreIme(event: KeyEvent): Boolean { + when (event.keyCode) { + KeyEvent.KEYCODE_BACK -> + if ( + statusBarStateController.state == StatusBarState.KEYGUARD && + statusBarKeyguardViewManager.dispatchBackKeyEventPreIme() + ) { + return backActionInteractor.onBackRequested() + } + } + return false + } + + fun interceptMediaKey(event: KeyEvent): Boolean { + return statusBarStateController.state == StatusBarState.KEYGUARD && + statusBarKeyguardViewManager.interceptMediaKey(event) + } + + private fun dispatchMenuKeyEvent(): Boolean { + val shouldUnlockOnMenuPressed = + isDeviceInteractive() && + (statusBarStateController.state != StatusBarState.SHADE) && + statusBarKeyguardViewManager.shouldDismissOnMenuPressed() + if (shouldUnlockOnMenuPressed) { + return collapseShadeLockedOrShowPrimaryBouncer() + } + return false + } + + private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean { + when (statusBarStateController.state) { + StatusBarState.SHADE -> return false + StatusBarState.SHADE_LOCKED -> { + shadeController.animateCollapseShadeForced() + return true + } + StatusBarState.KEYGUARD -> { + statusBarKeyguardViewManager.showPrimaryBouncer(true) + return true + } + } + return false + } + + private fun dispatchVolumeKeyEvent(event: KeyEvent): Boolean { + mediaSessionLegacyHelperWrapper + .getHelper(context) + .sendVolumeKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE, true) + return true + } + + private fun isDeviceInteractive(): Boolean { + return keyguardInteractor.wakefulnessModel.value.isDeviceInteractive() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index 4f7abd4a2174..0dc16e9e296c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -95,5 +95,11 @@ constructor( logger.log(TAG, VERBOSE, "Doze transition", it) } } + + scope.launch { + keyguardInteractor.onCameraLaunchDetected.collect { + logger.log(TAG, VERBOSE, "onCameraLaunchDetected", it) + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index 0dda625a8b41..54c6d5f6d09f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -21,7 +21,10 @@ import android.util.Log import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo +import com.android.systemui.util.kotlin.sample import java.util.UUID +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch /** * Each TransitionInteractor is responsible for determining under which conditions to notify @@ -46,7 +49,7 @@ sealed class TransitionInteractor( fun startTransitionTo( toState: KeyguardState, animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState), - resetIfCancelled: Boolean = false + resetIfCancelled: Boolean = false, ): UUID? { if ( fromState != transitionInteractor.startedKeyguardState.value && @@ -75,6 +78,27 @@ sealed class TransitionInteractor( ) } + /** This signal may come in before the occlusion signal, and can provide a custom transition */ + fun listenForTransitionToCamera( + scope: CoroutineScope, + keyguardInteractor: KeyguardInteractor, + ) { + scope.launch { + keyguardInteractor.onCameraLaunchDetected + .sample(transitionInteractor.finishedKeyguardState) + .collect { finishedKeyguardState -> + // Other keyguard state transitions may trigger on the first power button push, + // so use the last finishedKeyguardState to determine the overriding FROM state + if (finishedKeyguardState == fromState) { + startTransitionTo( + KeyguardState.OCCLUDED, + resetIfCancelled = true, + ) + } + } + } + } + /** * Returns a ValueAnimator to be used for transitions to [toState], if one is not explicitly * passed to [startTransitionTo]. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index 23b80b05a785..ed4dd6a15c18 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -19,10 +19,10 @@ package com.android.systemui.keyguard.ui.binder import android.os.Trace import android.util.Log +import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel import com.android.systemui.lifecycle.repeatWhenAttached import kotlinx.coroutines.launch @@ -31,19 +31,19 @@ class KeyguardBlueprintViewBinder { companion object { private const val TAG = "KeyguardBlueprintViewBinder" - fun bind(keyguardRootView: KeyguardRootView, viewModel: KeyguardBlueprintViewModel) { - keyguardRootView.repeatWhenAttached { + fun bind(constraintLayout: ConstraintLayout, viewModel: KeyguardBlueprintViewModel) { + constraintLayout.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { launch { viewModel.blueprint.collect { blueprint -> Trace.beginSection("KeyguardBlueprintController#applyBlueprint") Log.d(TAG, "applying blueprint: $blueprint") ConstraintSet().apply { - clone(keyguardRootView) + clone(constraintLayout) val emptyLayout = ConstraintSet.Layout() knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) } blueprint?.apply(this) - applyTo(keyguardRootView) + applyTo(constraintLayout) } Trace.endSection() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index dd3da97d398f..41c1c9600561 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -49,6 +49,7 @@ import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder +import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel @@ -56,25 +57,31 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel +import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel import com.android.systemui.monet.ColorScheme import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.FalsingManager import com.android.systemui.shared.clocks.ClockRegistry import com.android.systemui.shared.clocks.DefaultClockController import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants +import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants import com.android.systemui.statusbar.KeyguardIndicationController import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.statusbar.phone.KeyguardBottomAreaView +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking /** Renders the preview of the lock screen. */ class KeyguardPreviewRenderer +@OptIn(ExperimentalCoroutinesApi::class) @AssistedInject constructor( @Application private val context: Context, @@ -99,6 +106,9 @@ constructor( @Assisted bundle: Bundle, private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel, private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor, + private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel, + private val chipbarCoordinator: ChipbarCoordinator, + private val keyguardStateController: KeyguardStateController, ) { val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN) @@ -130,19 +140,21 @@ constructor( init { if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { - keyguardRootViewModel.enablePreviewMode( + keyguardRootViewModel.enablePreviewMode() + quickAffordancesCombinedViewModel.enablePreviewMode( initiallySelectedSlotId = - bundle.getString( - KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID, - ), + bundle.getString( + KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID, + ) + ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance, ) } else { bottomAreaViewModel.enablePreviewMode( initiallySelectedSlotId = - bundle.getString( - KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID, - ), + bundle.getString( + KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID, + ), shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance, ) } @@ -163,17 +175,8 @@ constructor( val rootView = FrameLayout(context) if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { - val keyguardRootView = KeyguardRootView(context, null) - rootView.addView( - keyguardRootView, - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT, - ), - ) - setupShortcuts(keyguardRootView) - KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) - keyguardBlueprintInteractor.refreshBlueprint() + setupKeyguardRootView(rootView) + setupShortcuts(rootView) } else { setUpBottomArea(rootView) } @@ -314,7 +317,7 @@ constructor( false, ) as KeyguardBottomAreaView bottomAreaView.init( - viewModel = bottomAreaViewModel, + viewModel = bottomAreaViewModel, ) parentView.addView( bottomAreaView, @@ -325,10 +328,34 @@ constructor( ) } - private fun setupShortcuts(keyguardRootView: KeyguardRootView) { + @OptIn(ExperimentalCoroutinesApi::class) + private fun setupKeyguardRootView(rootView: FrameLayout) { + val keyguardRootView = KeyguardRootView(context, null) + disposables.add( + KeyguardRootViewBinder.bind( + keyguardRootView, + keyguardRootViewModel, + featureFlags, + occludingAppDeviceEntryMessageViewModel, + chipbarCoordinator, + keyguardStateController, + ) + ) + rootView.addView( + keyguardRootView, + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ), + ) + KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) + keyguardBlueprintInteractor.refreshBlueprint() + } + + private fun setupShortcuts(rootView: FrameLayout) { shortcutsBindings.add( KeyguardQuickAffordanceViewBinder.bind( - keyguardRootView.requireViewById(R.id.start_button), + rootView.requireViewById(R.id.start_button), quickAffordancesCombinedViewModel.startButton, keyguardRootViewModel.alpha, falsingManager, @@ -340,7 +367,7 @@ constructor( shortcutsBindings.add( KeyguardQuickAffordanceViewBinder.bind( - keyguardRootView.requireViewById(R.id.end_button), + rootView.requireViewById(R.id.end_button), quickAffordancesCombinedViewModel.endButton, keyguardRootViewModel.alpha, falsingManager, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt index fefe6792036f..07f316b487bf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints +import com.android.systemui.communal.ui.view.layout.blueprints.DefaultCommunalBlueprint import com.android.systemui.keyguard.data.repository.KeyguardBlueprint import dagger.Binds import dagger.Module @@ -35,4 +36,10 @@ abstract class KeyguardBlueprintModule { abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint( shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint ): KeyguardBlueprint + + @Binds + @IntoSet + abstract fun bindDefaultCommunalBlueprint( + defaultCommunalBlueprint: DefaultCommunalBlueprint + ): KeyguardBlueprint } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index 56a98455d8b3..02ea5508f34f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -18,22 +18,20 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.annotation.VisibleForTesting -import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import javax.inject.Inject @OptIn(ExperimentalCoroutinesApi::class) class KeyguardQuickAffordancesCombinedViewModel @@ -43,6 +41,18 @@ constructor( private val keyguardInteractor: KeyguardInteractor, ) { + data class PreviewMode( + val isInPreviewMode: Boolean = false, + val shouldHighlightSelectedAffordance: Boolean = false, + ) + + /** + * Whether this view-model instance is powering the preview experience that renders exclusively + * in the wallpaper picker application. This should _always_ be `false` for the real lock screen + * experience. + */ + private val previewMode = MutableStateFlow(PreviewMode()) + /** * ID of the slot that's currently selected in the preview that renders exclusively in the * wallpaper picker application. This is ignored for the actual, real lock screen experience. @@ -85,39 +95,63 @@ constructor( selectedPreviewSlotId.value = slotId } + /** + * Puts this view-model in "preview mode", which means it's being used for UI that is rendering + * the lock screen preview in wallpaper picker / settings and not the real experience on the + * lock screen. + * + * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one. + * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be + * highlighted (while all others are dimmed to make the selected one stand out). + */ + fun enablePreviewMode( + initiallySelectedSlotId: String?, + shouldHighlightSelectedAffordance: Boolean, + ) { + val newPreviewMode = + PreviewMode( + isInPreviewMode = true, + shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance, + ) + onPreviewSlotSelected( + initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + ) + previewMode.value = newPreviewMode + } + private fun button( position: KeyguardQuickAffordancePosition ): Flow<KeyguardQuickAffordanceViewModel> { - return keyguardInteractor.previewMode.flatMapLatest { previewMode -> + return previewMode.flatMapLatest { previewMode -> combine( - if (previewMode.isInPreviewMode) { - quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position) - } else { - quickAffordanceInteractor.quickAffordance(position = position) - }, - keyguardInteractor.animateDozingTransitions.distinctUntilChanged(), - areQuickAffordancesFullyOpaque, - selectedPreviewSlotId, - quickAffordanceInteractor.useLongPress(), - ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId, useLongPress -> - val slotId = position.toSlotId() - val isSelected = selectedPreviewSlotId == slotId - model.toViewModel( - animateReveal = !previewMode.isInPreviewMode && animateReveal, - isClickable = isFullyOpaque && !previewMode.isInPreviewMode, - isSelected = - previewMode.isInPreviewMode && - previewMode.shouldHighlightSelectedAffordance && - isSelected, - isDimmed = - previewMode.isInPreviewMode && - previewMode.shouldHighlightSelectedAffordance && - !isSelected, - forceInactive = previewMode.isInPreviewMode, - slotId = slotId, - useLongPress = useLongPress, - ) - } + if (previewMode.isInPreviewMode) { + quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position) + } else { + quickAffordanceInteractor.quickAffordance(position = position) + }, + keyguardInteractor.animateDozingTransitions.distinctUntilChanged(), + areQuickAffordancesFullyOpaque, + selectedPreviewSlotId, + quickAffordanceInteractor.useLongPress(), + ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId, useLongPress -> + val slotId = position.toSlotId() + val isSelected = selectedPreviewSlotId == slotId + model.toViewModel( + animateReveal = !previewMode.isInPreviewMode && animateReveal, + isClickable = isFullyOpaque && !previewMode.isInPreviewMode, + isSelected = + previewMode.isInPreviewMode && + previewMode.shouldHighlightSelectedAffordance && + isSelected, + isDimmed = + previewMode.isInPreviewMode && + previewMode.shouldHighlightSelectedAffordance && + !isSelected, + forceInactive = previewMode.isInPreviewMode, + slotId = slotId, + useLongPress = useLongPress, + ) + } .distinctUntilChanged() } } @@ -167,8 +201,6 @@ constructor( // time, we don't want the number to be too close to 1.0 such that there is a chance that we // never treat the affordance UI as "fully opaque" as that would risk making it forever not // clickable. - @VisibleForTesting - const val AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD = 0.95f + @VisibleForTesting const val AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD = 0.95f } - -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 316ca771bf8f..92b9ee4cb134 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -19,29 +19,37 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState -import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import javax.inject.Inject @OptIn(ExperimentalCoroutinesApi::class) class KeyguardRootViewModel @Inject constructor( private val keyguardInteractor: KeyguardInteractor, - private val keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel -) -{ +) { + + data class PreviewMode(val isInPreviewMode: Boolean = false) + + /** + * Whether this view-model instance is powering the preview experience that renders exclusively + * in the wallpaper picker application. This should _always_ be `false` for the real lock screen + * experience. + */ + private val previewMode = MutableStateFlow(PreviewMode()) + /** Represents the current state of the KeyguardRootView visibility */ val keyguardRootViewVisibilityState: Flow<KeyguardRootViewVisibilityState> = keyguardInteractor.keyguardRootViewVisibilityState /** An observable for the alpha level for the entire keyguard root view. */ val alpha: Flow<Float> = - keyguardInteractor.previewMode.flatMapLatest { + previewMode.flatMapLatest { if (it.isInPreviewMode) { flowOf(1f) } else { @@ -53,23 +61,9 @@ constructor( * Puts this view-model in "preview mode", which means it's being used for UI that is rendering * the lock screen preview in wallpaper picker / settings and not the real experience on the * lock screen. - * - * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one. - * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be - * highlighted (while all others are dimmed to make the selected one stand out). */ - fun enablePreviewMode( - initiallySelectedSlotId: String?, - shouldHighlightSelectedAffordance: Boolean, - ) { - keyguardInteractor.previewMode.value = - KeyguardInteractor.PreviewMode( - isInPreviewMode = true, - shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance, - ) - keyguardQuickAffordancesCombinedViewModel.onPreviewSlotSelected( - initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START - ) + fun enablePreviewMode() { + val newPreviewMode = PreviewMode(true) + previewMode.value = newPreviewMode } - -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt new file mode 100644 index 000000000000..afb18f1f23c4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt @@ -0,0 +1,22 @@ +/* + * 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.log.dagger + +import javax.inject.Qualifier + +/** A [com.android.systemui.log.LogBuffer] for communal-related logging. */ +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class CommunalLog diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index b6577f747f03..5127d142d783 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -497,6 +497,16 @@ public class LogModule { return factory.create("DreamLog", 250); } + /** + * Provides a {@link LogBuffer} for communal-related logs. + */ + @Provides + @SysUISingleton + @CommunalLog + public static LogBuffer provideCommunalLogBuffer(LogBufferFactory factory) { + return factory.create("CommunalLog", 250); + } + /** Provides a {@link LogBuffer} for display metrics related logs. */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index f8f784ff948c..3d4fca1b8945 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -216,6 +216,58 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener } } + private void stopSound(Command cmd) { + final MediaPlayer mp; + synchronized (mPlayerLock) { + mp = mPlayer; + mPlayer = null; + } + if (mp == null) { + Log.w(mTag, "STOP command without a player"); + return; + } + + long delay = SystemClock.uptimeMillis() - cmd.requestTime; + if (delay > 1000) { + Log.w(mTag, "Notification stop delayed by " + delay + "msecs"); + } + try { + mp.stop(); + } catch (Exception e) { + Log.w(mTag, "Failed to stop MediaPlayer", e); + } + if (DEBUG) { + Log.i(mTag, "About to release MediaPlayer piid:" + + mp.getPlayerIId() + " due to notif cancelled"); + } + try { + mp.release(); + } catch (Exception e) { + Log.w(mTag, "Failed to release MediaPlayer", e); + } + synchronized (mQueueAudioFocusLock) { + if (mAudioManagerWithAudioFocus != null) { + if (DEBUG) { + Log.d(mTag, "in STOP: abandonning AudioFocus"); + } + try { + mAudioManagerWithAudioFocus.abandonAudioFocus(null); + } catch (Exception e) { + Log.w(mTag, "Failed to abandon audio focus", e); + } + mAudioManagerWithAudioFocus = null; + } + } + synchronized (mCompletionHandlingLock) { + if ((mLooper != null) && (mLooper.getThread().getState() != Thread.State.TERMINATED)) { + if (DEBUG) { + Log.d(mTag, "in STOP: quitting looper " + mLooper); + } + mLooper.quit(); + } + } + } + private final class CmdThread extends java.lang.Thread { CmdThread() { super("NotificationPlayer-" + mTag); @@ -229,62 +281,28 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener if (DEBUG) Log.d(mTag, "RemoveFirst"); cmd = mCmdQueue.removeFirst(); } - - switch (cmd.code) { - case PLAY: - if (DEBUG) Log.d(mTag, "PLAY"); - startSound(cmd); - break; - case STOP: - if (DEBUG) Log.d(mTag, "STOP"); - final MediaPlayer mp; - synchronized (mPlayerLock) { - mp = mPlayer; - mPlayer = null; + try { + switch (cmd.code) { + case PLAY: + if (DEBUG) Log.d(mTag, "PLAY"); + startSound(cmd); + break; + case STOP: + if (DEBUG) Log.d(mTag, "STOP"); + stopSound(cmd); + break; } - if (mp != null) { - long delay = SystemClock.uptimeMillis() - cmd.requestTime; - if (delay > 1000) { - Log.w(mTag, "Notification stop delayed by " + delay + "msecs"); + } finally { + synchronized (mCmdQueue) { + if (mCmdQueue.size() == 0) { + // nothing left to do, quit + // doing this check after we're done prevents the case where they + // added it during the operation from spawning two threads and + // trying to do them in parallel. + mThread = null; + releaseWakeLock(); + return; } - try { - mp.stop(); - } catch (Exception e) { } - if (DEBUG) { - Log.i(mTag, "About to release MediaPlayer piid:" - + mp.getPlayerIId() + " due to notif cancelled"); - } - mp.release(); - synchronized(mQueueAudioFocusLock) { - if (mAudioManagerWithAudioFocus != null) { - if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); } - mAudioManagerWithAudioFocus.abandonAudioFocus(null); - mAudioManagerWithAudioFocus = null; - } - } - synchronized (mCompletionHandlingLock) { - if ((mLooper != null) && - (mLooper.getThread().getState() != Thread.State.TERMINATED)) - { - if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); } - mLooper.quit(); - } - } - } else { - Log.w(mTag, "STOP command without a player"); - } - break; - } - - synchronized (mCmdQueue) { - if (mCmdQueue.size() == 0) { - // nothing left to do, quit - // doing this check after we're done prevents the case where they - // added it during the operation from spawning two threads and - // trying to do them in parallel. - mThread = null; - releaseWakeLock(); - return; } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt new file mode 100644 index 000000000000..99243696a4e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt @@ -0,0 +1,30 @@ +/* + * 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.media.controls.util + +import android.content.Context +import android.media.session.MediaSessionLegacyHelper +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +/** Injectable wrapper around `MediaSessionLegacyHelper` functions */ +@SysUISingleton +class MediaSessionLegacyHelperWrapper @Inject constructor() { + fun getHelper(context: Context): MediaSessionLegacyHelper { + return MediaSessionLegacyHelper.getHelper(context) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index a3d1d8c6959d..d88249830739 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -515,11 +515,13 @@ public abstract class MediaOutputBaseAdapter extends mSeekBar.setOnTouchListener((v, event) -> false); updateIconAreaClickListener((v) -> { if (device.getCurrentVolume() == 0) { + mController.logInteractionUnmuteDevice(device); mSeekBar.setVolume(UNMUTE_DEFAULT_VOLUME); mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME); updateUnmutedVolumeIcon(); mIconAreaLayout.setOnTouchListener(((iconV, event) -> false)); } else { + mController.logInteractionMuteDevice(device); mSeekBar.resetVolume(); mController.adjustVolume(device, 0); updateMutedVolumeIcon(); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index d281f50a292c..bb0e9d182579 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -837,6 +837,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, mMetricLogger.logInteractionAdjustVolume(device); } + void logInteractionMuteDevice(MediaDevice device) { + mMetricLogger.logInteractionMute(device); + } + + void logInteractionUnmuteDevice(MediaDevice device) { + mMetricLogger.logInteractionUnmute(device); + } + String getPackageName() { return mPackageName; } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java index 39d4e6e8d68a..ffd626abbfe8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java @@ -56,6 +56,7 @@ public class MediaOutputMetricLogger { * Update the endpoints of a content switching operation. * This method should be called before a switching operation, so the metric logger can track * source and target devices. + * * @param source the current connected media device * @param target the target media device for content switching to */ @@ -72,37 +73,9 @@ public class MediaOutputMetricLogger { /** * Do the metric logging of content switching success. + * * @param selectedDeviceType string representation of the target media device - * @param deviceList media device list for device count updating - */ - public void logOutputSuccess(String selectedDeviceType, List<MediaDevice> deviceList) { - if (DEBUG) { - Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType); - } - - if (mSourceDevice == null && mTargetDevice == null) { - return; - } - - updateLoggingDeviceCount(deviceList); - - SysUiStatsLog.write( - SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, - getLoggingDeviceType(mSourceDevice, true), - getLoggingDeviceType(mTargetDevice, false), - SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK, - SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR, - getLoggingPackageName(), - mWiredDeviceCount, - mConnectedBluetoothDeviceCount, - mRemoteDeviceCount, - mAppliedDeviceCountWithinRemoteGroup); - } - - /** - * Do the metric logging of content switching success. - * @param selectedDeviceType string representation of the target media device - * @param deviceItemList media item list for device count updating + * @param deviceItemList media item list for device count updating */ public void logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList) { if (DEBUG) { @@ -125,11 +98,14 @@ public class MediaOutputMetricLogger { mWiredDeviceCount, mConnectedBluetoothDeviceCount, mRemoteDeviceCount, - mAppliedDeviceCountWithinRemoteGroup); + mAppliedDeviceCountWithinRemoteGroup, + mTargetDevice.isSuggestedDevice(), + mTargetDevice.hasOngoingSession()); } /** * Do the metric logging of volume adjustment. + * * @param source the device been adjusted */ public void logInteractionAdjustVolume(MediaDevice source) { @@ -141,7 +117,8 @@ public class MediaOutputMetricLogger { SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME, getInteractionDeviceType(source), - getLoggingPackageName()); + getLoggingPackageName(), + source.isSuggestedDevice()); } /** @@ -156,7 +133,8 @@ public class MediaOutputMetricLogger { SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE, - getLoggingPackageName()); + getLoggingPackageName(), + /*isSuggestedDevice = */false); } /** @@ -171,42 +149,47 @@ public class MediaOutputMetricLogger { SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION, getInteractionDeviceType(source), - getLoggingPackageName()); + getLoggingPackageName(), + source.isSuggestedDevice()); } /** - * Do the metric logging of content switching failure. - * @param deviceList media device list for device count updating - * @param reason the reason of content switching failure + * Do the metric logging of muting device. */ - public void logOutputFailure(List<MediaDevice> deviceList, int reason) { + public void logInteractionMute(MediaDevice source) { if (DEBUG) { - Log.e(TAG, "logRequestFailed - " + reason); + Log.d(TAG, "logInteraction - Mute"); } - if (mSourceDevice == null && mTargetDevice == null) { - return; - } + SysUiStatsLog.write( + SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, + SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__MUTE, + getInteractionDeviceType(source), + getLoggingPackageName(), + source.isSuggestedDevice()); + } - updateLoggingDeviceCount(deviceList); + /** + * Do the metric logging of unmuting device. + */ + public void logInteractionUnmute(MediaDevice source) { + if (DEBUG) { + Log.d(TAG, "logInteraction - Unmute"); + } SysUiStatsLog.write( - SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, - getLoggingDeviceType(mSourceDevice, true), - getLoggingDeviceType(mTargetDevice, false), - SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR, - getLoggingSwitchOpSubResult(reason), + SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, + SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__UNMUTE, + getInteractionDeviceType(source), getLoggingPackageName(), - mWiredDeviceCount, - mConnectedBluetoothDeviceCount, - mRemoteDeviceCount, - mAppliedDeviceCountWithinRemoteGroup); + source.isSuggestedDevice()); } /** * Do the metric logging of content switching failure. + * * @param deviceItemList media item list for device count updating - * @param reason the reason of content switching failure + * @param reason the reason of content switching failure */ public void logOutputItemFailure(List<MediaItem> deviceItemList, int reason) { if (DEBUG) { @@ -229,7 +212,9 @@ public class MediaOutputMetricLogger { mWiredDeviceCount, mConnectedBluetoothDeviceCount, mRemoteDeviceCount, - mAppliedDeviceCountWithinRemoteGroup); + mAppliedDeviceCountWithinRemoteGroup, + mTargetDevice.isSuggestedDevice(), + mTargetDevice.hasOngoingSession()); } private void updateLoggingDeviceCount(List<MediaDevice> deviceList) { @@ -266,7 +251,7 @@ public class MediaOutputMetricLogger { mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; mAppliedDeviceCountWithinRemoteGroup = 0; - for (MediaItem mediaItem: deviceItemList) { + for (MediaItem mediaItem : deviceItemList) { if (mediaItem.getMediaDevice().isPresent() && mediaItem.getMediaDevice().get().isConnected()) { switch (mediaItem.getMediaDevice().get().getDeviceType()) { @@ -326,6 +311,10 @@ public class MediaOutputMetricLogger { return isSourceDevice ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP; + case MediaDevice.MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER: + return isSourceDevice + ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__AVR + : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__AVR; default: return isSourceDevice ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index e134f7c10b9b..ae0ab8423a99 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -25,6 +25,7 @@ import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR; import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; @@ -1714,10 +1715,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) { final InsetsFrameProvider navBarProvider = - new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars()) - .setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] { - new InsetsFrameProvider.InsetsSizeOverride( - TYPE_INPUT_METHOD, null)}); + new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars()); + if (!ENABLE_HIDE_IME_CAPTION_BAR) { + navBarProvider.setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] { + new InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, null) + }); + } if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) { navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight)); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt index 67927a4153b3..cb87e3cc142a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt @@ -22,13 +22,12 @@ import androidx.annotation.GuardedBy import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTileView import com.android.systemui.qs.external.TileServiceRequestController import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository import com.android.systemui.qs.pipeline.shared.TileSpec import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -37,10 +36,11 @@ import kotlinx.coroutines.launch /** * Adapter to determine what real class to use for classes that depend on [QSHost]. - * * When [Flags.QS_PIPELINE_NEW_HOST] is off, all calls will be routed to [QSTileHost]. - * * When [Flags.QS_PIPELINE_NEW_HOST] is on, calls regarding the current set of tiles will be - * routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will still be routed to + * * When [QSPipelineFlagsRepository.pipelineHostEnabled] is false, all calls will be routed to * [QSTileHost]. + * * When [QSPipelineFlagsRepository.pipelineHostEnabled] is true, calls regarding the current set + * of tiles will be routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will + * still be routed to [QSTileHost]. * * This routing also includes dumps. */ @@ -53,7 +53,7 @@ constructor( private val context: Context, private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder, @Application private val scope: CoroutineScope, - private val featureFlags: FeatureFlags, + flags: QSPipelineFlagsRepository, dumpManager: DumpManager, ) : QSHost { @@ -61,7 +61,7 @@ constructor( private const val TAG = "QSTileHost" } - private val useNewHost = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST) + private val useNewHost = flags.pipelineHostEnabled @GuardedBy("callbacksMap") private val callbacksMap = mutableMapOf<QSHost.Callback, Job>() diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 432147f00186..e57db562b27a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -35,8 +35,6 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.nano.SystemUIProtoDump; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.qs.QSFactory; @@ -50,6 +48,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.nano.QsTileState; import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository; import com.android.systemui.settings.UserFileManager; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeController; @@ -119,7 +118,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P private TileLifecycleManager.Factory mTileLifeCycleManagerFactory; - private final FeatureFlags mFeatureFlags; + private final QSPipelineFlagsRepository mFeatureFlags; @Inject public QSTileHost(Context context, @@ -135,7 +134,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P CustomTileStatePersister customTileStatePersister, TileLifecycleManager.Factory tileLifecycleManagerFactory, UserFileManager userFileManager, - FeatureFlags featureFlags + QSPipelineFlagsRepository featureFlags ) { mContext = context; mUserContext = context; @@ -162,7 +161,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P // finishes before creating any tiles. tunerService.addTunable(this, TILES_SETTING); // AutoTileManager can modify mTiles so make sure mTiles has already been initialized. - if (!mFeatureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) { + if (!mFeatureFlags.getPipelineAutoAddEnabled()) { mAutoTiles = autoTiles.get(); } }); @@ -283,7 +282,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P } } // Do not process tiles if the flag is enabled. - if (mFeatureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { + if (mFeatureFlags.getPipelineHostEnabled()) { return; } if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt index 1f63f5da2f2b..b2111d765a9d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt @@ -16,8 +16,6 @@ package com.android.systemui.qs.dagger -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.qs.QSHost import com.android.systemui.qs.QSHostAdapter import com.android.systemui.qs.QSTileHost @@ -27,6 +25,7 @@ import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepositor import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository import dagger.Binds import dagger.Module import dagger.Provides @@ -45,11 +44,11 @@ interface QSHostModule { @Provides @JvmStatic fun providePanelInteractor( - featureFlags: FeatureFlags, + featureFlags: QSPipelineFlagsRepository, qsHost: QSTileHost, panelInteractorImpl: PanelInteractorImpl ): PanelInteractor { - return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { + return if (featureFlags.pipelineHostEnabled) { panelInteractorImpl } else { qsHost @@ -59,11 +58,11 @@ interface QSHostModule { @Provides @JvmStatic fun provideCustomTileAddedRepository( - featureFlags: FeatureFlags, + featureFlags: QSPipelineFlagsRepository, qsHost: QSTileHost, customTileAddedRepository: CustomTileAddedSharedPrefsRepository ): CustomTileAddedRepository { - return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { + return if (featureFlags.pipelineHostEnabled) { customTileAddedRepository } else { qsHost diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index 966f37014bd2..5a5e47af73c9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -27,8 +27,6 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.nano.SystemUIProtoDump -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.plugins.qs.QSFactory import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.external.CustomTile @@ -39,6 +37,7 @@ import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepositor import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.domain.model.TileModel +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger import com.android.systemui.qs.toProto @@ -139,7 +138,7 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, @Application private val scope: CoroutineScope, private val logger: QSPipelineLogger, - featureFlags: FeatureFlags, + featureFlags: QSPipelineFlagsRepository, ) : CurrentTilesInteractor { private val _currentSpecsAndTiles: MutableStateFlow<List<TileModel>> = @@ -171,7 +170,7 @@ constructor( } init { - if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { + if (featureFlags.pipelineHostEnabled) { startTileCollection() } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt index 224fc1ae864f..0743ba0b121a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt @@ -18,10 +18,9 @@ package com.android.systemui.qs.pipeline.domain.startable import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository import javax.inject.Inject @SysUISingleton @@ -30,14 +29,11 @@ class QSPipelineCoreStartable constructor( private val currentTilesInteractor: CurrentTilesInteractor, private val autoAddInteractor: AutoAddInteractor, - private val featureFlags: FeatureFlags, + private val featureFlags: QSPipelineFlagsRepository, ) : CoreStartable { override fun start() { - if ( - featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST) && - featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD) - ) { + if (featureFlags.pipelineAutoAddEnabled) { autoAddInteractor.init(currentTilesInteractor) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt new file mode 100644 index 000000000000..551b0f4890a4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt @@ -0,0 +1,23 @@ +package com.android.systemui.qs.pipeline.shared + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import javax.inject.Inject + +/** Encapsulate the different QS pipeline flags and their dependencies */ +@SysUISingleton +class QSPipelineFlagsRepository +@Inject +constructor( + private val featureFlags: FeatureFlags, +) { + + /** @see Flags.QS_PIPELINE_NEW_HOST */ + val pipelineHostEnabled: Boolean + get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST) + + /** @see Flags.QS_PIPELINE_AUTO_ADD */ + val pipelineAutoAddEnabled: Boolean + get() = pipelineHostEnabled && featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD) +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 76d9b039e112..a7434c6ac797 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -21,16 +21,13 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.RemoteUserInput import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -109,10 +106,6 @@ constructor( /** Whether the scene container is visible. */ val isVisible: StateFlow<Boolean> = repository.isVisible - private val _remoteUserInput: MutableStateFlow<RemoteUserInput?> = MutableStateFlow(null) - /** A flow of motion events originating from outside of the scene framework. */ - val remoteUserInput: StateFlow<RemoteUserInput?> = _remoteUserInput.asStateFlow() - /** * Returns the keys of all scenes in the container. * @@ -160,11 +153,6 @@ constructor( repository.setTransitionState(transitionState) } - /** Handles a remote user input. */ - fun onRemoteUserInput(input: RemoteUserInput) { - _remoteUserInput.value = input - } - /** * Notifies that the UI has transitioned sufficiently to the given scene. * diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/RemoteUserInput.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/RemoteUserInput.kt deleted file mode 100644 index 680de590a3fc..000000000000 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/RemoteUserInput.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.android.systemui.scene.shared.model - -import android.view.MotionEvent - -/** A representation of user input that is used by the scene framework. */ -data class RemoteUserInput( - val x: Float, - val y: Float, - val action: RemoteUserInputAction, -) { - companion object { - fun translateMotionEvent(event: MotionEvent): RemoteUserInput { - return RemoteUserInput( - x = event.x, - y = event.y, - action = - when (event.actionMasked) { - MotionEvent.ACTION_DOWN -> RemoteUserInputAction.DOWN - MotionEvent.ACTION_MOVE -> RemoteUserInputAction.MOVE - MotionEvent.ACTION_UP -> RemoteUserInputAction.UP - MotionEvent.ACTION_CANCEL -> RemoteUserInputAction.CANCEL - else -> RemoteUserInputAction.UNKNOWN - } - ) - } - } -} - -enum class RemoteUserInputAction { - DOWN, - MOVE, - UP, - CANCEL, - UNKNOWN, -} diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt index 8601b3de2f7c..cdf50bab6b42 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt @@ -2,7 +2,6 @@ package com.android.systemui.scene.ui.view import android.content.Context import android.util.AttributeSet -import android.view.MotionEvent import android.view.View import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig @@ -39,14 +38,6 @@ class SceneWindowRootView( ) } - override fun onTouchEvent(event: MotionEvent?): Boolean { - return event?.let { - viewModel.onRemoteUserInput(event) - true - } - ?: false - } - override fun setVisibility(visibility: Int) { // Do nothing. We don't want external callers to invoke this. Instead, we drive our own // visibility from our view-binder. diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index 3e9bbe464e2c..5c16fb54e3a0 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -16,11 +16,9 @@ package com.android.systemui.scene.ui.viewmodel -import android.view.MotionEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.RemoteUserInput import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject @@ -34,9 +32,6 @@ class SceneContainerViewModel constructor( private val interactor: SceneInteractor, ) { - /** A flow of motion events originating from outside of the scene framework. */ - val remoteUserInput: StateFlow<RemoteUserInput?> = interactor.remoteUserInput - /** * Keys of all scenes in the container. * @@ -68,11 +63,6 @@ constructor( interactor.setTransitionState(transitionState) } - /** Handles a [MotionEvent] representing remote user input. */ - fun onRemoteUserInput(event: MotionEvent) { - interactor.onRemoteUserInput(RemoteUserInput.translateMotionEvent(event)) - } - companion object { private const val SCENE_TRANSITION_LOGGING_REASON = "user input" } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt index e8683fbea735..fb99775b6549 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt @@ -63,6 +63,7 @@ class ScreenRecordPermissionDialog( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setDialogTitle(R.string.screenrecord_permission_dialog_title) + setTitle(R.string.screenrecord_title) setStartButtonText(R.string.screenrecord_permission_dialog_continue) setStartButtonOnClickListener { v: View? -> onStartRecordingClicked?.run() diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt index 05a0416f8f64..ab2a8d9c6e95 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt @@ -82,11 +82,15 @@ object ActionIntentCreator { return editIntent .setDataAndType(uri, "image/png") + .putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_SCREENSHOT) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) } + + private const val EXTRA_EDIT_SOURCE = "edit_source" + private const val EDIT_SOURCE_SCREENSHOT = "screenshot" } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java index 0b4b7c691cfd..bdbc470a61f1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java @@ -43,6 +43,7 @@ import com.android.systemui.R; */ public class DraggableConstraintLayout extends ConstraintLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { + public static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe private static final float VELOCITY_DP_PER_MS = 1; private static final int MAXIMUM_DISMISS_DISTANCE_DP = 400; @@ -179,8 +180,13 @@ public class DraggableConstraintLayout extends ConstraintLayout Region r = new Region(); Rect rect = new Rect(); for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).getGlobalVisibleRect(rect); - r.op(rect, Region.Op.UNION); + View child = getChildAt(i); + if (child.getVisibility() == View.VISIBLE) { + child.getGlobalVisibleRect(rect); + rect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP), + (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP)); + r.op(rect, Region.Op.UNION); + } } inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); inoutInfo.touchableRegion.set(r); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 3903bb2815ef..03e1e15c5210 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -68,7 +68,6 @@ import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.ScrollCaptureResponse; -import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -123,7 +122,6 @@ public class ScreenshotView extends FrameLayout implements public static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400; private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100; private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f; - private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe private final Resources mResources; private final Interpolator mFastOutSlowIn; @@ -284,17 +282,22 @@ public class ScreenshotView extends FrameLayout implements Region swipeRegion = new Region(); final Rect tmpRect = new Rect(); + int swipePadding = (int) FloatingWindowUtil.dpToPx( + mDisplayMetrics, DraggableConstraintLayout.SWIPE_PADDING_DP * -1); mScreenshotPreview.getBoundsOnScreen(tmpRect); - tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP), - (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP)); + tmpRect.inset(swipePadding, swipePadding); swipeRegion.op(tmpRect, Region.Op.UNION); mActionsContainerBackground.getBoundsOnScreen(tmpRect); - tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP), - (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP)); + tmpRect.inset(swipePadding, swipePadding); swipeRegion.op(tmpRect, Region.Op.UNION); mDismissButton.getBoundsOnScreen(tmpRect); swipeRegion.op(tmpRect, Region.Op.UNION); + View messageContainer = findViewById(R.id.screenshot_message_container); + if (messageContainer != null) { + messageContainer.getBoundsOnScreen(tmpRect); + swipeRegion.op(tmpRect, Region.Op.UNION); + } View messageDismiss = findViewById(R.id.message_dismiss_button); if (messageDismiss != null) { messageDismiss.getBoundsOnScreen(tmpRect); @@ -378,16 +381,6 @@ public class ScreenshotView extends FrameLayout implements mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip)); mScrollChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_scroll_chip)); - int swipePaddingPx = (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, SWIPE_PADDING_DP); - TouchDelegate previewDelegate = new TouchDelegate( - new Rect(swipePaddingPx, swipePaddingPx, swipePaddingPx, swipePaddingPx), - mScreenshotPreview); - mScreenshotPreview.setTouchDelegate(previewDelegate); - TouchDelegate actionsDelegate = new TouchDelegate( - new Rect(swipePaddingPx, swipePaddingPx, swipePaddingPx, swipePaddingPx), - mActionsContainerBackground); - mActionsContainerBackground.setTouchDelegate(actionsDelegate); - setFocusable(true); mActionsContainer.setScrollX(0); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 132cd6115bc7..014093de62bd 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -985,6 +985,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Make sure the clock is in the correct position after the unlock animation // so that it's not in the wrong place when we show the keyguard again. positionClockAndNotifications(true /* forceClockUpdate */); + mScrimController.onUnlockAnimationFinished(); } private void unlockAnimationStarted( @@ -1243,6 +1244,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.init(); } mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled); + mKeyguardStatusViewController.getView().addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + int oldHeight = oldBottom - oldTop; + if (v.getHeight() != oldHeight) { + mNotificationStackScrollLayoutController.animateNextTopPaddingChange(); + } + }); updateClockAppearance(); @@ -3198,6 +3206,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } } + @Override + public void performHapticFeedback(int constant) { + mVibratorHelper.performHapticFeedback(mView, constant); + } + private class ShadeHeadsUpTrackerImpl implements ShadeHeadsUpTracker { @Override public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 832a25bfbc41..802ed8069a69 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -21,8 +21,6 @@ import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.app.StatusBarManager; -import android.media.AudioManager; -import android.media.session.MediaSessionLegacyHelper; import android.os.PowerManager; import android.util.Log; import android.view.GestureDetector; @@ -47,6 +45,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionState; @@ -101,6 +100,7 @@ public class NotificationShadeWindowViewController { private final NotificationInsetsController mNotificationInsetsController; private final boolean mIsTrackpadCommonEnabled; private final FeatureFlags mFeatureFlags; + private final KeyEventInteractor mKeyEventInteractor; private GestureDetector mPulsingWakeupGestureHandler; private GestureDetector mDreamingWakeupGestureHandler; private View mBrightnessMirror; @@ -164,7 +164,8 @@ public class NotificationShadeWindowViewController { FeatureFlags featureFlags, SystemClock clock, BouncerMessageInteractor bouncerMessageInteractor, - BouncerLogger bouncerLogger) { + BouncerLogger bouncerLogger, + KeyEventInteractor keyEventInteractor) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; @@ -190,6 +191,7 @@ public class NotificationShadeWindowViewController { mNotificationInsetsController = notificationInsetsController; mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON); mFeatureFlags = featureFlags; + mKeyEventInteractor = keyEventInteractor; // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); @@ -457,44 +459,17 @@ public class NotificationShadeWindowViewController { @Override public boolean interceptMediaKey(KeyEvent event) { - return mService.interceptMediaKey(event); + return mKeyEventInteractor.interceptMediaKey(event); } @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { - return mService.dispatchKeyEventPreIme(event); + return mKeyEventInteractor.dispatchKeyEventPreIme(event); } @Override public boolean dispatchKeyEvent(KeyEvent event) { - boolean down = event.getAction() == KeyEvent.ACTION_DOWN; - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_BACK: - if (!down) { - mBackActionInteractor.onBackRequested(); - } - return true; - case KeyEvent.KEYCODE_MENU: - if (!down) { - return mService.onMenuPressed(); - } - break; - case KeyEvent.KEYCODE_SPACE: - if (!down) { - return mService.onSpacePressed(); - } - break; - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_VOLUME_UP: - if (mStatusBarStateController.isDozing()) { - MediaSessionLegacyHelper.getHelper(mView.getContext()) - .sendVolumeKeyEvent( - event, AudioManager.USE_DEFAULT_STREAM_TYPE, true); - return true; - } - break; - } - return false; + return mKeyEventInteractor.dispatchKeyEvent(event); } }); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index bea12de5ab49..656411874de5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -263,9 +263,11 @@ constructor( resources.getDimensionPixelSize( R.dimen.shade_header_system_icons_padding_start ), - systemIcons.paddingTop, + resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_top), resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_end), - systemIcons.paddingBottom + resources.getDimensionPixelSize( + R.dimen.shade_header_system_icons_padding_bottom + ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt index d5b5c87ec781..182a676c9841 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt @@ -248,6 +248,16 @@ interface ShadeViewController { /** Starts tracking a shade expansion gesture that originated from the status bar. */ fun startTrackingExpansionFromStatusBar() + /** + * Performs haptic feedback from a view with a haptic feedback constant. + * + * The implementation of this method should use the [android.view.View.performHapticFeedback] + * method with the provided constant. + * + * @param[constant] One of [android.view.HapticFeedbackConstants] + */ + fun performHapticFeedback(constant: Int) + // ******* End Keyguard Section ********* /** Returns the ShadeHeadsUpTracker. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt index 287ac528385f..09b74b213ebf 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt @@ -86,6 +86,8 @@ class ShadeViewControllerEmptyImpl @Inject constructor() : ShadeViewController { return false } override fun startTrackingExpansionFromStatusBar() {} + override fun performHapticFeedback(constant: Int) {} + override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl() override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt index d3c19b75a71d..5042f1b279e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt @@ -31,56 +31,68 @@ class ActionClickLogger @Inject constructor( ) { fun logInitialClick( entry: NotificationEntry?, + index: Integer?, pendingIntent: PendingIntent ) { buffer.log(TAG, LogLevel.DEBUG, { str1 = entry?.key str2 = entry?.ranking?.channel?.id - str3 = pendingIntent.intent.toString() + str3 = pendingIntent.toString() + int1 = index?.toInt() ?: Int.MIN_VALUE }, { - "ACTION CLICK $str1 (channel=$str2) for pending intent $str3" + "ACTION CLICK $str1 (channel=$str2) for pending intent $str3 at index $int1" }) } fun logRemoteInputWasHandled( - entry: NotificationEntry? + entry: NotificationEntry?, + index: Int? ) { buffer.log(TAG, LogLevel.DEBUG, { str1 = entry?.key + int1 = index ?: Int.MIN_VALUE }, { - " [Action click] Triggered remote input (for $str1))" + " [Action click] Triggered remote input (for $str1) at index $int1" }) } fun logStartingIntentWithDefaultHandler( entry: NotificationEntry?, - pendingIntent: PendingIntent + pendingIntent: PendingIntent, + index: Int? ) { buffer.log(TAG, LogLevel.DEBUG, { str1 = entry?.key - str2 = pendingIntent.intent.toString() + str2 = pendingIntent.toString() + int1 = index ?: Int.MIN_VALUE }, { - " [Action click] Launching intent $str2 via default handler (for $str1)" + " [Action click] Launching intent $str2 via default handler (for $str1 at index $int1)" }) } fun logWaitingToCloseKeyguard( - pendingIntent: PendingIntent + pendingIntent: PendingIntent, + index: Int? ) { buffer.log(TAG, LogLevel.DEBUG, { - str1 = pendingIntent.intent.toString() + str1 = pendingIntent.toString() + int1 = index ?: Int.MIN_VALUE }, { - " [Action click] Intent $str1 launches an activity, dismissing keyguard first..." + " [Action click] Intent $str1 at index $int1 launches an activity, dismissing " + + "keyguard first..." }) } fun logKeyguardGone( - pendingIntent: PendingIntent + pendingIntent: PendingIntent, + index: Int? ) { buffer.log(TAG, LogLevel.DEBUG, { - str1 = pendingIntent.intent.toString() + str1 = pendingIntent.toString() + int1 = index ?: Int.MIN_VALUE }, { - " [Action click] Keyguard dismissed, calling default handler for intent $str1" + " [Action click] Keyguard dismissed, calling default handler for intent $str1 at " + + "index $int1" }) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt index abf81c5c4cb6..692a9977c364 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt @@ -2,10 +2,12 @@ package com.android.systemui.statusbar import android.app.Notification import android.os.RemoteException +import android.util.Log import com.android.internal.statusbar.IStatusBarService import com.android.internal.statusbar.NotificationVisibility import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.util.Assert import java.util.concurrent.Executor import javax.inject.Inject @@ -21,7 +23,8 @@ import javax.inject.Inject @SysUISingleton public class NotificationClickNotifier @Inject constructor( val barService: IStatusBarService, - @Main val mainExecutor: Executor + @Main val mainExecutor: Executor, + @UiBackground val backgroundExecutor: Executor ) { val listeners = mutableListOf<NotificationInteractionListener>() @@ -48,13 +51,14 @@ public class NotificationClickNotifier @Inject constructor( visibility: NotificationVisibility, generatedByAssistant: Boolean ) { - try { - barService.onNotificationActionClick( - key, actionIndex, action, visibility, generatedByAssistant) - } catch (e: RemoteException) { - // nothing + backgroundExecutor.execute { + try { + barService.onNotificationActionClick( + key, actionIndex, action, visibility, generatedByAssistant) + } catch (e: RemoteException) { + // nothing + } } - mainExecutor.execute { notifyListenersAboutInteraction(key) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index da84afef42c5..8089fd94f7db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -119,11 +119,14 @@ public class NotificationRemoteInputManager implements Dumpable { mPowerInteractor.wakeUpIfDozing( "NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE); + Integer actionIndex = (Integer) + view.getTag(com.android.internal.R.id.notification_action_index_tag); + final NotificationEntry entry = getNotificationForParent(view.getParent()); - mLogger.logInitialClick(entry, pendingIntent); + mLogger.logInitialClick(entry, actionIndex, pendingIntent); if (handleRemoteInput(view, pendingIntent)) { - mLogger.logRemoteInputWasHandled(entry); + mLogger.logRemoteInputWasHandled(entry, actionIndex); return true; } @@ -141,9 +144,9 @@ public class NotificationRemoteInputManager implements Dumpable { } Notification.Action action = getActionFromView(view, entry, pendingIntent); return mCallback.handleRemoteViewClick(view, pendingIntent, - action == null ? false : action.isAuthenticationRequired(), () -> { + action == null ? false : action.isAuthenticationRequired(), actionIndex, () -> { Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view); - mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent); + mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent, actionIndex); boolean started = RemoteViews.startPendingIntent(view, pendingIntent, options); if (started) releaseNotificationIfKeptForRemoteInputHistory(entry); return started; @@ -692,11 +695,13 @@ public class NotificationRemoteInputManager implements Dumpable { * @param view * @param pendingIntent * @param appRequestedAuth + * @param actionIndex * @param defaultHandler * @return true iff the click was handled */ boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, - boolean appRequestedAuth, ClickHandler defaultHandler); + boolean appRequestedAuth, @Nullable Integer actionIndex, + ClickHandler defaultHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index d6a14604ffb0..6dd24ea4062b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -48,8 +48,10 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardClockSwitch; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; @@ -420,6 +422,25 @@ public class StatusBarStateControllerImpl implements } } + /** Returns the id of the currently rendering clock */ + public String getClockId() { + if (mView == null) { + return KeyguardClockSwitch.MISSING_CLOCK_ID; + } + + View clockSwitch = mView.findViewById(R.id.keyguard_clock_container); + if (clockSwitch == null) { + Log.e(TAG, "Clock container was missing"); + return KeyguardClockSwitch.MISSING_CLOCK_ID; + } + if (!(clockSwitch instanceof KeyguardClockSwitch)) { + Log.e(TAG, "Clock container was incorrect type: " + clockSwitch); + return KeyguardClockSwitch.MISSING_CLOCK_ID; + } + + return ((KeyguardClockSwitch) clockSwitch).getClockId(); + } + private void beginInteractionJankMonitor() { final boolean shouldPost = (mIsDozing && mDozeAmount == 0) || (!mIsDozing && mDozeAmount == 1); @@ -429,6 +450,7 @@ public class StatusBarStateControllerImpl implements Choreographer.CALLBACK_ANIMATION, this::beginInteractionJankMonitor, null); } else { Configuration.Builder builder = Configuration.Builder.withView(getCujType(), mView) + .setTag(getClockId()) .setDeferMonitorForAnimationStart(false); mInteractionJankMonitor.begin(builder); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS index ed80f33be701..5558ab1e2af2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS @@ -2,13 +2,17 @@ set noparent # Bug component: 78010 -aaliomer@google.com +aioana@google.com aroederer@google.com +iyz@google.com jeffdq@google.com juliacr@google.com juliatuttle@google.com +kurucz@google.com +liuyining@google.com lynhan@google.com -steell@google.com +matiashe@google.com +valiiftime@google.com yurilin@google.com per-file MediaNotificationProcessor.java = ethibodeau@google.com, asc@google.com diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index 5c2f9a8d28ec..62a0d138fd05 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -39,10 +39,7 @@ class StackCoordinator @Inject internal constructor( override fun attach(pipeline: NotifPipeline) { pipeline.addOnAfterRenderListListener(::onAfterRenderList) - // TODO(b/282865576): This has an issue where it makes changes to some groups without - // notifying listeners. To be fixed in QPR, but for now let's comment it out to avoid the - // group expansion bug. - // groupExpansionManagerImpl.attach(pipeline) + groupExpansionManagerImpl.attach(pipeline) } fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java index 46af03a438f5..5d33804ab6a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java @@ -67,18 +67,29 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl * Cleanup entries from mExpandedGroups that no longer exist in the pipeline. */ private final OnBeforeRenderListListener mNotifTracker = (entries) -> { + if (mExpandedGroups.isEmpty()) { + return; // nothing to do + } + final Set<NotificationEntry> renderingSummaries = new HashSet<>(); for (ListEntry entry : entries) { if (entry instanceof GroupEntry) { renderingSummaries.add(entry.getRepresentativeEntry()); } } - mExpandedGroups.removeIf(expandedGroup -> !renderingSummaries.contains(expandedGroup)); + + // Create a copy of mExpandedGroups so we can modify it in a thread-safe way. + final var currentExpandedGroups = new HashSet<>(mExpandedGroups); + for (NotificationEntry entry : currentExpandedGroups) { + setExpanded(entry, renderingSummaries.contains(entry)); + } }; public void attach(NotifPipeline pipeline) { - mDumpManager.registerDumpable(this); - pipeline.addOnBeforeRenderListListener(mNotifTracker); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) { + mDumpManager.registerDumpable(this); + pipeline.addOnBeforeRenderListListener(mNotifTracker); + } } @Override @@ -94,11 +105,24 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl @Override public void setGroupExpanded(NotificationEntry entry, boolean expanded) { final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry); + setExpanded(groupSummary, expanded); + } + + /** + * Add or remove {@code entry} to/from {@code mExpandedGroups} and notify listeners if + * something changed. This assumes that {@code entry} is a group summary. + * <p> + * TODO(b/293434635): Currently, in spite of its docs, + * {@code mGroupMembershipManager.getGroupSummary(entry)} returns null if {@code entry} is + * already a summary. Instead of needing this helper method to bypass that, we probably want to + * move this code back to {@code setGroupExpanded} and use that everywhere. + */ + private void setExpanded(NotificationEntry entry, boolean expanded) { boolean changed; if (expanded) { - changed = mExpandedGroups.add(groupSummary); + changed = mExpandedGroups.add(entry); } else { - changed = mExpandedGroups.remove(groupSummary); + changed = mExpandedGroups.remove(entry); } // Only notify listeners if something changed. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt new file mode 100644 index 000000000000..26dfe3edf793 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt @@ -0,0 +1,684 @@ +/* + * 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.statusbar.notification.icon.ui.viewbinder + +import android.content.Context +import android.graphics.Color +import android.graphics.Rect +import android.os.Bundle +import android.os.Trace +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.annotation.ColorInt +import androidx.annotation.VisibleForTesting +import androidx.collection.ArrayMap +import com.android.app.animation.Interpolators +import com.android.internal.statusbar.StatusBarIcon +import com.android.internal.util.ContrastColorUtil +import com.android.settingslib.Utils +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.demomode.DemoMode +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.flags.ViewRefactorFlag +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.CrossFadeHelper +import com.android.systemui.statusbar.NotificationListener +import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.NotificationShelfController +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.NotificationUtils +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel +import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.NotificationIconAreaController +import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.wm.shell.bubbles.Bubbles +import java.util.Optional +import java.util.function.Function +import javax.inject.Inject +import kotlinx.coroutines.DisposableHandle + +/** + * Controller class for [NotificationIconContainer]. This implementation serves as a temporary + * wrapper around [NotificationIconContainerViewBinder], so that external code can continue to + * depend on the [NotificationIconAreaController] interface. Once + * [LegacyNotificationIconAreaControllerImpl] is removed, this class can go away and the ViewBinder + * can be used directly. + */ +@SysUISingleton +class NotificationIconAreaControllerViewBinderWrapperImpl +@Inject +constructor( + private val context: Context, + private val statusBarStateController: StatusBarStateController, + private val wakeUpCoordinator: NotificationWakeUpCoordinator, + private val bypassController: KeyguardBypassController, + private val mediaManager: NotificationMediaManager, + notificationListener: NotificationListener, + private val dozeParameters: DozeParameters, + private val sectionStyleProvider: SectionStyleProvider, + private val bubblesOptional: Optional<Bubbles>, + demoModeController: DemoModeController, + darkIconDispatcher: DarkIconDispatcher, + featureFlags: FeatureFlags, + private val statusBarWindowController: StatusBarWindowController, + private val screenOffAnimationController: ScreenOffAnimationController, + private val shelfIconsViewModel: NotificationIconContainerShelfViewModel, + private val statusBarIconsViewModel: NotificationIconContainerStatusBarViewModel, + private val aodIconsViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, +) : + NotificationIconAreaController, + DarkIconDispatcher.DarkReceiver, + StatusBarStateController.StateListener, + NotificationWakeUpCoordinator.WakeUpListener, + DemoMode { + + private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context) + private val updateStatusBarIcons = Runnable { updateStatusBarIcons() } + private val shelfRefactor = ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR) + private val tintAreas = ArrayList<Rect>() + + private var iconSize = 0 + private var iconHPadding = 0 + private var iconTint = Color.WHITE + private var notificationEntries = listOf<ListEntry>() + private var notificationIconArea: View? = null + private var notificationIcons: NotificationIconContainer? = null + private var shelfIcons: NotificationIconContainer? = null + private var aodIcons: NotificationIconContainer? = null + private var aodBindJob: DisposableHandle? = null + private var aodIconAppearTranslation = 0 + private var animationsEnabled = false + private var aodIconTint = 0 + private var aodIconsVisible = false + private var showLowPriority = true + + @VisibleForTesting + val settingsListener: NotificationListener.NotificationSettingsListener = + object : NotificationListener.NotificationSettingsListener { + override fun onStatusBarIconsBehaviorChanged(hideSilentStatusIcons: Boolean) { + showLowPriority = !hideSilentStatusIcons + updateStatusBarIcons() + } + } + + init { + statusBarStateController.addCallback(this) + wakeUpCoordinator.addListener(this) + demoModeController.addCallback(this) + notificationListener.addNotificationSettingsListener(settingsListener) + initializeNotificationAreaViews(context) + reloadAodColor() + darkIconDispatcher.addDarkReceiver(this) + } + + @VisibleForTesting + fun shouldShowLowPriorityIcons(): Boolean { + return showLowPriority + } + + /** Called by the Keyguard*ViewController whose view contains the aod icons. */ + override fun setupAodIcons(aodIcons: NotificationIconContainer) { + val changed = this.aodIcons != null && aodIcons !== this.aodIcons + if (changed) { + this.aodIcons!!.setAnimationsEnabled(false) + this.aodIcons!!.removeAllViews() + aodBindJob?.dispose() + } + this.aodIcons = aodIcons + this.aodIcons!!.setOnLockScreen(true) + aodBindJob = NotificationIconContainerViewBinder.bind(aodIcons, aodIconsViewModel) + updateAodIconsVisibility(animate = false, forceUpdate = changed) + updateAnimations() + if (changed) { + updateAodNotificationIcons() + } + updateIconLayoutParams(context) + } + + override fun setupShelf(notificationShelfController: NotificationShelfController) = + NotificationShelfViewBinderWrapperControllerImpl.unsupported + + override fun setShelfIcons(icons: NotificationIconContainer) { + if (shelfRefactor.expectEnabled()) { + NotificationIconContainerViewBinder.bind(icons, shelfIconsViewModel) + shelfIcons = icons + } + } + + override fun onDensityOrFontScaleChanged(context: Context) { + updateIconLayoutParams(context) + } + + /** Returns the view that represents the notification area. */ + override fun getNotificationInnerAreaView(): View? { + return notificationIconArea + } + + /** + * See [com.android.systemui.statusbar.policy.DarkIconDispatcher.setIconsDarkArea]. Sets the + * color that should be used to tint any icons in the notification area. + * + * @param tintAreas the areas in which to tint the icons, specified in screen coordinates + * @param darkIntensity + */ + override fun onDarkChanged(tintAreas: ArrayList<Rect>, darkIntensity: Float, iconTint: Int) { + this.tintAreas.clear() + this.tintAreas.addAll(tintAreas) + if (DarkIconDispatcher.isInAreas(tintAreas, notificationIconArea)) { + this.iconTint = iconTint + } + applyNotificationIconsTint() + } + + /** Updates the notifications with the given list of notifications to display. */ + override fun updateNotificationIcons(entries: List<ListEntry>) { + notificationEntries = entries + updateNotificationIcons() + } + + private fun updateStatusBarIcons() { + updateIconsForLayout( + { entry: NotificationEntry -> entry.icons.statusBarIcon }, + notificationIcons, + showAmbient = false /* showAmbient */, + showLowPriority = showLowPriority, + hideDismissed = true /* hideDismissed */, + hideRepliedMessages = true /* hideRepliedMessages */, + hideCurrentMedia = false /* hideCurrentMedia */, + hidePulsing = false /* hidePulsing */ + ) + } + + override fun updateAodNotificationIcons() { + if (aodIcons == null) { + return + } + updateIconsForLayout( + { entry: NotificationEntry -> entry.icons.aodIcon }, + aodIcons, + showAmbient = false /* showAmbient */, + showLowPriority = true /* showLowPriority */, + hideDismissed = true /* hideDismissed */, + hideRepliedMessages = true /* hideRepliedMessages */, + hideCurrentMedia = true /* hideCurrentMedia */, + hidePulsing = bypassController.bypassEnabled /* hidePulsing */ + ) + } + + override fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean) { + notificationIcons!!.showIconIsolated(icon, animated) + } + + override fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean) { + notificationIcons!!.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate) + } + + override fun onDozingChanged(isDozing: Boolean) { + if (aodIcons == null) { + return + } + val animate = (dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking) + aodIcons!!.setDozing(isDozing, animate, 0) + } + + override fun setAnimationsEnabled(enabled: Boolean) { + animationsEnabled = enabled + updateAnimations() + } + + override fun onStateChanged(newState: Int) { + updateAodIconsVisibility(animate = false, forceUpdate = false) + updateAnimations() + } + + override fun onThemeChanged() { + reloadAodColor() + updateAodIconColors() + } + + override fun getHeight(): Int { + return if (aodIcons == null) 0 else aodIcons!!.height + } + + @VisibleForTesting + fun appearAodIcons() { + if (aodIcons == null) { + return + } + if (screenOffAnimationController.shouldAnimateAodIcons()) { + aodIcons!!.translationY = -aodIconAppearTranslation.toFloat() + aodIcons!!.alpha = 0f + animateInAodIconTranslation() + aodIcons!! + .animate() + .alpha(1f) + .setInterpolator(Interpolators.LINEAR) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } else { + aodIcons!!.alpha = 1.0f + aodIcons!!.translationY = 0f + } + } + + override fun onFullyHiddenChanged(isFullyHidden: Boolean) { + var animate = true + if (!bypassController.bypassEnabled) { + animate = dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking + // We only want the appear animations to happen when the notifications get fully hidden, + // since otherwise the unhide animation overlaps + animate = animate and isFullyHidden + } + updateAodIconsVisibility(animate, false /* force */) + updateAodNotificationIcons() + updateAodIconColors() + } + + override fun onPulseExpansionChanged(expandingChanged: Boolean) { + if (expandingChanged) { + updateAodIconsVisibility(animate = true, forceUpdate = false) + } + } + + override fun demoCommands(): List<String> { + val commands = ArrayList<String>() + commands.add(DemoMode.COMMAND_NOTIFICATIONS) + return commands + } + + override fun dispatchDemoCommand(command: String, args: Bundle) { + if (notificationIconArea != null) { + val visible = args.getString("visible") + val vis = if ("false" == visible) View.INVISIBLE else View.VISIBLE + notificationIconArea?.visibility = vis + } + } + + override fun onDemoModeFinished() { + if (notificationIconArea != null) { + notificationIconArea?.visibility = View.VISIBLE + } + } + + private fun inflateIconArea(inflater: LayoutInflater): View { + return inflater.inflate(R.layout.notification_icon_area, null) + } + + /** Initializes the views that will represent the notification area. */ + private fun initializeNotificationAreaViews(context: Context) { + reloadDimens(context) + val layoutInflater = LayoutInflater.from(context) + notificationIconArea = inflateIconArea(layoutInflater) + notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons) + NotificationIconContainerViewBinder.bind(notificationIcons!!, statusBarIconsViewModel) + } + + private fun updateIconLayoutParams(context: Context) { + reloadDimens(context) + val params = generateIconLayoutParams() + for (i in 0 until notificationIcons!!.childCount) { + val child = notificationIcons!!.getChildAt(i) + child.layoutParams = params + } + if (shelfIcons != null) { + for (i in 0 until shelfIcons!!.childCount) { + val child = shelfIcons!!.getChildAt(i) + child.layoutParams = params + } + } + if (aodIcons != null) { + for (i in 0 until aodIcons!!.childCount) { + val child = aodIcons!!.getChildAt(i) + child.layoutParams = params + } + } + } + + private fun generateIconLayoutParams(): FrameLayout.LayoutParams { + return FrameLayout.LayoutParams( + iconSize + 2 * iconHPadding, + statusBarWindowController.statusBarHeight + ) + } + + private fun reloadDimens(context: Context) { + val res = context.resources + iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size_sp) + iconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin) + aodIconAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation) + } + + private fun shouldShowNotificationIcon( + entry: NotificationEntry, + showAmbient: Boolean, + showLowPriority: Boolean, + hideDismissed: Boolean, + hideRepliedMessages: Boolean, + hideCurrentMedia: Boolean, + hidePulsing: Boolean + ): Boolean { + if (!showAmbient && sectionStyleProvider.isMinimized(entry)) { + return false + } + if (hideCurrentMedia && entry.key == mediaManager.mediaNotificationKey) { + return false + } + if (!showLowPriority && sectionStyleProvider.isSilent(entry)) { + return false + } + if (entry.isRowDismissed && hideDismissed) { + return false + } + if (hideRepliedMessages && entry.isLastMessageFromReply) { + return false + } + // showAmbient == show in shade but not shelf + if (!showAmbient && entry.shouldSuppressStatusBar()) { + return false + } + if ( + hidePulsing && + entry.showingPulsing() && + (!wakeUpCoordinator.notificationsFullyHidden || !entry.isPulseSuppressed) + ) { + return false + } + return if (bubblesOptional.isPresent && bubblesOptional.get().isBubbleExpanded(entry.key)) { + false + } else true + } + + private fun updateNotificationIcons() { + Trace.beginSection("NotificationIconAreaController.updateNotificationIcons") + updateStatusBarIcons() + updateShelfIcons() + updateAodNotificationIcons() + applyNotificationIconsTint() + Trace.endSection() + } + + private fun updateShelfIcons() { + if (shelfIcons == null) { + return + } + updateIconsForLayout( + { entry: NotificationEntry -> entry.icons.shelfIcon }, + shelfIcons, + showAmbient = true, + showLowPriority = true, + hideDismissed = false, + hideRepliedMessages = false, + hideCurrentMedia = false, + hidePulsing = false + ) + } + + /** + * Updates the notification icons for a host layout. This will ensure that the notification host + * layout will have the same icons like the ones in here. + * + * @param function A function to look up an icon view based on an entry + * @param hostLayout which layout should be updated + * @param showAmbient should ambient notification icons be shown + * @param showLowPriority should icons from silent notifications be shown + * @param hideDismissed should dismissed icons be hidden + * @param hideRepliedMessages should messages that have been replied to be hidden + * @param hidePulsing should pulsing notifications be hidden + */ + private fun updateIconsForLayout( + function: Function<NotificationEntry, StatusBarIconView?>, + hostLayout: NotificationIconContainer?, + showAmbient: Boolean, + showLowPriority: Boolean, + hideDismissed: Boolean, + hideRepliedMessages: Boolean, + hideCurrentMedia: Boolean, + hidePulsing: Boolean, + ) { + val toShow = ArrayList<StatusBarIconView>(notificationEntries.size) + // Filter out ambient notifications and notification children. + for (i in notificationEntries.indices) { + val entry = notificationEntries[i].representativeEntry + if (entry != null && entry.row != null) { + if ( + shouldShowNotificationIcon( + entry, + showAmbient, + showLowPriority, + hideDismissed, + hideRepliedMessages, + hideCurrentMedia, + hidePulsing + ) + ) { + val iconView = function.apply(entry) + if (iconView != null) { + toShow.add(iconView) + } + } + } + } + + // In case we are changing the suppression of a group, the replacement shouldn't flicker + // and it should just be replaced instead. We therefore look for notifications that were + // just replaced by the child or vice-versa to suppress this. + val replacingIcons = ArrayMap<String, ArrayList<StatusBarIcon>>() + val toRemove = ArrayList<View>() + for (i in 0 until hostLayout!!.childCount) { + val child = hostLayout.getChildAt(i) as? StatusBarIconView ?: continue + if (!toShow.contains(child)) { + var iconWasReplaced = false + val removedGroupKey = child.notification.groupKey + for (j in toShow.indices) { + val candidate = toShow[j] + if ( + candidate.sourceIcon.sameAs(child.sourceIcon) && + candidate.notification.groupKey == removedGroupKey + ) { + if (!iconWasReplaced) { + iconWasReplaced = true + } else { + iconWasReplaced = false + break + } + } + } + if (iconWasReplaced) { + var statusBarIcons = replacingIcons[removedGroupKey] + if (statusBarIcons == null) { + statusBarIcons = ArrayList() + replacingIcons[removedGroupKey] = statusBarIcons + } + statusBarIcons.add(child.statusBarIcon) + } + toRemove.add(child) + } + } + // removing all duplicates + val duplicates = ArrayList<String?>() + for (key in replacingIcons.keys) { + val statusBarIcons = replacingIcons[key]!! + if (statusBarIcons.size != 1) { + duplicates.add(key) + } + } + replacingIcons.removeAll(duplicates) + hostLayout.setReplacingIcons(replacingIcons) + val toRemoveCount = toRemove.size + for (i in 0 until toRemoveCount) { + hostLayout.removeView(toRemove[i]) + } + val params = generateIconLayoutParams() + for (i in toShow.indices) { + val v = toShow[i] + // The view might still be transiently added if it was just removed and added again + hostLayout.removeTransientView(v) + if (v.parent == null) { + if (hideDismissed) { + v.setOnDismissListener(updateStatusBarIcons) + } + hostLayout.addView(v, i, params) + } + } + hostLayout.setChangingViewPositions(true) + // Re-sort notification icons + val childCount = hostLayout.childCount + for (i in 0 until childCount) { + val actual = hostLayout.getChildAt(i) + val expected = toShow[i] + if (actual === expected) { + continue + } + hostLayout.removeView(expected) + hostLayout.addView(expected, i) + } + hostLayout.setChangingViewPositions(false) + hostLayout.setReplacingIcons(null) + } + + /** Applies [.mIconTint] to the notification icons. */ + private fun applyNotificationIconsTint() { + for (i in 0 until notificationIcons!!.childCount) { + val iv = notificationIcons!!.getChildAt(i) as StatusBarIconView + if (iv.width != 0) { + updateTintForIcon(iv, iconTint) + } else { + iv.executeOnLayout { updateTintForIcon(iv, iconTint) } + } + } + updateAodIconColors() + } + + private fun updateTintForIcon(v: StatusBarIconView, tint: Int) { + val isPreL = java.lang.Boolean.TRUE == v.getTag(R.id.icon_is_pre_L) + var color = StatusBarIconView.NO_COLOR + val colorize = !isPreL || NotificationUtils.isGrayscale(v, contrastColorUtil) + if (colorize) { + color = DarkIconDispatcher.getTint(tintAreas, v, tint) + } + v.staticDrawableColor = color + v.setDecorColor(tint) + } + + private fun updateAnimations() { + val inShade = statusBarStateController.state == StatusBarState.SHADE + if (aodIcons != null) { + aodIcons!!.setAnimationsEnabled(animationsEnabled && !inShade) + } + notificationIcons!!.setAnimationsEnabled(animationsEnabled && inShade) + } + + private fun animateInAodIconTranslation() { + aodIcons!! + .animate() + .setInterpolator(Interpolators.DECELERATE_QUINT) + .translationY(0f) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } + + private fun reloadAodColor() { + aodIconTint = + Utils.getColorAttrDefaultColor( + context, + R.attr.wallpaperTextColor, + DEFAULT_AOD_ICON_COLOR + ) + } + + private fun updateAodIconColors() { + if (aodIcons != null) { + for (i in 0 until aodIcons!!.childCount) { + val iv = aodIcons!!.getChildAt(i) as StatusBarIconView + if (iv.width != 0) { + updateTintForIcon(iv, aodIconTint) + } else { + iv.executeOnLayout { updateTintForIcon(iv, aodIconTint) } + } + } + } + } + + private fun updateAodIconsVisibility(animate: Boolean, forceUpdate: Boolean) { + if (aodIcons == null) { + return + } + var visible = (bypassController.bypassEnabled || wakeUpCoordinator.notificationsFullyHidden) + + // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is + // playing, in which case we want them to be visible since we're animating in the AOD UI and + // will be switching to KEYGUARD shortly. + if ( + statusBarStateController.state != StatusBarState.KEYGUARD && + !screenOffAnimationController.shouldShowAodIconsWhenShade() + ) { + visible = false + } + if (visible && wakeUpCoordinator.isPulseExpanding() && !bypassController.bypassEnabled) { + visible = false + } + if (aodIconsVisible != visible || forceUpdate) { + aodIconsVisible = visible + aodIcons!!.animate().cancel() + if (animate) { + val wasFullyInvisible = aodIcons!!.visibility != View.VISIBLE + if (aodIconsVisible) { + if (wasFullyInvisible) { + // No fading here, let's just appear the icons instead! + aodIcons!!.visibility = View.VISIBLE + aodIcons!!.alpha = 1.0f + appearAodIcons() + } else { + // Let's make sure the icon are translated to 0, since we cancelled it above + animateInAodIconTranslation() + // We were fading out, let's fade in instead + CrossFadeHelper.fadeIn(aodIcons) + } + } else { + // Let's make sure the icon are translated to 0, since we cancelled it above + animateInAodIconTranslation() + CrossFadeHelper.fadeOut(aodIcons) + } + } else { + aodIcons!!.alpha = 1.0f + aodIcons!!.translationY = 0f + aodIcons!!.visibility = if (visible) View.VISIBLE else View.INVISIBLE + } + } + } + + companion object { + private const val AOD_ICONS_APPEAR_DURATION: Long = 200 + + @ColorInt private val DEFAULT_AOD_ICON_COLOR = -0x1 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt new file mode 100644 index 000000000000..8293bb329a01 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -0,0 +1,33 @@ +/* + * 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.statusbar.notification.icon.ui.viewbinder + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel +import com.android.systemui.statusbar.phone.NotificationIconContainer +import kotlinx.coroutines.DisposableHandle + +/** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */ +object NotificationIconContainerViewBinder { + fun bind( + view: NotificationIconContainer, + viewModel: NotificationIconContainerViewModel, + ): DisposableHandle { + return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) {} } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt new file mode 100644 index 000000000000..f68b0ef79638 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt @@ -0,0 +1,22 @@ +/* + * 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.statusbar.notification.icon.ui.viewmodel + +import javax.inject.Inject + +/** View-model for the row of notification icons displayed on the always-on display. */ +class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor() : + NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt new file mode 100644 index 000000000000..933c76f19aee --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt @@ -0,0 +1,22 @@ +/* + * 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.statusbar.notification.icon.ui.viewmodel + +import javax.inject.Inject + +/** View-model for the overflow row of notification icons displayed in the notification shade. */ +class NotificationIconContainerShelfViewModel @Inject constructor() : + NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt new file mode 100644 index 000000000000..2217646e6022 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt @@ -0,0 +1,22 @@ +/* + * 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.statusbar.notification.icon.ui.viewmodel + +import javax.inject.Inject + +/** View-model for the row of notification icons displayed in the status bar, */ +class NotificationIconContainerStatusBarViewModel @Inject constructor() : + NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt new file mode 100644 index 000000000000..892b2be9ed6e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt @@ -0,0 +1,22 @@ +/* + * 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.statusbar.notification.icon.ui.viewmodel + +/** + * View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and + * AOD. + */ +interface NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt index b0023305ecdd..c27682726da5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt @@ -20,7 +20,9 @@ import android.content.Context import android.util.AttributeSet import android.view.View import android.widget.TextView +import com.android.internal.widget.ConversationLayout import com.android.internal.widget.ImageFloatingTextView +import com.android.internal.widget.MessagingLayout import javax.inject.Inject class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory { @@ -35,6 +37,10 @@ class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory TextView::class.java.simpleName -> PrecomputedTextView(context, attrs) ImageFloatingTextView::class.java.name -> PrecomputedImageFloatingTextView(context, attrs) + MessagingLayout::class.java.name -> + MessagingLayout(context, attrs).apply { setPrecomputedTextEnabled(true) } + ConversationLayout::class.java.name -> + ConversationLayout(context, attrs).apply { setPrecomputedTextEnabled(true) } else -> null } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 22a87a7c9432..b92c51fac5f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -64,8 +64,10 @@ class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() : override fun setOnClickListener(listener: View.OnClickListener) = unsupported - private val unsupported: Nothing - get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled") + companion object { + val unsupported: Nothing + get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled") + } } /** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 4668aa433533..6db8df91edcc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -28,7 +28,6 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows; -import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; @@ -150,6 +149,7 @@ import javax.inject.Named; public class NotificationStackScrollLayoutController { private static final String TAG = "StackScrollerController"; private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); + private static final String HIGH_PRIORITY = "high_priority"; private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 5c28be3bc678..af09bf281c0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -25,7 +25,6 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RemoteAnimationAdapter; import android.view.View; @@ -276,19 +275,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void userActivity(); - boolean interceptMediaKey(KeyEvent event); - - boolean dispatchKeyEventPreIme(KeyEvent event); - - boolean onMenuPressed(); - void endAffordanceLaunch(); /** Should the keyguard be hidden immediately in response to a back press/gesture. */ boolean shouldKeyguardHideImmediately(); - boolean onSpacePressed(); - void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction, Runnable cancelAction); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index 6431ef958239..2bc7b996ffb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; @@ -33,12 +34,15 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.util.Log; import android.util.Slog; +import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsController.Appearance; import android.view.WindowInsetsController.Behavior; +import androidx.annotation.VisibleForTesting; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.statusbar.LetterboxDetails; @@ -49,6 +53,7 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.camera.CameraIntents; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSHost; @@ -107,6 +112,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba private final Lazy<CameraLauncher> mCameraLauncherLazy; private final QuickSettingsController mQsController; private final QSHost mQSHost; + private final FeatureFlags mFeatureFlags; private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); @@ -144,7 +150,8 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba Lazy<CameraLauncher> cameraLauncherLazy, UserTracker userTracker, QSHost qsHost, - ActivityStarter activityStarter) { + ActivityStarter activityStarter, + FeatureFlags featureFlags) { mCentralSurfaces = centralSurfaces; mQsController = quickSettingsController; mContext = context; @@ -171,6 +178,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba mCameraLauncherLazy = cameraLauncherLazy; mUserTracker = userTracker; mQSHost = qsHost; + mFeatureFlags = featureFlags; mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation); mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect( @@ -314,7 +322,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN); if (mShadeViewController.isFullyCollapsed()) { if (mVibrateOnOpening) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + vibrateOnNavigationKeyDown(); } mShadeViewController.expand(true /* animate */); mNotificationStackScrollLayoutController.setWillExpand(true); @@ -587,4 +595,15 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba } return VibrationEffect.createWaveform(timings, /* repeat= */ -1); } + + @VisibleForTesting + void vibrateOnNavigationKeyDown() { + if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { + mShadeViewController.performHapticFeedback( + HapticFeedbackConstants.GESTURE_START + ); + } else { + mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 127569d179fe..ccb87bf44dc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -89,7 +89,6 @@ import android.util.MathUtils; import android.view.Display; import android.view.IRemoteAnimationRunner; import android.view.IWindowManager; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ThreadedRenderer; import android.view.View; @@ -1612,8 +1611,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mBackActionInteractor.setup(mQsController, mShadeSurface); mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter(); - mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener()); - // Listen for demo mode changes mDemoModeController.addCallback(mDemoModeCallback); @@ -2638,44 +2635,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public boolean interceptMediaKey(KeyEvent event) { - return mState == StatusBarState.KEYGUARD - && mStatusBarKeyguardViewManager.interceptMediaKey(event); - } - - /** - * While IME is active and a BACK event is detected, check with - * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event - * should be handled before routing to IME, in order to prevent the user having to hit back - * twice to exit bouncer. - */ - @Override - public boolean dispatchKeyEventPreIme(KeyEvent event) { - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_BACK: - if (mState == StatusBarState.KEYGUARD - && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) { - return mBackActionInteractor.onBackRequested(); - } - } - return false; - } - - protected boolean shouldUnlockOnMenuPressed() { - return mDeviceInteractive && mState != StatusBarState.SHADE - && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed(); - } - - @Override - public boolean onMenuPressed() { - if (shouldUnlockOnMenuPressed()) { - mShadeController.animateCollapseShadeForced(); - return true; - } - return false; - } - - @Override public void endAffordanceLaunch() { releaseGestureWakeLock(); mCameraLauncherLazy.get().setLaunchingAffordance(false); @@ -2694,15 +2653,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { return (isScrimmedBouncer || isBouncerOverDream); } - @Override - public boolean onSpacePressed() { - if (mDeviceInteractive && mState != StatusBarState.SHADE) { - mShadeController.animateCollapseShadeForced(); - return true; - } - return false; - } - private void showBouncerOrLockScreenIfKeyguard() { // If the keyguard is animating away, we aren't really the keyguard anymore and should not // show the bouncer/lockscreen. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java index 0bf0f4b504b0..d22ed3802eed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java @@ -1,3 +1,18 @@ +/* + * 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.statusbar.phone; import android.content.Context; @@ -43,6 +58,8 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionSt import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.wm.shell.bubbles.Bubbles; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -55,13 +72,13 @@ import javax.inject.Inject; * normally reserved for notifications. */ @SysUISingleton -public class NotificationIconAreaController implements +public class LegacyNotificationIconAreaControllerImpl implements + NotificationIconAreaController, DarkReceiver, StatusBarStateController.StateListener, NotificationWakeUpCoordinator.WakeUpListener, DemoMode { - public static final String HIGH_PRIORITY = "high_priority"; private static final long AOD_ICONS_APPEAR_DURATION = 200; @ColorInt private static final int DEFAULT_AOD_ICON_COLOR = 0xffffffff; @@ -110,7 +127,7 @@ public class NotificationIconAreaController implements }; @Inject - public NotificationIconAreaController( + public LegacyNotificationIconAreaControllerImpl( Context context, StatusBarStateController statusBarStateController, NotificationWakeUpCoordinator wakeUpCoordinator, @@ -192,7 +209,7 @@ public class NotificationIconAreaController implements } } - public void onDensityOrFontScaleChanged(Context context) { + public void onDensityOrFontScaleChanged(@NotNull Context context) { updateIconLayoutParams(context); } @@ -493,7 +510,7 @@ public class NotificationIconAreaController implements mNotificationIcons.showIconIsolated(icon, animated); } - public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) { + public void setIsolatedIconLocation(@NotNull Rect iconDrawingRect, boolean requireStateUpdate) { mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt new file mode 100644 index 000000000000..0079f7ceb539 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt @@ -0,0 +1,47 @@ +/* + * 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.statusbar.phone + +import android.content.Context +import android.graphics.Rect +import android.view.View +import com.android.systemui.statusbar.NotificationShelfController +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.collection.ListEntry + +/** + * A controller for the space in the status bar to the left of the system icons. This area is + * normally reserved for notifications. + */ +interface NotificationIconAreaController { + /** Called by the Keyguard*ViewController whose view contains the aod icons. */ + fun setupAodIcons(aodIcons: NotificationIconContainer) + fun setupShelf(notificationShelfController: NotificationShelfController) + fun setShelfIcons(icons: NotificationIconContainer) + fun onDensityOrFontScaleChanged(context: Context) + + /** Returns the view that represents the notification area. */ + fun getNotificationInnerAreaView(): View? + + /** Updates the notifications with the given list of notifications to display. */ + fun updateNotificationIcons(entries: List<@JvmSuppressWildcards ListEntry>) + fun updateAodNotificationIcons() + fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean) + fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean) + fun setAnimationsEnabled(enabled: Boolean) + fun onThemeChanged() + fun getHeight(): Int +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt new file mode 100644 index 000000000000..d1ddd51a04c7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt @@ -0,0 +1,38 @@ +/* + * 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.statusbar.phone + +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconAreaControllerViewBinderWrapperImpl +import dagger.Module +import dagger.Provides +import javax.inject.Provider + +@Module +object NotificationIconAreaControllerModule { + @Provides + fun provideNotificationIconAreaControllerImpl( + featureFlags: FeatureFlags, + legacyProvider: Provider<LegacyNotificationIconAreaControllerImpl>, + newProvider: Provider<NotificationIconAreaControllerViewBinderWrapperImpl>, + ): NotificationIconAreaController = + if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + newProvider.get() + } else { + legacyProvider.get() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index 931aedd90542..4de669c0b34a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -28,8 +28,7 @@ import com.android.systemui.Gefingerpoken import com.android.systemui.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.RemoteUserInput +import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeLogger import com.android.systemui.shade.ShadeViewController @@ -56,7 +55,7 @@ class PhoneStatusBarViewController private constructor( private val centralSurfaces: CentralSurfaces, private val shadeController: ShadeController, private val shadeViewController: ShadeViewController, - private val sceneInteractor: Provider<SceneInteractor>, + private val windowRootView: Provider<WindowRootView>, private val shadeLogger: ShadeLogger, private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?, private val userChipViewModel: StatusBarUserChipViewModel, @@ -80,7 +79,8 @@ class PhoneStatusBarViewController private constructor( statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)) if (moveFromCenterAnimationController == null) return - val statusBarLeftSide: View = mView.requireViewById(R.id.status_bar_start_side_except_heads_up) + val statusBarLeftSide: View = + mView.requireViewById(R.id.status_bar_start_side_except_heads_up) val systemIconArea: ViewGroup = mView.requireViewById(R.id.status_bar_end_side_content) val viewsToAnimate = arrayOf( @@ -179,11 +179,8 @@ class PhoneStatusBarViewController private constructor( // If scene framework is enabled, route the touch to it and // ignore the rest of the gesture. if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { - sceneInteractor.get() - .onRemoteUserInput(RemoteUserInput.translateMotionEvent(event)) - // TODO(b/291965119): remove once view is expanded to cover the status bar - sceneInteractor.get().setVisible(true, "swipe down from status bar") - return false + windowRootView.get().dispatchTouchEvent(event) + return true } if (event.action == MotionEvent.ACTION_DOWN) { @@ -247,7 +244,7 @@ class PhoneStatusBarViewController private constructor( private val centralSurfaces: CentralSurfaces, private val shadeController: ShadeController, private val shadeViewController: ShadeViewController, - private val sceneInteractor: Provider<SceneInteractor>, + private val windowRootView: Provider<WindowRootView>, private val shadeLogger: ShadeLogger, private val viewUtil: ViewUtil, private val configurationController: ConfigurationController, @@ -269,7 +266,7 @@ class PhoneStatusBarViewController private constructor( centralSurfaces, shadeController, shadeViewController, - sceneInteractor, + windowRootView, shadeLogger, statusBarMoveFromCenterAnimationController, userChipViewModel, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index fc661384146c..62a8cfde80da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -709,6 +709,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } + public void onUnlockAnimationFinished() { + mAnimatingPanelExpansionOnUnlock = false; + applyAndDispatchState(); + } + /** * Set the amount of progress we are currently in if we're transitioning to the full shade. * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java index 9a295e63fb9e..4b39854e43ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java @@ -16,23 +16,24 @@ package com.android.systemui.statusbar.phone; +import com.android.systemui.CoreStartable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.window.StatusBarWindowController; import javax.inject.Inject; /** - * Ties the {@link CentralSurfaces} to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. + * Ties the status bar to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. */ -@CentralSurfacesComponent.CentralSurfacesScope -public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener { +@SysUISingleton +public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, CoreStartable { private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarWindowController mStatusBarWindowController; private final ShadeViewController mShadeViewController; @@ -63,6 +64,11 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener } @Override + public void start() { + mHeadsUpManager.addListener(this); + } + + @Override public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { if (inPinnedMode) { mNotificationShadeWindowController.setHeadsUpShowing(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 7fe01825890f..a6284e3f62ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -32,6 +32,8 @@ import android.os.UserHandle; import android.view.View; import android.view.ViewParent; +import androidx.annotation.Nullable; + import com.android.systemui.ActivityIntentHelper; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -254,16 +256,16 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, @Override public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, - boolean appRequestedAuth, + boolean appRequestedAuth, @Nullable Integer actionIndex, NotificationRemoteInputManager.ClickHandler defaultHandler) { final boolean isActivity = pendingIntent.isActivity(); if (isActivity || appRequestedAuth) { - mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent); + mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent, actionIndex); final boolean afterKeyguardGone = mActivityIntentHelper .wouldPendingLaunchResolverActivity(pendingIntent, mLockscreenUserManager.getCurrentUserId()); mActivityStarter.dismissKeyguardThenExecute(() -> { - mActionClickLogger.logKeyguardGone(pendingIntent); + mActionClickLogger.logKeyguardGone(pendingIntent, actionIndex); try { ActivityManager.getService().resumeAppSwitches(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index 1bceb29dc85d..cd1afc727346 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -223,10 +223,14 @@ class UnlockedScreenOffAnimationController @Inject constructor( } .setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_SLOW_IN), true /* animate */) - interactionJankMonitor.begin( - notifShadeWindowControllerLazy.get().windowRootView, - CUJ_SCREEN_OFF_SHOW_AOD - ) + val builder = InteractionJankMonitor.Configuration.Builder + .withView( + InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD, + notifShadeWindowControllerLazy.get().windowRootView + ) + .setTag(statusBarStateControllerImpl.getClockId()) + + interactionJankMonitor.begin(builder) } override fun onStartedWakingUp() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java index 3a3663d4d6c4..bbbe16f54734 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java @@ -25,7 +25,6 @@ import com.android.systemui.shade.ShadeHeaderController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks; import com.android.systemui.statusbar.phone.CentralSurfacesImpl; -import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarterModule; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; @@ -72,11 +71,6 @@ public interface CentralSurfacesComponent { WindowRootView getWindowRootView(); /** - * Creates a StatusBarHeadsUpChangeListener. - */ - StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener(); - - /** * Creates a CentralSurfacesCommandQueueCallbacks. */ CentralSurfacesCommandQueueCallbacks getCentralSurfacesCommandQueueCallbacks(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt index fe2481595ff4..275cfc58d581 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt @@ -221,36 +221,15 @@ constructor( override val activityInVisible: Flow<Boolean> = activity .map { it?.hasActivityIn ?: false } - .distinctUntilChanged() - .logDiffsForTable( - iconInteractor.tableLogBuffer, - columnPrefix = "", - columnName = "activityInVisible", - initialValue = false, - ) .stateIn(scope, SharingStarted.WhileSubscribed(), false) override val activityOutVisible: Flow<Boolean> = activity .map { it?.hasActivityOut ?: false } - .distinctUntilChanged() - .logDiffsForTable( - iconInteractor.tableLogBuffer, - columnPrefix = "", - columnName = "activityOutVisible", - initialValue = false, - ) .stateIn(scope, SharingStarted.WhileSubscribed(), false) override val activityContainerVisible: Flow<Boolean> = activity .map { it != null && (it.hasActivityIn || it.hasActivityOut) } - .distinctUntilChanged() - .logDiffsForTable( - iconInteractor.tableLogBuffer, - columnPrefix = "", - columnName = "activityContainerVisible", - initialValue = false, - ) .stateIn(scope, SharingStarted.WhileSubscribed(), false) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt index 4a31b8687069..eaae0f0c4f75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt @@ -33,6 +33,7 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.Dependency import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.shade.ShadeExpansionStateManager +import com.android.systemui.shade.ShadeLogger import com.android.systemui.util.ViewController import com.android.systemui.util.time.SystemClock import java.text.FieldPosition @@ -83,6 +84,7 @@ class VariableDateViewController( private val systemClock: SystemClock, private val broadcastDispatcher: BroadcastDispatcher, private val shadeExpansionStateManager: ShadeExpansionStateManager, + private val shadeLogger: ShadeLogger, private val timeTickHandler: Handler, view: VariableDateView ) : ViewController<VariableDateView>(view) { @@ -111,24 +113,29 @@ class VariableDateViewController( private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + if ( + Intent.ACTION_LOCALE_CHANGED == action || + Intent.ACTION_TIMEZONE_CHANGED == action + ) { + // need to get a fresh date format + dateFormat = null + shadeLogger.d("VariableDateViewController received intent to refresh date format") + } + + val handler = mView.handler + // If the handler is null, it means we received a broadcast while the view has not // finished being attached or in the process of being detached. // In that case, do not post anything. - val handler = mView.handler ?: return - val action = intent.action - if ( + if (handler == null) { + shadeLogger.d("VariableDateViewController received intent but handler was null") + } else if ( Intent.ACTION_TIME_TICK == action || Intent.ACTION_TIME_CHANGED == action || Intent.ACTION_TIMEZONE_CHANGED == action || Intent.ACTION_LOCALE_CHANGED == action ) { - if ( - Intent.ACTION_LOCALE_CHANGED == action || - Intent.ACTION_TIMEZONE_CHANGED == action - ) { - // need to get a fresh date format - handler.post { dateFormat = null } - } handler.post(::updateClock) } } @@ -231,6 +238,7 @@ class VariableDateViewController( private val systemClock: SystemClock, private val broadcastDispatcher: BroadcastDispatcher, private val shadeExpansionStateManager: ShadeExpansionStateManager, + private val shadeLogger: ShadeLogger, @Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler ) { fun create(view: VariableDateView): VariableDateViewController { @@ -238,6 +246,7 @@ class VariableDateViewController( systemClock, broadcastDispatcher, shadeExpansionStateManager, + shadeLogger, handler, view ) diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 53f4837cefcb..9cc3cdbf5c34 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -67,7 +67,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; @@ -84,6 +83,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; @@ -134,7 +134,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final Receiver mReceiver = new Receiver(); private final RingerModeObservers mRingerModeObservers; private final MediaSessions mMediaSessions; - private CaptioningManager mCaptioningManager; + private final AtomicReference<CaptioningManager> mCaptioningManager = new AtomicReference<>(); private final KeyguardManager mKeyguardManager; private final ActivityManager mActivityManager; private final UserTracker mUserTracker; @@ -158,16 +158,16 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final WakefulnessLifecycle.Observer mWakefullnessLifecycleObserver = new WakefulnessLifecycle.Observer() { - @Override - public void onStartedWakingUp() { - mDeviceInteractive = true; - } + @Override + public void onStartedWakingUp() { + mDeviceInteractive = true; + } - @Override - public void onFinishedGoingToSleep() { - mDeviceInteractive = false; - } - }; + @Override + public void onFinishedGoingToSleep() { + mDeviceInteractive = false; + } + }; @Inject public VolumeDialogControllerImpl( @@ -185,8 +185,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa KeyguardManager keyguardManager, ActivityManager activityManager, UserTracker userTracker, - DumpManager dumpManager, - @Main Handler mainHandler + DumpManager dumpManager ) { mContext = context.getApplicationContext(); mPackageManager = packageManager; @@ -215,7 +214,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mKeyguardManager = keyguardManager; mActivityManager = activityManager; mUserTracker = userTracker; - mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mainHandler)); + mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mWorker)); createCaptioningManagerServiceByUserContext(mUserTracker.getUserContext()); dumpManager.registerDumpable("VolumeDialogControllerImpl", this); @@ -223,8 +222,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa boolean accessibilityVolumeStreamActive = accessibilityManager .isAccessibilityVolumeStreamActive(); mVolumeController.setA11yMode(accessibilityVolumeStreamActive ? - VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME : - VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME); + VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME : + VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME); mWakefulnessLifecycle.addObserver(mWakefullnessLifecycleObserver); } @@ -337,15 +336,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa }; private void createCaptioningManagerServiceByUserContext(@NonNull Context userContext) { - mCaptioningManager = userContext.getSystemService(CaptioningManager.class); + mCaptioningManager.set(userContext.getSystemService(CaptioningManager.class)); } - public boolean areCaptionsEnabled() { - return mCaptioningManager.isSystemAudioCaptioningEnabled(); + public void getCaptionsEnabledState(boolean checkForSwitchState) { + mWorker.obtainMessage(W.GET_CAPTIONS_ENABLED_STATE, checkForSwitchState).sendToTarget(); } - public void setCaptionsEnabled(boolean isEnabled) { - mCaptioningManager.setSystemAudioCaptioningEnabled(isEnabled); + public void setCaptionsEnabledState(boolean enabled) { + mWorker.obtainMessage(W.SET_CAPTIONS_ENABLED_STATE, enabled).sendToTarget(); } public void getCaptionsComponentState(boolean fromTooltip) { @@ -386,8 +385,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) { - mShowVolumeDialog = volumeUi; - mShowSafetyWarning = safetyWarning; + mShowVolumeDialog = volumeUi; + mShowSafetyWarning = safetyWarning; } @Override @@ -438,12 +437,38 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } private void onShowCsdWarningW(@AudioManager.CsdWarning int csdWarning, int durationMs) { - mCallbacks.onShowCsdWarning(csdWarning, durationMs); + mCallbacks.onShowCsdWarning(csdWarning, durationMs); } private void onGetCaptionsComponentStateW(boolean fromTooltip) { - mCallbacks.onCaptionComponentStateChanged( - mCaptioningManager.isSystemAudioCaptioningUiEnabled(), fromTooltip); + CaptioningManager captioningManager = mCaptioningManager.get(); + if (null != captioningManager) { + mCallbacks.onCaptionComponentStateChanged( + captioningManager.isSystemAudioCaptioningUiEnabled(), fromTooltip); + } else { + Log.e(TAG, "onGetCaptionsComponentStateW(), null captioningManager"); + } + } + + private void onGetCaptionsEnabledStateW(boolean checkForSwitchState) { + CaptioningManager captioningManager = mCaptioningManager.get(); + if (null != captioningManager) { + mCallbacks.onCaptionEnabledStateChanged( + captioningManager.isSystemAudioCaptioningEnabled(), checkForSwitchState); + } else { + Log.e(TAG, "onGetCaptionsEnabledStateW(), null captioningManager"); + } + } + + private void onSetCaptionsEnabledStateW(boolean enabled) { + CaptioningManager captioningManager = mCaptioningManager.get(); + if (null != captioningManager) { + captioningManager.setSystemAudioCaptioningEnabled(enabled); + mCallbacks.onCaptionEnabledStateChanged( + captioningManager.isSystemAudioCaptioningEnabled(), false); + } else { + Log.e(TAG, "onGetCaptionsEnabledStateW(), null captioningManager"); + } } private void onAccessibilityModeChanged(Boolean showA11yStream) { @@ -822,6 +847,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private static final int ACCESSIBILITY_MODE_CHANGED = 15; private static final int GET_CAPTIONS_COMPONENT_STATE = 16; private static final int SHOW_CSD_WARNING = 17; + private static final int GET_CAPTIONS_ENABLED_STATE = 18; + private static final int SET_CAPTIONS_ENABLED_STATE = 19; W(Looper looper) { super(looper); @@ -849,6 +876,10 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj); break; case SHOW_CSD_WARNING: onShowCsdWarningW(msg.arg1, msg.arg2); break; + case GET_CAPTIONS_ENABLED_STATE: + onGetCaptionsEnabledStateW((Boolean) msg.obj); break; + case SET_CAPTIONS_ENABLED_STATE: + onSetCaptionsEnabledStateW((Boolean) msg.obj); break; } } } @@ -1017,6 +1048,17 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa componentEnabled, fromTooltip)); } } + + @Override + public void onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkBeforeSwitch) { + boolean captionsEnabled = isEnabled != null && isEnabled; + for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) { + entry.getValue().post( + () -> entry.getKey().onCaptionEnabledStateChanged( + captionsEnabled, checkBeforeSwitch)); + } + } + } private final class RingerModeObservers { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 0e97e2192cdc..dcc0525bb436 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -1333,21 +1333,30 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, if (!isServiceComponentEnabled) return; - updateCaptionsIcon(); + checkEnabledStateForCaptionsIconUpdate(); if (fromTooltip) showCaptionsTooltip(); } - private void updateCaptionsIcon() { - boolean captionsEnabled = mController.areCaptionsEnabled(); - if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) { - mHandler.post(mODICaptionsIcon.setCaptionsEnabled(captionsEnabled)); + private void updateCaptionsEnabledH(boolean isCaptionsEnabled, boolean checkForSwitchState) { + if (checkForSwitchState) { + mController.setCaptionsEnabledState(!isCaptionsEnabled); + } else { + updateCaptionsIcon(isCaptionsEnabled); + } + } + + private void checkEnabledStateForCaptionsIconUpdate() { + mController.getCaptionsEnabledState(false); + } + + private void updateCaptionsIcon(boolean isCaptionsEnabled) { + if (mODICaptionsIcon.getCaptionsEnabled() != isCaptionsEnabled) { + mHandler.post(mODICaptionsIcon.setCaptionsEnabled(isCaptionsEnabled)); } } private void onCaptionIconClicked() { - boolean isEnabled = mController.areCaptionsEnabled(); - mController.setCaptionsEnabled(!isEnabled); - updateCaptionsIcon(); + mController.getCaptionsEnabledState(true); } private void incrementManualToggleCount() { @@ -2365,7 +2374,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } else { updateRowsH(activeRow); } - } @Override @@ -2373,6 +2381,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, Boolean isComponentEnabled, Boolean fromTooltip) { updateODICaptionsH(isComponentEnabled, fromTooltip); } + + @Override + public void onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkForSwitchState) { + updateCaptionsEnabledH(isEnabled, checkForSwitchState); + } }; @VisibleForTesting void onPostureChanged(int posture) { diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index 316b54eb0c80..aea3030967d2 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -109,6 +109,7 @@ public class ImageWallpaper extends WallpaperService { private WallpaperManager mWallpaperManager; private final WallpaperLocalColorExtractor mWallpaperLocalColorExtractor; private SurfaceHolder mSurfaceHolder; + private boolean mDrawn = false; @VisibleForTesting static final int MIN_SURFACE_WIDTH = 128; @VisibleForTesting @@ -133,6 +134,7 @@ public class ImageWallpaper extends WallpaperService { setShowForAllUsers(true); mWallpaperLocalColorExtractor = new WallpaperLocalColorExtractor( mLongExecutor, + mLock, new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() { @Override public void onColorsProcessed(List<RectF> regions, @@ -238,6 +240,7 @@ public class ImageWallpaper extends WallpaperService { private void drawFrameSynchronized() { synchronized (mLock) { + if (mDrawn) return; drawFrameInternal(); } } @@ -275,6 +278,7 @@ public class ImageWallpaper extends WallpaperService { Rect dest = mSurfaceHolder.getSurfaceFrame(); try { canvas.drawBitmap(bitmap, null, dest, null); + mDrawn = true; } finally { surface.unlockCanvasAndPost(canvas); } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java index 1e8446f8df1d..e2ec8dc056ca 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java @@ -61,7 +61,7 @@ public class WallpaperLocalColorExtractor { private int mBitmapWidth = -1; private int mBitmapHeight = -1; - private final Object mLock = new Object(); + private final Object mLock; private final List<RectF> mPendingRegions = new ArrayList<>(); private final Set<RectF> mProcessedRegions = new ArraySet<>(); @@ -102,12 +102,15 @@ public class WallpaperLocalColorExtractor { /** * Creates a new color extractor. * @param longExecutor the executor on which the color extraction will be performed + * @param lock the lock object to use for computing colors or processing the bitmap * @param wallpaperLocalColorExtractorCallback an interface to handle the callbacks from * the color extractor. */ public WallpaperLocalColorExtractor(@LongRunning Executor longExecutor, + Object lock, WallpaperLocalColorExtractorCallback wallpaperLocalColorExtractorCallback) { mLongExecutor = longExecutor; + mLock = lock; mWallpaperLocalColorExtractorCallback = wallpaperLocalColorExtractorCallback; } @@ -149,6 +152,12 @@ public class WallpaperLocalColorExtractor { private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) { synchronized (mLock) { + if (bitmap.isRecycled()) { + // ImageWallpaper loaded a new bitmap before the extraction of the previous one + // In that case, ImageWallpaper also scheduled the extraction of the next bitmap + Log.i(TAG, "Wallpaper has changed; deferring color extraction"); + return; + } if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { Log.e(TAG, "Attempt to extract colors from an invalid bitmap"); return; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index 677d3ff3df69..c894d914bfa3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -180,8 +180,9 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { } @Test - public void testResume() { - mKeyguardAbsKeyInputViewController.onResume(KeyguardSecurityView.VIEW_REVEALED); + public void testOnViewAttached() { + reset(mLockPatternUtils); + mKeyguardAbsKeyInputViewController.onViewAttached(); verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt()); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java index b3496967f525..438f0f43acb6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java @@ -18,6 +18,8 @@ package com.android.keyguard; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; +import static com.android.systemui.flags.Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; @@ -37,6 +39,7 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -59,8 +62,13 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { @Mock private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; @Mock + private ConnectedDisplayKeyguardPresentation.Factory + mConnectedDisplayKeyguardPresentationFactory; + @Mock private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation; @Mock + private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation; + @Mock private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper; @Mock private KeyguardStateController mKeyguardStateController; @@ -69,7 +77,7 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { private Executor mBackgroundExecutor = Runnable::run; private KeyguardDisplayManager mManager; private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); - + private FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags(); // The default and secondary displays are both in the default group private Display mDefaultDisplay; private Display mSecondaryDisplay; @@ -80,10 +88,14 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, false); mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController, mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor, - mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController)); + mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController, + mConnectedDisplayKeyguardPresentationFactory, mFakeFeatureFlags)); doReturn(mKeyguardPresentation).when(mManager).createPresentation(any()); + doReturn(mConnectedDisplayKeyguardPresentation).when( + mConnectedDisplayKeyguardPresentationFactory).create(any()); mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); @@ -138,4 +150,15 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { when(mKeyguardStateController.isOccluded()).thenReturn(true); verify(mManager, never()).createPresentation(eq(mSecondaryDisplay)); } + + @Test + public void testShow_withClockPresentationFlagEnabled_presentationCreated() { + when(mManager.createPresentation(any())).thenCallRealMethod(); + mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, true); + mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay}); + + mManager.show(); + + verify(mConnectedDisplayKeyguardPresentationFactory).create(eq(mSecondaryDisplay)); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt index 1a9260c2ede6..3a9473085583 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt @@ -20,6 +20,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.inputmethod.InputMethodManager import android.widget.EditText +import android.widget.ImageView import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils @@ -29,6 +30,7 @@ import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.mockito.whenever import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -36,6 +38,7 @@ import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` @@ -45,104 +48,109 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class KeyguardPasswordViewControllerTest : SysuiTestCase() { - @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView - @Mock private lateinit var passwordEntry: EditText - @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode - @Mock lateinit var lockPatternUtils: LockPatternUtils - @Mock lateinit var keyguardSecurityCallback: KeyguardSecurityCallback - @Mock lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory - @Mock lateinit var latencyTracker: LatencyTracker - @Mock lateinit var inputMethodManager: InputMethodManager - @Mock lateinit var emergencyButtonController: EmergencyButtonController - @Mock lateinit var mainExecutor: DelayableExecutor - @Mock lateinit var falsingCollector: FalsingCollector - @Mock lateinit var keyguardViewController: KeyguardViewController - @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea - @Mock - private lateinit var mKeyguardMessageAreaController: - KeyguardMessageAreaController<BouncerKeyguardMessageArea> + @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView + @Mock private lateinit var passwordEntry: EditText + @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode + @Mock lateinit var lockPatternUtils: LockPatternUtils + @Mock lateinit var keyguardSecurityCallback: KeyguardSecurityCallback + @Mock lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory + @Mock lateinit var latencyTracker: LatencyTracker + @Mock lateinit var inputMethodManager: InputMethodManager + @Mock lateinit var emergencyButtonController: EmergencyButtonController + @Mock lateinit var mainExecutor: DelayableExecutor + @Mock lateinit var falsingCollector: FalsingCollector + @Mock lateinit var keyguardViewController: KeyguardViewController + @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea + @Mock + private lateinit var mKeyguardMessageAreaController: + KeyguardMessageAreaController<BouncerKeyguardMessageArea> - private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController + private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - Mockito.`when`( - keyguardPasswordView.requireViewById<BouncerKeyguardMessageArea>( - R.id.bouncer_message_area)) - .thenReturn(mKeyguardMessageArea) - Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea)) - .thenReturn(mKeyguardMessageAreaController) - Mockito.`when`(keyguardPasswordView.passwordTextViewId).thenReturn(R.id.passwordEntry) - Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry)) - .thenReturn(passwordEntry) - `when`(keyguardPasswordView.resources).thenReturn(context.resources) - val fakeFeatureFlags = FakeFeatureFlags() - fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) - keyguardPasswordViewController = - KeyguardPasswordViewController( - keyguardPasswordView, - keyguardUpdateMonitor, - securityMode, - lockPatternUtils, - keyguardSecurityCallback, - messageAreaControllerFactory, - latencyTracker, - inputMethodManager, - emergencyButtonController, - mainExecutor, - mContext.resources, - falsingCollector, - keyguardViewController, - fakeFeatureFlags) - } + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + Mockito.`when`( + keyguardPasswordView.requireViewById<BouncerKeyguardMessageArea>( + R.id.bouncer_message_area + ) + ) + .thenReturn(mKeyguardMessageArea) + Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea)) + .thenReturn(mKeyguardMessageAreaController) + Mockito.`when`(keyguardPasswordView.passwordTextViewId).thenReturn(R.id.passwordEntry) + Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry)) + .thenReturn(passwordEntry) + whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button)) + .thenReturn(mock(ImageView::class.java)) + `when`(keyguardPasswordView.resources).thenReturn(context.resources) + val fakeFeatureFlags = FakeFeatureFlags() + fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) + keyguardPasswordViewController = + KeyguardPasswordViewController( + keyguardPasswordView, + keyguardUpdateMonitor, + securityMode, + lockPatternUtils, + keyguardSecurityCallback, + messageAreaControllerFactory, + latencyTracker, + inputMethodManager, + emergencyButtonController, + mainExecutor, + mContext.resources, + falsingCollector, + keyguardViewController, + fakeFeatureFlags + ) + } - @Test - fun testFocusWhenBouncerIsShown() { - Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(true) - Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) - keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) - keyguardPasswordView.post { - verify(keyguardPasswordView).requestFocus() - verify(keyguardPasswordView).showKeyboard() + @Test + fun testFocusWhenBouncerIsShown() { + Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(true) + Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) + keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) + keyguardPasswordView.post { + verify(keyguardPasswordView).requestFocus() + verify(keyguardPasswordView).showKeyboard() + } } - } - @Test - fun testDoNotFocusWhenBouncerIsHidden() { - Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(false) - Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) - keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) - verify(keyguardPasswordView, never()).requestFocus() - } + @Test + fun testDoNotFocusWhenBouncerIsHidden() { + Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(false) + Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) + keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) + verify(keyguardPasswordView, never()).requestFocus() + } - @Test - fun testHideKeyboardWhenOnPause() { - keyguardPasswordViewController.onPause() - keyguardPasswordView.post { - verify(keyguardPasswordView).clearFocus() - verify(keyguardPasswordView).hideKeyboard() + @Test + fun testHideKeyboardWhenOnPause() { + keyguardPasswordViewController.onPause() + keyguardPasswordView.post { + verify(keyguardPasswordView).clearFocus() + verify(keyguardPasswordView).hideKeyboard() + } } - } - @Test - fun startAppearAnimation() { - keyguardPasswordViewController.startAppearAnimation() - verify(mKeyguardMessageAreaController) - .setMessage(context.resources.getString(R.string.keyguard_enter_your_password), false) - } + @Test + fun testOnViewAttached() { + keyguardPasswordViewController.onViewAttached() + verify(mKeyguardMessageAreaController) + .setMessage(context.resources.getString(R.string.keyguard_enter_your_password), false) + } - @Test - fun startAppearAnimation_withExistingMessage() { - `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") - keyguardPasswordViewController.startAppearAnimation() - verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean()) - } + @Test + fun testOnViewAttached_withExistingMessage() { + `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") + keyguardPasswordViewController.onViewAttached() + verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean()) + } - @Test - fun testMessageIsSetWhenReset() { - keyguardPasswordViewController.resetState() - verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password) - } + @Test + fun testMessageIsSetWhenReset() { + keyguardPasswordViewController.resetState() + verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password) + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt index 9f7ab7b30d19..1acd676f02cd 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt @@ -46,6 +46,7 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -54,35 +55,35 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class KeyguardPatternViewControllerTest : SysuiTestCase() { - private lateinit var mKeyguardPatternView: KeyguardPatternView + private lateinit var mKeyguardPatternView: KeyguardPatternView - @Mock private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode + @Mock private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode - @Mock private lateinit var mLockPatternUtils: LockPatternUtils + @Mock private lateinit var mLockPatternUtils: LockPatternUtils - @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback + @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback - @Mock private lateinit var mLatencyTracker: LatencyTracker - private var mFalsingCollector: FalsingCollector = FalsingCollectorFake() + @Mock private lateinit var mLatencyTracker: LatencyTracker + private var mFalsingCollector: FalsingCollector = FalsingCollectorFake() - @Mock private lateinit var mEmergencyButtonController: EmergencyButtonController + @Mock private lateinit var mEmergencyButtonController: EmergencyButtonController - @Mock - private lateinit var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory + @Mock + private lateinit var mKeyguardMessageAreaControllerFactory: + KeyguardMessageAreaController.Factory - @Mock - private lateinit var mKeyguardMessageAreaController: - KeyguardMessageAreaController<BouncerKeyguardMessageArea> + @Mock + private lateinit var mKeyguardMessageAreaController: + KeyguardMessageAreaController<BouncerKeyguardMessageArea> - @Mock private lateinit var mPostureController: DevicePostureController + @Mock private lateinit var mPostureController: DevicePostureController - private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController - private lateinit var fakeFeatureFlags: FakeFeatureFlags + private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController + private lateinit var fakeFeatureFlags: FakeFeatureFlags - @Captor - lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback> + @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback> @Before fun setup() { @@ -91,9 +92,8 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() { .thenReturn(mKeyguardMessageAreaController) fakeFeatureFlags = FakeFeatureFlags() fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false) - mKeyguardPatternView = View.inflate(mContext, R.layout.keyguard_pattern_view, null) - as KeyguardPatternView - + mKeyguardPatternView = + View.inflate(mContext, R.layout.keyguard_pattern_view, null) as KeyguardPatternView mKeyguardPatternViewController = KeyguardPatternViewController( @@ -125,8 +125,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() { @Test fun onDevicePostureChanged_deviceOpened_propagatedToPatternView() { overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f) - whenever(mPostureController.devicePosture) - .thenReturn(DEVICE_POSTURE_HALF_OPENED) + whenever(mPostureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED) mKeyguardPatternViewController.onViewAttached() @@ -159,39 +158,37 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() { return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio) } - @Test - fun withFeatureFlagOn_oldMessage_isHidden() { - fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) - - mKeyguardPatternViewController.onViewAttached() - - verify<KeyguardMessageAreaController<*>>(mKeyguardMessageAreaController).disable() - } - - @Test - fun onPause_resetsText() { - mKeyguardPatternViewController.init() - mKeyguardPatternViewController.onPause() - verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern) - } - - @Test - fun startAppearAnimation() { - mKeyguardPatternViewController.startAppearAnimation() - verify(mKeyguardMessageAreaController) - .setMessage(context.resources.getString(R.string.keyguard_enter_your_pattern), false) - } - - @Test - fun startAppearAnimation_withExistingMessage() { - `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") - mKeyguardPatternViewController.startAppearAnimation() - verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean()) - } - - @Test - fun resume() { - mKeyguardPatternViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) - verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt()) - } + @Test + fun withFeatureFlagOn_oldMessage_isHidden() { + fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) + + mKeyguardPatternViewController.onViewAttached() + + verify<KeyguardMessageAreaController<*>>(mKeyguardMessageAreaController).disable() + } + + @Test + fun onPause_resetsText() { + mKeyguardPatternViewController.init() + mKeyguardPatternViewController.onPause() + verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern) + } + + @Test + fun testOnViewAttached() { + reset(mKeyguardMessageAreaController) + reset(mLockPatternUtils) + mKeyguardPatternViewController.onViewAttached() + verify(mKeyguardMessageAreaController) + .setMessage(context.resources.getString(R.string.keyguard_enter_your_pattern), false) + verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt()) + } + + @Test + fun testOnViewAttached_withExistingMessage() { + reset(mKeyguardMessageAreaController) + `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") + mKeyguardPatternViewController.onViewAttached() + verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean()) + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java index cf86c2192352..efe1955595ca 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java @@ -100,6 +100,8 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { .thenReturn(mDeleteButton); when(mPinBasedInputView.findViewById(R.id.key_enter)) .thenReturn(mOkButton); + + when(mPinBasedInputView.getResources()).thenReturn(getContext().getResources()); FakeFeatureFlags featureFlags = new FakeFeatureFlags(); featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 309d9e084ac8..80fd7213778e 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -185,27 +185,27 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { } @Test - fun startAppearAnimation() { + fun testOnViewAttached() { val pinViewController = constructPinViewController(mockKeyguardPinView) - pinViewController.startAppearAnimation() + pinViewController.onViewAttached() verify(keyguardMessageAreaController) .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) } @Test - fun startAppearAnimation_withExistingMessage() { + fun testOnViewAttached_withExistingMessage() { val pinViewController = constructPinViewController(mockKeyguardPinView) Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.") - pinViewController.startAppearAnimation() + pinViewController.onViewAttached() verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean()) } @Test - fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { + fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { val pinViewController = constructPinViewController(mockKeyguardPinView) `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) @@ -213,7 +213,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3) `when`(passwordTextView.text).thenReturn("") - pinViewController.startAppearAnimation() + pinViewController.onViewAttached() verify(deleteButton).visibility = View.INVISIBLE verify(enterButton).visibility = View.INVISIBLE @@ -222,7 +222,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { } @Test - fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { + fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { val pinViewController = constructPinViewController(mockKeyguardPinView) `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) @@ -230,7 +230,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6) `when`(passwordTextView.text).thenReturn("") - pinViewController.startAppearAnimation() + pinViewController.onViewAttached() verify(deleteButton).visibility = View.VISIBLE verify(enterButton).visibility = View.VISIBLE diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 0192e78dc09d..dff058c49188 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -58,12 +58,15 @@ import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.UserSwitcherController import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argThat +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.GlobalSettings @@ -133,6 +136,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @Mock private lateinit var audioManager: AudioManager @Mock private lateinit var userInteractor: UserInteractor @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate + @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @Captor private lateinit var swipeListenerArgumentCaptor: @@ -182,6 +186,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { whenever(keyguardPasswordView.windowInsetsController).thenReturn(windowInsetsController) whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN) whenever(keyguardStateController.canDismissLockScreen()).thenReturn(true) + whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(true) featureFlags = FakeFeatureFlags() featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) @@ -249,6 +254,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { mock(), { JavaAdapter(sceneTestUtils.testScope.backgroundScope) }, userInteractor, + deviceProvisionedController, faceAuthAccessibilityDelegate, keyguardTransitionInteractor ) { @@ -506,6 +512,30 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // THEN the next security method of None will dismiss keyguard. verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) } + @Test + fun showNextSecurityScreenOrFinish_SimPin_Swipe_userNotSetup() { + // GIVEN the current security method is SimPin + whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)) + .thenReturn(false) + underTest.showSecurityScreen(SecurityMode.SimPin) + + // WHEN a request is made from the SimPin screens to show the next security method + whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)) + .thenReturn(SecurityMode.None) + // WHEN security method is SWIPE + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(false) + underTest.showNextSecurityScreenOrFinish( + /* authenticated= */ true, + TARGET_USER_ID, + /* bypassSecondaryLockScreen= */ true, + SecurityMode.SimPin + ) + + // THEN the next security method of None will dismiss keyguard. + verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt()) + } @Test fun onSwipeUp_whenFaceDetectionIsNotRunning_initiatesFaceAuth() { @@ -578,29 +608,56 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) underTest.onViewAttached() verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture()) - clearInvocations(viewFlipperController) configurationListenerArgumentCaptor.value.onThemeChanged() - verify(viewFlipperController).clearViews() - verify(viewFlipperController) + verify(view).reloadColors() + } + + @Test + fun onUiModeChanged() { + val configurationListenerArgumentCaptor = + ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) + underTest.onViewAttached() + verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture()) + configurationListenerArgumentCaptor.value.onUiModeChanged() + verify(view).reloadColors() + } + @Test + fun onOrientationChanged_landscapeKeyguardFlagDisabled_blockReinflate() { + featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) + + // Run onOrientationChanged + val configurationListenerArgumentCaptor = + ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) + underTest.onViewAttached() + verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture()) + clearInvocations(viewFlipperController) + configurationListenerArgumentCaptor.value.onOrientationChanged( + Configuration.ORIENTATION_LANDSCAPE + ) + // Verify view is reinflated when flag is on + verify(viewFlipperController, never()).clearViews() + verify(viewFlipperController, never()) .asynchronouslyInflateView( eq(SecurityMode.PIN), any(), onViewInflatedCallbackArgumentCaptor.capture() ) - onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController) - verify(view).reset() - verify(viewFlipperController).reset() - verify(view).reloadColors() } @Test - fun onUiModeChanged() { + fun onOrientationChanged_landscapeKeyguardFlagEnabled_doesReinflate() { + featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true) + + // Run onOrientationChanged val configurationListenerArgumentCaptor = ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) underTest.onViewAttached() verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture()) clearInvocations(viewFlipperController) - configurationListenerArgumentCaptor.value.onUiModeChanged() + configurationListenerArgumentCaptor.value.onOrientationChanged( + Configuration.ORIENTATION_LANDSCAPE + ) + // Verify view is reinflated when flag is on verify(viewFlipperController).clearViews() verify(viewFlipperController) .asynchronouslyInflateView( @@ -608,8 +665,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { any(), onViewInflatedCallbackArgumentCaptor.capture() ) - onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController) - verify(view).reloadColors() } @Test @@ -847,6 +902,17 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { verify(userSwitcher).setAlpha(0f) } + @Test + fun testOnUserSwitched() { + val userSwitchCallbackArgumentCaptor = + argumentCaptor<UserSwitcherController.UserSwitchCallback>() + underTest.onViewAttached() + verify(userSwitcherController) + .addUserSwitchCallback(capture(userSwitchCallbackArgumentCaptor)) + userSwitchCallbackArgumentCaptor.value.onUserSwitched() + verify(viewFlipperController).asynchronouslyInflateView(any(), any(), any()) + } + private val registeredSwipeListener: KeyguardSecurityContainer.SwipeListener get() { underTest.onViewAttached() diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index a3acc781f2a7..291dda256c4f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -97,6 +97,8 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { @Test fun onViewAttached() { underTest.onViewAttached() + verify(keyguardMessageAreaController) + .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) } @Test @@ -120,8 +122,6 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation() { underTest.startAppearAnimation() - verify(keyguardMessageAreaController) - .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt index efcf4ddb5c71..626faa601970 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt @@ -98,6 +98,8 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() { underTest.onViewAttached() Mockito.verify(keyguardUpdateMonitor) .registerCallback(any(KeyguardUpdateMonitorCallback::class.java)) + Mockito.verify(keyguardMessageAreaController) + .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) } @Test @@ -120,8 +122,6 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation() { underTest.startAppearAnimation() - Mockito.verify(keyguardMessageAreaController) - .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index 7d23c800321a..b8b0198f94df 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -77,7 +77,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(false, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", false, true)); mController.updatePosition(10, 15, 20f, true); @@ -92,7 +92,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(true, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", true, true)); mController.updatePosition(10, 15, 20f, true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 6f3322ab1b85..0cd82f09b6c2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -1694,71 +1694,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled() - throws RemoteException { - // SFPS supported and enrolled - when(mAuthController.isSfpsSupported()).thenReturn(true); - when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true); - - // WHEN require interactive to auth is disabled, and keyguard is not awake - when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(false); - - // Preconditions for sfps auth to run - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - - statusBarShadeIsLocked(); - mTestableLooper.processAllMessages(); - - // THEN we should listen for sfps when screen off, because require screen on is disabled - assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue(); - - // WHEN require interactive to auth is enabled, and keyguard is not awake - when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true); - - // THEN we shouldn't listen for sfps when screen off, because require screen on is enabled - assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse(); - - // Device now awake & keyguard is now interactive - deviceNotGoingToSleep(); - deviceIsInteractive(); - keyguardIsVisible(); - - // THEN we should listen for sfps when screen on, and require screen on is enabled - assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue(); - } - - @Test - public void notListeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthEnabled() - throws RemoteException { - // GIVEN SFPS supported and enrolled - when(mAuthController.isSfpsSupported()).thenReturn(true); - when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true); - - // GIVEN Preconditions for sfps auth to run - keyguardNotGoingAway(); - currentUserIsSystem(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - statusBarShadeIsLocked(); - - // WHEN require interactive to auth is enabled & keyguard is going to sleep - when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true); - deviceGoingToSleep(); - - mTestableLooper.processAllMessages(); - - // THEN we should NOT listen for sfps because device is going to sleep - assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse(); - } - - @Test public void listeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthDisabled() throws RemoteException { // GIVEN SFPS supported and enrolled diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index b23f7f2db01e..b100336b602f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -21,6 +21,8 @@ import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static com.android.systemui.appops.AppOpsControllerImpl.OPS_MIC; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; @@ -66,6 +68,7 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -543,69 +546,114 @@ public class AppOpsControllerTest extends SysuiTestCase { @Test public void testAudioFilteredWhenMicDisabled() { - mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA}, - mCallback); + int micOp = AppOpsManager.OP_RECORD_AUDIO; + int nonMicOp = AppOpsManager.OP_CAMERA; + + // Add callbacks for the micOp and nonMicOp, called for the micOp active state change, + // verify the micOp is the only active op returned. + mController.addCallback(new int[]{micOp, nonMicOp}, mCallback); mTestableLooper.processAllMessages(); mController.onOpActiveChanged( - AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); - List<AppOpItem> list = mController.getActiveAppOps(); - assertEquals(1, list.size()); - assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); - assertFalse(list.get(0).isDisabled()); + verifySingleActiveOps(micOp); - // Add a camera op, and disable the microphone. The camera op should be the only op returned + // Add a non-mic op, and disable the microphone. The camera op should be the only active op + // returned. mController.onSensorBlockedChanged(MICROPHONE, true); - mController.onOpActiveChanged( - AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER, + TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); - list = mController.getActiveAppOps(); - assertEquals(1, list.size()); - assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + verifySingleActiveOps(nonMicOp); - - // Re enable the microphone, and verify the op returns + // Re-enable the microphone, and verify the active op returns. mController.onSensorBlockedChanged(MICROPHONE, false); mTestableLooper.processAllMessages(); - - list = mController.getActiveAppOps(); - assertEquals(2, list.size()); - int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0; - assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(micIdx).getCode()); + verifyActiveOps(micOp, nonMicOp); } @Test public void testPhoneCallMicrophoneFilteredWhenMicDisabled() { - mController.addCallback( - new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE, AppOpsManager.OP_CAMERA}, - mCallback); + int micOp = AppOpsManager.OP_PHONE_CALL_MICROPHONE; + int nonMicOp = AppOpsManager.OP_CAMERA; + + // Add callbacks for the micOp and nonMicOp, called for the micOp active state change, + // verify the micOp is the only active op returned. + mController.addCallback(new int[]{micOp, nonMicOp}, mCallback); mTestableLooper.processAllMessages(); mController.onOpActiveChanged( - AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); - List<AppOpItem> list = mController.getActiveAppOps(); - assertEquals(1, list.size()); - assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(0).getCode()); - assertFalse(list.get(0).isDisabled()); + verifySingleActiveOps(micOp); - // Add a camera op, and disable the microphone. The camera op should be the only op returned + // Add a non-mic op, and disable the microphone. The camera op should be the only active op + // returned. mController.onSensorBlockedChanged(MICROPHONE, true); + mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER, + TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + verifySingleActiveOps(nonMicOp); + + // Re-enable the microphone, and verify the active op returns. + mController.onSensorBlockedChanged(MICROPHONE, false); + mTestableLooper.processAllMessages(); + verifyActiveOps(micOp, nonMicOp); + } + + @Test + public void testAmbientTriggerMicrophoneFilteredWhenMicDisabled() { + int micOp = AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; + int nonMicOp = AppOpsManager.OP_CAMERA; + + // Add callbacks for the micOp and nonMicOp, called for the micOp active state change, + // verify the micOp is the only active op returned. + mController.addCallback(new int[]{micOp, nonMicOp}, mCallback); + mTestableLooper.processAllMessages(); mController.onOpActiveChanged( - AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); - list = mController.getActiveAppOps(); - assertEquals(1, list.size()); - assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + verifySingleActiveOps(micOp); + // Add a non-mic op, and disable the microphone. The camera op should be the only active op + // returned. + mController.onSensorBlockedChanged(MICROPHONE, true); + mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER, + TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + verifySingleActiveOps(nonMicOp); - // Re enable the microphone, and verify the op returns + // Re-enable the microphone, and verify the active op returns. mController.onSensorBlockedChanged(MICROPHONE, false); mTestableLooper.processAllMessages(); + verifyActiveOps(micOp, nonMicOp); + } - list = mController.getActiveAppOps(); - assertEquals(2, list.size()); - int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0; - assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(micIdx).getCode()); + @Test + public void testSandboxTriggerMicrophoneFilteredWhenMicDisabled() { + int micOp = AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; + int nonMicOp = AppOpsManager.OP_CAMERA; + + // Add callbacks for the micOp and nonMicOp, called for the micOp active state change, + // verify the micOp is the only active op returned. + mController.addCallback(new int[]{micOp, nonMicOp}, mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + verifySingleActiveOps(micOp); + + // Add a non-mic op, and disable the microphone. The camera op should be the only active op + // returned. + mController.onSensorBlockedChanged(MICROPHONE, true); + mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER, + TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + verifySingleActiveOps(nonMicOp); + + // Re-enable the microphone, and verify the active op returns. + mController.onSensorBlockedChanged(MICROPHONE, false); + mTestableLooper.processAllMessages(); + verifyActiveOps(micOp, nonMicOp); } @Test @@ -708,6 +756,22 @@ public class AppOpsControllerTest extends SysuiTestCase { micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, false); } + private void verifySingleActiveOps(int op) { + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(op, list.get(0).getCode()); + assertFalse(list.get(0).isDisabled()); + } + + private void verifyActiveOps(int micOp, int nonMicOp) { + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(2, list.size()); + List<Integer> codes = Arrays.asList(list.get(0).getCode(), list.get(1).getCode()); + assertThat(codes).containsExactly(micOp, nonMicOp); + assertFalse(list.get(0).isDisabled()); + assertFalse(list.get(1).isDisabled()); + } + private class TestHandler extends AppOpsControllerImpl.H { TestHandler(Looper looper) { mController.super(looper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 5ce8a001ea7b..48e513140c3a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -99,6 +99,7 @@ import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java index 362d26b040e8..cf4e2c319ac9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; @@ -28,6 +29,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; @@ -86,6 +88,9 @@ public class BiometricNotificationDialogFactoryTest extends SysuiTestCase { @Test public void testFingerprintReEnrollDialog_onRemovalSucceeded() { + assumeTrue(getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)); + mDialogFactory.createReenrollDialog(mContextSpy, mDialog, BiometricSourceType.FINGERPRINT); @@ -109,6 +114,9 @@ public class BiometricNotificationDialogFactoryTest extends SysuiTestCase { @Test public void testFingerprintReEnrollDialog_onRemovalError() { + assumeTrue(getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)); + mDialogFactory.createReenrollDialog(mContextSpy, mDialog, BiometricSourceType.FINGERPRINT); @@ -130,6 +138,9 @@ public class BiometricNotificationDialogFactoryTest extends SysuiTestCase { @Test public void testFaceReEnrollDialog_onRemovalSucceeded() { + assumeTrue(getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_FACE)); + mDialogFactory.createReenrollDialog(mContextSpy, mDialog, BiometricSourceType.FACE); @@ -153,6 +164,9 @@ public class BiometricNotificationDialogFactoryTest extends SysuiTestCase { @Test public void testFaceReEnrollDialog_onRemovalError() { + assumeTrue(getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_FACE)); + mDialogFactory.createReenrollDialog(mContextSpy, mDialog, BiometricSourceType.FACE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt index efae3fe1af2c..8eb274a65bc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt @@ -24,13 +24,12 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.util.mockito.whenever import com.google.common.truth.StringSubject import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before @@ -39,13 +38,12 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class BouncerMessageFactoryTest : SysuiTestCase() { private lateinit var underTest: BouncerMessageFactory - @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Mock private lateinit var securityModel: KeyguardSecurityModel @@ -55,7 +53,8 @@ class BouncerMessageFactoryTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) testScope = TestScope() - underTest = BouncerMessageFactory(updateMonitor, securityModel) + biometricSettingsRepository = FakeBiometricSettingsRepository() + underTest = BouncerMessageFactory(biometricSettingsRepository, securityModel) } @Test @@ -167,7 +166,7 @@ class BouncerMessageFactoryTest : SysuiTestCase() { secondaryMessageOverride: String? = null, ): BouncerMessageModel? { whenever(securityModel.getSecurityMode(0)).thenReturn(mode) - whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(fpAuthAllowed) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(fpAuthAllowed) return underTest.createFromPromptReason( reason, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt index 2be7d8a43a18..562a8ef512d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt @@ -101,14 +101,15 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository() testScope = TestScope() - whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(false) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false) whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN) underTest = BouncerMessageRepositoryImpl( trustRepository = trustRepository, biometricSettingsRepository = biometricSettingsRepository, updateMonitor = updateMonitor, - bouncerMessageFactory = BouncerMessageFactory(updateMonitor, securityModel), + bouncerMessageFactory = + BouncerMessageFactory(biometricSettingsRepository, securityModel), userRepository = userRepository, fingerprintAuthRepository = fingerprintRepository, systemPropertiesHelper = systemPropertiesHelper @@ -222,8 +223,7 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { whenever(systemPropertiesHelper.get("sys.boot.reason.last")) .thenReturn("reboot,mainline_update") userRepository.setSelectedUserInfo(PRIMARY_USER) - biometricSettingsRepository.setFaceEnrolled(true) - biometricSettingsRepository.setIsFaceAuthEnabled(true) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) verifyMessagesForAuthFlag( STRONG_AUTH_REQUIRED_AFTER_BOOT to @@ -236,8 +236,8 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setFaceEnrolled(false) - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) verifyMessagesForAuthFlag( STRONG_AUTH_NOT_REQUIRED to null, @@ -258,8 +258,8 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { fun authFlagsChanges_withTrustManaged_providesDifferentMessages() = testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) - biometricSettingsRepository.setFaceEnrolled(false) - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) trustRepository.setCurrentUserTrustManaged(true) @@ -290,10 +290,9 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) - biometricSettingsRepository.setIsFaceAuthEnabled(true) - biometricSettingsRepository.setFaceEnrolled(true) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) verifyMessagesForAuthFlag( STRONG_AUTH_NOT_REQUIRED to null, @@ -320,11 +319,9 @@ class BouncerMessageRepositoryTest : SysuiTestCase() { testScope.runTest { userRepository.setSelectedUserInfo(PRIMARY_USER) trustRepository.setCurrentUserTrustManaged(false) - biometricSettingsRepository.setIsFaceAuthEnabled(false) - biometricSettingsRepository.setFaceEnrolled(false) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) - biometricSettingsRepository.setFingerprintEnrolled(true) - biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) verifyMessagesForAuthFlag( STRONG_AUTH_NOT_REQUIRED to null, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index 38e5728f6e70..0d172706b127 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -94,9 +94,9 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - fun canShowAlternateBouncerForFingerprint_noFingerprintsEnrolled() { + fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() { givenCanShowAlternateBouncer() - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) assertFalse(underTest.canShowAlternateBouncerForFingerprint()) } @@ -104,15 +104,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { @Test fun canShowAlternateBouncerForFingerprint_strongBiometricNotAllowed() { givenCanShowAlternateBouncer() - biometricSettingsRepository.setStrongBiometricAllowed(false) - - assertFalse(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test - fun canShowAlternateBouncerForFingerprint_devicePolicyDoesNotAllowFingerprint() { - givenCanShowAlternateBouncer() - biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(false) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false) assertFalse(underTest.canShowAlternateBouncerForFingerprint()) } @@ -189,14 +181,13 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { private fun givenCanShowAlternateBouncer() { bouncerRepository.setAlternateBouncerUIAvailable(true) - biometricSettingsRepository.setFingerprintEnrolled(true) - biometricSettingsRepository.setStrongBiometricAllowed(true) - biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) whenever(keyguardStateController.isUnlocked).thenReturn(false) } private fun givenCannotShowAlternateBouncer() { - biometricSettingsRepository.setFingerprintEnrolled(false) + biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt index 3ca94aa8e7af..4089abef399b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt @@ -22,9 +22,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.R.string.keyguard_enter_pin import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown +import com.android.systemui.R.string.kg_unlock_with_pin_or_fp import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.factory.BouncerMessageFactory import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository @@ -34,11 +33,11 @@ import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.KotlinArgumentCaptor import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before @@ -49,14 +48,13 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) class BouncerMessageInteractorTest : SysuiTestCase() { @Mock private lateinit var securityModel: KeyguardSecurityModel - @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Mock private lateinit var countDownTimerUtil: CountDownTimerUtil private lateinit var countDownTimerCallback: KotlinArgumentCaptor<CountDownTimerCallback> private lateinit var underTest: BouncerMessageInteractor @@ -73,10 +71,11 @@ class BouncerMessageInteractorTest : SysuiTestCase() { userRepository.setUserInfos(listOf(PRIMARY_USER)) testScope = TestScope() countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java) + biometricSettingsRepository = FakeBiometricSettingsRepository() allowTestableLooperAsMainThread() whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN) - whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(false) + biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) } suspend fun TestScope.init() { @@ -86,7 +85,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { underTest = BouncerMessageInteractor( repository = repository, - factory = BouncerMessageFactory(updateMonitor, securityModel), + factory = BouncerMessageFactory(biometricSettingsRepository, securityModel), userRepository = userRepository, countDownTimerUtil = countDownTimerUtil, featureFlags = featureFlags @@ -151,7 +150,8 @@ class BouncerMessageInteractorTest : SysuiTestCase() { underTest.setCustomMessage("not empty") val customMessage = repository.customMessage - assertThat(customMessage.value!!.message!!.messageResId).isEqualTo(keyguard_enter_pin) + assertThat(customMessage.value!!.message!!.messageResId) + .isEqualTo(kg_unlock_with_pin_or_fp) assertThat(customMessage.value!!.secondaryMessage!!.message).isEqualTo("not empty") underTest.setCustomMessage(null) @@ -168,7 +168,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { val faceAcquisitionMessage = repository.faceAcquisitionMessage assertThat(faceAcquisitionMessage.value!!.message!!.messageResId) - .isEqualTo(keyguard_enter_pin) + .isEqualTo(kg_unlock_with_pin_or_fp) assertThat(faceAcquisitionMessage.value!!.secondaryMessage!!.message) .isEqualTo("not empty") @@ -186,7 +186,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { val fingerprintAcquisitionMessage = repository.fingerprintAcquisitionMessage assertThat(fingerprintAcquisitionMessage.value!!.message!!.messageResId) - .isEqualTo(keyguard_enter_pin) + .isEqualTo(kg_unlock_with_pin_or_fp) assertThat(fingerprintAcquisitionMessage.value!!.secondaryMessage!!.message) .isEqualTo("not empty") @@ -275,7 +275,8 @@ class BouncerMessageInteractorTest : SysuiTestCase() { repository.setBiometricLockedOutMessage(null) // sets the default message if everything else is null - assertThat(bouncerMessage()!!.message!!.messageResId).isEqualTo(keyguard_enter_pin) + assertThat(bouncerMessage()!!.message!!.messageResId) + .isEqualTo(kg_unlock_with_pin_or_fp) } private fun message(value: String): BouncerMessageModel { diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java index 7628be44755d..662c89c268e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java @@ -80,6 +80,7 @@ public class IntentCreatorTest extends SysuiTestCase { assertEquals(Intent.ACTION_EDIT, intent.getAction()); assertEquals("image/*", intent.getType()); assertEquals(null, intent.getComponent()); + assertEquals("clipboard", intent.getStringExtra("edit_source")); assertFlags(intent, EXTERNAL_INTENT_FLAGS); // try again with an editor component diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt new file mode 100644 index 000000000000..3df9cbb29e4a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt @@ -0,0 +1,285 @@ +package com.android.systemui.communal.data.repository + +import android.appwidget.AppWidgetHost +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetProviderInfo +import android.content.BroadcastReceiver +import android.content.pm.PackageManager +import android.os.UserHandle +import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.FakeLogBuffer +import com.android.systemui.settings.UserTracker +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.kotlinArgumentCaptor +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.StandardTestDispatcher +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.Mock +import org.mockito.Mockito +import org.mockito.Mockito.anyInt +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class CommunalWidgetRepositoryImplTest : SysuiTestCase() { + @Mock private lateinit var appWidgetManager: AppWidgetManager + + @Mock private lateinit var appWidgetHost: AppWidgetHost + + @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher + + @Mock private lateinit var packageManager: PackageManager + + @Mock private lateinit var userManager: UserManager + + @Mock private lateinit var userHandle: UserHandle + + @Mock private lateinit var userTracker: UserTracker + + @Mock private lateinit var featureFlags: FeatureFlags + + @Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo + + private lateinit var logBuffer: LogBuffer + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + logBuffer = FakeLogBuffer.Factory.create() + + featureFlagEnabled(true) + whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch") + whenever(userTracker.userHandle).thenReturn(userHandle) + } + + @Test + fun broadcastReceiver_featureDisabled_doNotRegisterUserUnlockedBroadcastReceiver() = + testScope.runTest { + featureFlagEnabled(false) + val repository = initCommunalWidgetRepository() + collectLastValue(repository.stopwatchAppWidgetInfo)() + verifyBroadcastReceiverNeverRegistered() + } + + @Test + fun broadcastReceiver_featureEnabledAndUserUnlocked_doNotRegisterBroadcastReceiver() = + testScope.runTest { + userUnlocked(true) + val repository = initCommunalWidgetRepository() + collectLastValue(repository.stopwatchAppWidgetInfo)() + verifyBroadcastReceiverNeverRegistered() + } + + @Test + fun broadcastReceiver_featureEnabledAndUserLocked_registerBroadcastReceiver() = + testScope.runTest { + userUnlocked(false) + val repository = initCommunalWidgetRepository() + collectLastValue(repository.stopwatchAppWidgetInfo)() + verifyBroadcastReceiverRegistered() + } + + @Test + fun broadcastReceiver_whenFlowFinishes_unregisterBroadcastReceiver() = + testScope.runTest { + userUnlocked(false) + val repository = initCommunalWidgetRepository() + + val job = launch { repository.stopwatchAppWidgetInfo.collect() } + runCurrent() + val receiver = broadcastReceiverUpdate() + + job.cancel() + runCurrent() + + Mockito.verify(broadcastDispatcher).unregisterReceiver(receiver) + } + + @Test + fun stopwatch_whenUserUnlocks_receiveProviderInfo() = + testScope.runTest { + userUnlocked(false) + val repository = initCommunalWidgetRepository() + val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo) + assertThat(lastStopwatchProviderInfo()).isNull() + + userUnlocked(true) + installedProviders(listOf(stopwatchProviderInfo)) + broadcastReceiverUpdate() + + assertThat(lastStopwatchProviderInfo()?.providerInfo).isEqualTo(stopwatchProviderInfo) + } + + @Test + fun stopwatch_userUnlockedButWidgetNotInstalled_noProviderInfo() = + testScope.runTest { + userUnlocked(true) + installedProviders(listOf()) + + val repository = initCommunalWidgetRepository() + + val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo) + assertThat(lastStopwatchProviderInfo()).isNull() + } + + @Test + fun appWidgetId_providerInfoAvailable_allocateAppWidgetId() = + testScope.runTest { + userUnlocked(true) + installedProviders(listOf(stopwatchProviderInfo)) + val repository = initCommunalWidgetRepository() + collectLastValue(repository.stopwatchAppWidgetInfo)() + Mockito.verify(appWidgetHost).allocateAppWidgetId() + } + + @Test + fun appWidgetId_userLockedAgainAfterProviderInfoAvailable_deleteAppWidgetId() = + testScope.runTest { + whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(123456) + userUnlocked(false) + val repository = initCommunalWidgetRepository() + val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo) + assertThat(lastStopwatchProviderInfo()).isNull() + + // User unlocks + userUnlocked(true) + installedProviders(listOf(stopwatchProviderInfo)) + broadcastReceiverUpdate() + + // Verify app widget id allocated + assertThat(lastStopwatchProviderInfo()?.appWidgetId).isEqualTo(123456) + Mockito.verify(appWidgetHost).allocateAppWidgetId() + Mockito.verify(appWidgetHost, Mockito.never()).deleteAppWidgetId(anyInt()) + + // User locked again + userUnlocked(false) + broadcastReceiverUpdate() + + // Verify app widget id deleted + assertThat(lastStopwatchProviderInfo()).isNull() + Mockito.verify(appWidgetHost).deleteAppWidgetId(123456) + } + + @Test + fun appWidgetHost_userUnlocked_startListening() = + testScope.runTest { + userUnlocked(false) + val repository = initCommunalWidgetRepository() + collectLastValue(repository.stopwatchAppWidgetInfo)() + Mockito.verify(appWidgetHost, Mockito.never()).startListening() + + userUnlocked(true) + broadcastReceiverUpdate() + collectLastValue(repository.stopwatchAppWidgetInfo)() + + Mockito.verify(appWidgetHost).startListening() + } + + @Test + fun appWidgetHost_userLockedAgain_stopListening() = + testScope.runTest { + userUnlocked(false) + val repository = initCommunalWidgetRepository() + collectLastValue(repository.stopwatchAppWidgetInfo)() + + userUnlocked(true) + broadcastReceiverUpdate() + collectLastValue(repository.stopwatchAppWidgetInfo)() + + Mockito.verify(appWidgetHost).startListening() + Mockito.verify(appWidgetHost, Mockito.never()).stopListening() + + userUnlocked(false) + broadcastReceiverUpdate() + collectLastValue(repository.stopwatchAppWidgetInfo)() + + Mockito.verify(appWidgetHost).stopListening() + } + + private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl { + return CommunalWidgetRepositoryImpl( + appWidgetManager, + appWidgetHost, + broadcastDispatcher, + packageManager, + userManager, + userTracker, + logBuffer, + featureFlags, + ) + } + + private fun verifyBroadcastReceiverRegistered() { + Mockito.verify(broadcastDispatcher) + .registerReceiver( + any(), + any(), + nullable(), + nullable(), + anyInt(), + nullable(), + ) + } + + private fun verifyBroadcastReceiverNeverRegistered() { + Mockito.verify(broadcastDispatcher, Mockito.never()) + .registerReceiver( + any(), + any(), + nullable(), + nullable(), + anyInt(), + nullable(), + ) + } + + private fun broadcastReceiverUpdate(): BroadcastReceiver { + val broadcastReceiverCaptor = kotlinArgumentCaptor<BroadcastReceiver>() + Mockito.verify(broadcastDispatcher) + .registerReceiver( + broadcastReceiverCaptor.capture(), + any(), + nullable(), + nullable(), + anyInt(), + nullable(), + ) + broadcastReceiverCaptor.value.onReceive(null, null) + return broadcastReceiverCaptor.value + } + + private fun featureFlagEnabled(enabled: Boolean) { + whenever(featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)).thenReturn(enabled) + } + + private fun userUnlocked(userUnlocked: Boolean) { + whenever(userManager.isUserUnlockingOrUnlocked(userHandle)).thenReturn(userUnlocked) + } + + private fun installedProviders(providers: List<AppWidgetProviderInfo>) { + whenever(appWidgetManager.installedProviders).thenReturn(providers) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt new file mode 100644 index 000000000000..d28f530f04f3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -0,0 +1,70 @@ +/* + * 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.communal.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.RoboPilotTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository +import com.android.systemui.communal.shared.CommunalAppWidgetInfo +import com.android.systemui.coroutines.collectLastValue +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +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.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RoboPilotTest +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +class CommunalInteractorTest : SysuiTestCase() { + @Mock private lateinit var stopwatchAppWidgetInfo: CommunalAppWidgetInfo + + private lateinit var testScope: TestScope + + private lateinit var widgetRepository: FakeCommunalWidgetRepository + private lateinit var interactor: CommunalInteractor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + testScope = TestScope() + widgetRepository = FakeCommunalWidgetRepository() + interactor = CommunalInteractor(widgetRepository) + } + + @Test + fun testAppWidgetInfoFlow() = + testScope.runTest { + val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo) + runCurrent() + assertThat(lastAppWidgetInfo()).isNull() + + widgetRepository.setStopwatchAppWidgetInfo(stopwatchAppWidgetInfo) + runCurrent() + assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt new file mode 100644 index 000000000000..e3a75f161318 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt @@ -0,0 +1,36 @@ +package com.android.systemui.communal.ui.view.layout.blueprints + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.constraintlayout.widget.ConstraintSet +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class DefaultCommunalBlueprintTest : SysuiTestCase() { + @Mock private lateinit var widgetSection: DefaultCommunalWidgetSection + + private lateinit var blueprint: DefaultCommunalBlueprint + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + blueprint = DefaultCommunalBlueprint(widgetSection) + } + + @Test + fun apply() { + val cs = ConstraintSet() + blueprint.apply(cs) + verify(widgetSection).apply(cs) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt index 692d794f21f0..8416c46a3f38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt @@ -18,12 +18,14 @@ package com.android.systemui.controls.ui import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner +import android.view.HapticFeedbackConstants import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.settings.ControlsSettingsDialogManager import com.android.systemui.controls.settings.FakeControlsSettingsRepository -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.policy.KeyguardStateController @@ -33,6 +35,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers +import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.anyBoolean @@ -68,8 +71,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { @Mock private lateinit var metricsLogger: ControlsMetricsLogger @Mock - private lateinit var featureFlags: FeatureFlags - @Mock private lateinit var controlsSettingsDialogManager: ControlsSettingsDialogManager companion object { @@ -82,6 +83,8 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { private lateinit var action: ControlActionCoordinatorImpl.Action private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository + private val featureFlags = FakeFeatureFlags() + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -101,6 +104,7 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { metricsLogger, vibratorHelper, controlsSettingsRepository, + featureFlags )) coordinator.activityContext = mContext @@ -194,4 +198,50 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { verify(coordinator).bouncerOrRun(action) verify(action, never()).invoke() } + + @Test + fun drag_isEdge_oneWayHapticsDisabled_usesVibrate() { + featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false) + + coordinator.drag(cvh, true) + + verify(vibratorHelper).vibrate( + Vibrations.rangeEdgeEffect + ) + } + + @Test + fun drag_isNotEdge_oneWayHapticsDisabled_usesVibrate() { + featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false) + + coordinator.drag(cvh, false) + + verify(vibratorHelper).vibrate( + Vibrations.rangeMiddleEffect + ) + } + + @Test + fun drag_isEdge_oneWayHapticsEnabled_usesPerformHapticFeedback() { + featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true) + + coordinator.drag(cvh, true) + + verify(vibratorHelper).performHapticFeedback( + any(), + eq(HapticFeedbackConstants.SEGMENT_TICK) + ) + } + + @Test + fun drag_isNotEdge_oneWayHapticsEnabled_usesPerformHapticFeedback() { + featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true) + + coordinator.drag(cvh, false) + + verify(vibratorHelper).performHapticFeedback( + any(), + eq(HapticFeedbackConstants.SEGMENT_FREQUENT_TICK) + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java index 07cb5d88a515..6a178895839b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java @@ -22,17 +22,14 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import android.os.RemoteException; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.internal.app.AssistUtils; -import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.systemui.SysuiTestCase; -import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; import com.android.systemui.shared.condition.Condition; import org.junit.Before; @@ -50,9 +47,7 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { @Mock Condition.Callback mCallback; @Mock - AssistUtils mAssistUtils; - @Mock - DreamOverlayStateController mDreamOverlayStateController; + AssistManager mAssistManager; @Mock CoroutineScope mScope; @@ -62,55 +57,34 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); - mAssistantAttentionCondition = - new AssistantAttentionCondition(mScope, mDreamOverlayStateController, mAssistUtils); + mAssistantAttentionCondition = new AssistantAttentionCondition(mScope, mAssistManager); // Adding a callback also starts the condition. mAssistantAttentionCondition.addCallback(mCallback); } @Test public void testEnableVisualQueryDetection() { - final ArgumentCaptor<DreamOverlayStateController.Callback> argumentCaptor = - ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); - verify(mDreamOverlayStateController).addCallback(argumentCaptor.capture()); - - when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); - argumentCaptor.getValue().onStateChanged(); - - verify(mAssistUtils).enableVisualQueryDetection(any()); + verify(mAssistManager).addVisualQueryAttentionListener( + any(VisualQueryAttentionListener.class)); } @Test public void testDisableVisualQueryDetection() { - final ArgumentCaptor<DreamOverlayStateController.Callback> argumentCaptor = - ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); - verify(mDreamOverlayStateController).addCallback(argumentCaptor.capture()); - - when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); - argumentCaptor.getValue().onStateChanged(); - when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(false); - argumentCaptor.getValue().onStateChanged(); - - verify(mAssistUtils).disableVisualQueryDetection(); + mAssistantAttentionCondition.stop(); + verify(mAssistManager).removeVisualQueryAttentionListener( + any(VisualQueryAttentionListener.class)); } @Test - public void testAttentionChangedTriggersCondition() throws RemoteException { - final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor = - ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); - verify(mDreamOverlayStateController).addCallback(callbackCaptor.capture()); - - when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); - callbackCaptor.getValue().onStateChanged(); - - final ArgumentCaptor<IVisualQueryDetectionAttentionListener> listenerCaptor = - ArgumentCaptor.forClass(IVisualQueryDetectionAttentionListener.class); - verify(mAssistUtils).enableVisualQueryDetection(listenerCaptor.capture()); + public void testAttentionChangedTriggersCondition() { + final ArgumentCaptor<VisualQueryAttentionListener> argumentCaptor = + ArgumentCaptor.forClass(VisualQueryAttentionListener.class); + verify(mAssistManager).addVisualQueryAttentionListener(argumentCaptor.capture()); - listenerCaptor.getValue().onAttentionGained(); + argumentCaptor.getValue().onAttentionGained(); assertThat(mAssistantAttentionCondition.isConditionMet()).isTrue(); - listenerCaptor.getValue().onAttentionLost(); + argumentCaptor.getValue().onAttentionLost(); assertThat(mAssistantAttentionCondition.isConditionMet()).isFalse(); verify(mCallback, times(2)).onConditionChanged(eq(mAssistantAttentionCondition)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt new file mode 100644 index 000000000000..632d149c9520 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt @@ -0,0 +1,147 @@ +/* + * 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.keyevent.domain.interactor + +import android.view.KeyEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.back.domain.interactor.BackActionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@SmallTest +@RunWith(AndroidJUnit4::class) +class KeyEventInteractorTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + private lateinit var keyguardInteractorWithDependencies: + KeyguardInteractorFactory.WithDependencies + @Mock private lateinit var keyguardKeyEventInteractor: KeyguardKeyEventInteractor + @Mock private lateinit var backActionInteractor: BackActionInteractor + + private lateinit var underTest: KeyEventInteractor + + @Before + fun setup() { + keyguardInteractorWithDependencies = KeyguardInteractorFactory.create() + underTest = + KeyEventInteractor( + backActionInteractor, + keyguardKeyEventInteractor, + ) + } + + @Test + fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() { + val backKeyEventActionDown = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK) + val backKeyEventActionUp = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK) + + // GIVEN back key ACTION_DOWN and ACTION_UP aren't handled by the keyguardKeyEventInteractor + whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionDown)) + .thenReturn(false) + whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionUp)) + .thenReturn(false) + + // WHEN back key event ACTION_DOWN, the event is handled even though back isn't requested + assertThat(underTest.dispatchKeyEvent(backKeyEventActionDown)).isTrue() + // THEN back event isn't handled on ACTION_DOWN + verify(backActionInteractor, never()).onBackRequested() + + // WHEN back key event ACTION_UP + assertThat(underTest.dispatchKeyEvent(backKeyEventActionUp)).isTrue() + // THEN back event is handled on ACTION_UP + verify(backActionInteractor).onBackRequested() + } + + @Test + fun dispatchKeyEvent_isNotHandledByKeyguardKeyEventInteractor() { + val keyEvent = + KeyEvent( + KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_SPACE, + ) + whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(false) + assertThat(underTest.dispatchKeyEvent(keyEvent)).isFalse() + } + + @Test + fun dispatchKeyEvent_handledByKeyguardKeyEventInteractor() { + val keyEvent = + KeyEvent( + KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_SPACE, + ) + whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(true) + assertThat(underTest.dispatchKeyEvent(keyEvent)).isTrue() + } + + @Test + fun interceptMediaKey_isNotHandledByKeyguardKeyEventInteractor() { + val keyEvent = + KeyEvent( + KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_SPACE, + ) + whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(false) + assertThat(underTest.interceptMediaKey(keyEvent)).isFalse() + } + + @Test + fun interceptMediaKey_handledByKeyguardKeyEventInteractor() { + val keyEvent = + KeyEvent( + KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_SPACE, + ) + whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(true) + assertThat(underTest.interceptMediaKey(keyEvent)).isTrue() + } + + @Test + fun dispatchKeyEventPreIme_isNotHandledByKeyguardKeyEventInteractor() { + val keyEvent = + KeyEvent( + KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_SPACE, + ) + whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(false) + assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isFalse() + } + + @Test + fun dispatchKeyEventPreIme_handledByKeyguardKeyEventInteractor() { + val keyEvent = + KeyEvent( + KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_SPACE, + ) + whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(true) + assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt index c6a2fa50b446..a6930d595326 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt @@ -36,6 +36,11 @@ import com.android.systemui.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController +import com.android.systemui.biometrics.data.repository.FaceSensorInfo +import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.shared.model.FingerprintSensorType +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.BiometricType.FACE @@ -63,6 +68,7 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.isNull import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -89,6 +95,8 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { ArgumentCaptor<IBiometricEnabledOnKeyguardCallback.Stub> private lateinit var userRepository: FakeUserRepository private lateinit var devicePostureRepository: FakeDevicePostureRepository + private lateinit var facePropertyRepository: FakeFacePropertyRepository + private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository private lateinit var testDispatcher: TestDispatcher private lateinit var testScope: TestScope @@ -102,6 +110,8 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testScope = TestScope(testDispatcher) userRepository = FakeUserRepository() devicePostureRepository = FakeDevicePostureRepository() + facePropertyRepository = FakeFacePropertyRepository() + fingerprintPropertyRepository = FakeFingerprintPropertyRepository() } private suspend fun createBiometricSettingsRepository() { @@ -120,74 +130,110 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { biometricManager = biometricManager, devicePostureRepository = devicePostureRepository, dumpManager = dumpManager, + facePropertyRepository = facePropertyRepository, + fingerprintPropertyRepository = fingerprintPropertyRepository, ) testScope.runCurrent() + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.STRONG, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture()) + verify(authController, atLeastOnce()).addCallback(authControllerCallback.capture()) } @Test fun fingerprintEnrollmentChange() = testScope.runTest { createBiometricSettingsRepository() - val fingerprintEnrolled = collectLastValue(underTest.isFingerprintEnrolled) + val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) runCurrent() - verify(authController).addCallback(authControllerCallback.capture()) whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true) enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(fingerprintEnrolled()).isTrue() + assertThat(fingerprintAllowed()).isTrue() whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(false) enrollmentChange(UNDER_DISPLAY_FINGERPRINT, ANOTHER_USER_ID, false) - assertThat(fingerprintEnrolled()).isTrue() + assertThat(fingerprintAllowed()).isTrue() enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, false) - assertThat(fingerprintEnrolled()).isFalse() + assertThat(fingerprintAllowed()).isFalse() } @Test fun strongBiometricAllowedChange() = testScope.runTest { + fingerprintIsEnrolled() + doNotDisableKeyguardAuthFeatures() createBiometricSettingsRepository() - val strongBiometricAllowed = collectLastValue(underTest.isStrongBiometricAllowed) + + val strongBiometricAllowed by + collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) runCurrent() onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) - assertThat(strongBiometricAllowed()).isTrue() + assertThat(strongBiometricAllowed).isTrue() onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_BOOT, PRIMARY_USER_ID) - assertThat(strongBiometricAllowed()).isFalse() + assertThat(strongBiometricAllowed).isFalse() } @Test fun convenienceBiometricAllowedChange() = testScope.runTest { overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false) + deviceIsInPostureThatSupportsFaceAuth() + faceAuthIsEnrolled() + faceAuthIsNonStrongBiometric() createBiometricSettingsRepository() - val convenienceBiometricAllowed = - collectLastValue(underTest.isNonStrongBiometricAllowed) - runCurrent() + val convenienceFaceAuthAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + doNotDisableKeyguardAuthFeatures() + faceAuthIsEnabledByBiometricManager() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(true, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isTrue() + runCurrent() + assertThat(convenienceFaceAuthAllowed).isTrue() onNonStrongAuthChanged(false, ANOTHER_USER_ID) - assertThat(convenienceBiometricAllowed()).isTrue() + assertThat(convenienceFaceAuthAllowed).isTrue() onNonStrongAuthChanged(false, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isFalse() + assertThat(convenienceFaceAuthAllowed).isFalse() mContext.orCreateTestableResources.removeOverride( com.android.internal.R.bool.config_strongAuthRequiredOnBoot ) } + private fun faceAuthIsNonStrongBiometric() { + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.CONVENIENCE)) + } + + private fun faceAuthIsStrongBiometric() { + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) + } + + private fun deviceIsInPostureThatSupportsFaceAuth() { + overrideResource( + R.integer.config_face_auth_supported_posture, + DevicePostureController.DEVICE_POSTURE_FLIPPED + ) + devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED) + } + @Test fun whenStrongAuthRequiredAfterBoot_nonStrongBiometricNotAllowed() = testScope.runTest { overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, true) createBiometricSettingsRepository() + faceAuthIsNonStrongBiometric() + faceAuthIsEnrolled() + doNotDisableKeyguardAuthFeatures() - val convenienceBiometricAllowed = - collectLastValue(underTest.isNonStrongBiometricAllowed) + val convenienceBiometricAllowed = collectLastValue(underTest.isFaceAuthCurrentlyAllowed) runCurrent() onNonStrongAuthChanged(true, PRIMARY_USER_ID) @@ -201,16 +247,24 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun whenStrongBiometricAuthIsNotAllowed_nonStrongBiometrics_alsoNotAllowed() = testScope.runTest { overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false) + faceAuthIsNonStrongBiometric() + deviceIsInPostureThatSupportsFaceAuth() + faceAuthIsEnrolled() createBiometricSettingsRepository() - - val convenienceBiometricAllowed = - collectLastValue(underTest.isNonStrongBiometricAllowed) + doNotDisableKeyguardAuthFeatures() + faceAuthIsEnabledByBiometricManager() runCurrent() + + val convenienceBiometricAllowed by + collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(true, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isTrue() + runCurrent() + assertThat(convenienceBiometricAllowed).isTrue() onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, PRIMARY_USER_ID) - assertThat(convenienceBiometricAllowed()).isFalse() + assertThat(convenienceBiometricAllowed).isFalse() mContext.orCreateTestableResources.removeOverride( com.android.internal.R.bool.config_strongAuthRequiredOnBoot ) @@ -229,9 +283,11 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { @Test fun fingerprintDisabledByDpmChange() = testScope.runTest { + fingerprintIsEnrolled(PRIMARY_USER_ID) createBiometricSettingsRepository() + val fingerprintEnabledByDevicePolicy = - collectLastValue(underTest.isFingerprintEnabledByDevicePolicy) + collectLastValue(underTest.isFingerprintEnrolledAndEnabled) runCurrent() whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())) @@ -244,43 +300,57 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(fingerprintEnabledByDevicePolicy()).isTrue() } + private fun fingerprintIsEnrolled(userId: Int = PRIMARY_USER_ID) { + whenever(authController.isFingerprintEnrolled(userId)).thenReturn(true) + } + @Test fun faceEnrollmentChangeIsPropagatedForTheCurrentUser() = testScope.runTest { createBiometricSettingsRepository() + faceAuthIsEnabledByBiometricManager() + + doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) + runCurrent() clearInvocations(authController) whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false) - val faceEnrolled = collectLastValue(underTest.isFaceEnrolled) + val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() verify(authController).addCallback(authControllerCallback.capture()) enrollmentChange(REAR_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() enrollmentChange(SIDE_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) enrollmentChange(FACE, ANOTHER_USER_ID, true) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() - whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(true) + faceAuthIsEnrolled() enrollmentChange(FACE, PRIMARY_USER_ID, true) - assertThat(faceEnrolled()).isTrue() + assertThat(faceAuthAllowed()).isTrue() } + private fun faceAuthIsEnabledByBiometricManager(userId: Int = PRIMARY_USER_ID) { + verify(biometricManager, atLeastOnce()) + .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) + biometricManagerCallback.value.onChanged(true, userId) + } + @Test fun faceEnrollmentStatusOfNewUserUponUserSwitch() = testScope.runTest { @@ -290,21 +360,26 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false) whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) - val faceEnrolled = collectLastValue(underTest.isFaceEnrolled) + val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - assertThat(faceEnrolled()).isFalse() + assertThat(faceAuthAllowed()).isFalse() } @Test fun faceEnrollmentChangesArePropagatedAfterUserSwitch() = testScope.runTest { createBiometricSettingsRepository() + verify(biometricManager) + .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) userRepository.setSelectedUserInfo(ANOTHER_USER) + doNotDisableKeyguardAuthFeatures(ANOTHER_USER_ID) + biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) + runCurrent() clearInvocations(authController) - val faceEnrolled = collectLastValue(underTest.isFaceEnrolled) + val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) runCurrent() verify(authController).addCallback(authControllerCallback.capture()) @@ -312,12 +387,14 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) enrollmentChange(FACE, ANOTHER_USER_ID, true) - assertThat(faceEnrolled()).isTrue() + assertThat(faceAuthAllowed()).isTrue() } @Test fun devicePolicyControlsFaceAuthenticationEnabledState() = testScope.runTest { + faceAuthIsEnrolled() + createBiometricSettingsRepository() verify(biometricManager) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) @@ -325,62 +402,70 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID))) .thenReturn(KEYGUARD_DISABLE_FINGERPRINT or KEYGUARD_DISABLE_FACE) - val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled) + val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) runCurrent() broadcastDPMStateChange() - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID) runCurrent() - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID))) .thenReturn(KEYGUARD_DISABLE_FINGERPRINT) broadcastDPMStateChange() - assertThat(isFaceAuthEnabled()).isTrue() + assertThat(isFaceAuthAllowed()).isTrue() } @Test fun biometricManagerControlsFaceAuthenticationEnabledStatus() = testScope.runTest { + faceAuthIsEnrolled() + createBiometricSettingsRepository() verify(biometricManager) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID))) .thenReturn(0) broadcastDPMStateChange() - val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled) + val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() // Value changes for another user biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) - assertThat(isFaceAuthEnabled()).isFalse() + assertThat(isFaceAuthAllowed()).isFalse() // Value changes for current user. biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID) - assertThat(isFaceAuthEnabled()).isTrue() + assertThat(isFaceAuthAllowed()).isTrue() } + private fun faceAuthIsEnrolled(userId: Int = PRIMARY_USER_ID) { + whenever(authController.isFaceAuthEnrolled(userId)).thenReturn(true) + } + @Test fun userChange_biometricEnabledChange_handlesRaceCondition() = testScope.runTest { createBiometricSettingsRepository() + whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) + verify(biometricManager) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) - val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled) + val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) runCurrent() userRepository.setSelectedUserInfo(ANOTHER_USER) runCurrent() - assertThat(isFaceAuthEnabled()).isTrue() + assertThat(isFaceAuthAllowed()).isTrue() } @Test @@ -388,9 +473,9 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testScope.runTest { createBiometricSettingsRepository() - collectLastValue(underTest.isFaceAuthenticationEnabled)() - collectLastValue(underTest.isFaceAuthenticationEnabled)() - collectLastValue(underTest.isFaceAuthenticationEnabled)() + collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)() + collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)() + collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)() verify(biometricManager, times(1)).registerEnabledOnKeyguardCallback(any()) } @@ -495,10 +580,138 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(authFlags()!!.flag).isEqualTo(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) } + @Test + fun faceAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFaceIsClass3() = + testScope.runTest { + createBiometricSettingsRepository() + val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + + faceAuthIsEnrolled() + deviceIsInPostureThatSupportsFaceAuth() + doNotDisableKeyguardAuthFeatures() + faceAuthIsStrongBiometric() + faceAuthIsEnabledByBiometricManager() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isTrue() + + onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isFalse() + } + + @Test + fun faceAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFaceIsNotStrong() = + testScope.runTest { + createBiometricSettingsRepository() + val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed) + + faceAuthIsEnrolled() + deviceIsInPostureThatSupportsFaceAuth() + doNotDisableKeyguardAuthFeatures() + faceAuthIsNonStrongBiometric() + faceAuthIsEnabledByBiometricManager() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isFalse() + + onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isFalse() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFaceAuthCurrentlyAllowed).isTrue() + } + + @Test + fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() = + testScope.runTest { + createBiometricSettingsRepository() + val isFingerprintCurrentlyAllowed by + collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) + + fingerprintIsEnrolled(PRIMARY_USER_ID) + enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) + doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) + runCurrent() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.STRONG, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + // Non strong auth is not allowed now, FP is marked strong + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFingerprintCurrentlyAllowed).isTrue() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.CONVENIENCE, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + assertThat(isFingerprintCurrentlyAllowed).isFalse() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.WEAK, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + assertThat(isFingerprintCurrentlyAllowed).isFalse() + } + + @Test + fun fpAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFpIsStrong() = + testScope.runTest { + createBiometricSettingsRepository() + val isFingerprintCurrentlyAllowed by + collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) + + fingerprintIsEnrolled(PRIMARY_USER_ID) + enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) + doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) + runCurrent() + + fingerprintPropertyRepository.setProperties( + 1, + SensorStrength.STRONG, + FingerprintSensorType.UDFPS_OPTICAL, + emptyMap() + ) + // Non strong auth is not allowed now, FP is marked strong + onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID) + onNonStrongAuthChanged(true, PRIMARY_USER_ID) + + assertThat(isFingerprintCurrentlyAllowed).isFalse() + + onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) + onNonStrongAuthChanged(false, PRIMARY_USER_ID) + + assertThat(isFingerprintCurrentlyAllowed).isTrue() + } + private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) { authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled) } + private fun doNotDisableKeyguardAuthFeatures(userId: Int = PRIMARY_USER_ID) { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(userId))) + .thenReturn(0) + broadcastDPMStateChange() + } + private fun broadcastDPMStateChange() { fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index fe5b8120428d..64b94707da57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -40,9 +40,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.data.repository.FaceSensorInfo import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository -import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.coroutines.FlowValue @@ -255,7 +253,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { faceAuthBuffer, keyguardTransitionInteractor, featureFlags, - fakeFacePropertyRepository, dumpManager, ) } @@ -525,15 +522,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun authenticateDoesNotRunIfFaceIsNotEnrolled() = + fun authenticateDoesNotRunIfFaceIsNotUsuallyAllowed() = testScope.runTest { - testGatingCheckForFaceAuth { biometricSettingsRepository.setFaceEnrolled(false) } - } - - @Test - fun authenticateDoesNotRunIfFaceIsNotEnabled() = - testScope.runTest { - testGatingCheckForFaceAuth { biometricSettingsRepository.setIsFaceAuthEnabled(false) } + testGatingCheckForFaceAuth { + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + } } @Test @@ -589,21 +582,10 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun authenticateDoesNotRunWhenNonStrongBiometricIsNotAllowed() = + fun authenticateDoesNotRunWhenFaceAuthIsNotCurrentlyAllowedToRun() = testScope.runTest { testGatingCheckForFaceAuth { - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) - } - } - - @Test - fun authenticateDoesNotRunWhenStrongBiometricIsNotAllowedAndFaceSensorIsStrong() = - testScope.runTest { - fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) - runCurrent() - - testGatingCheckForFaceAuth(isFaceStrong = true) { - biometricSettingsRepository.setIsStrongBiometricAllowed(false) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false) } } @@ -662,7 +644,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { allPreconditionsToRunFaceAuthAreTrue() // Flip one precondition to false. - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false) assertThat(canFaceAuthRun()).isFalse() underTest.authenticate( FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, @@ -827,15 +809,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun detectDoesNotRunWhenFaceIsNotEnrolled() = - testScope.runTest { - testGatingCheckForDetect { biometricSettingsRepository.setFaceEnrolled(false) } - } - - @Test - fun detectDoesNotRunWhenFaceIsNotEnabled() = + fun detectDoesNotRunWhenFaceIsNotUsuallyAllowed() = testScope.runTest { - testGatingCheckForDetect { biometricSettingsRepository.setIsFaceAuthEnabled(false) } + testGatingCheckForDetect { + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + } } @Test @@ -932,23 +910,10 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun detectDoesNotRunWhenNonStrongBiometricIsAllowed() = + fun detectDoesNotRunWhenFaceAuthIsCurrentlyAllowedToRun() = testScope.runTest { testGatingCheckForDetect { - biometricSettingsRepository.setIsNonStrongBiometricAllowed(true) - } - } - - @Test - fun detectDoesNotRunWhenStrongBiometricIsAllowedAndFaceAuthSensorStrengthIsStrong() = - testScope.runTest { - fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) - runCurrent() - - testGatingCheckForDetect(isFaceStrong = true) { - biometricSettingsRepository.setIsStrongBiometricAllowed(true) - // this shouldn't matter as face is set as a strong sensor - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true) } } @@ -1043,12 +1008,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { faceAuthenticateIsCalled() } - private suspend fun TestScope.testGatingCheckForFaceAuth( - isFaceStrong: Boolean = false, - gatingCheckModifier: () -> Unit - ) { + private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) { initCollectors() - allPreconditionsToRunFaceAuthAreTrue(isFaceStrong) + allPreconditionsToRunFaceAuthAreTrue() gatingCheckModifier() runCurrent() @@ -1057,7 +1019,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(underTest.canRunFaceAuth.value).isFalse() // flip the gating check back on. - allPreconditionsToRunFaceAuthAreTrue(isFaceStrong) + allPreconditionsToRunFaceAuthAreTrue() triggerFaceAuth(false) @@ -1076,19 +1038,12 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { faceAuthenticateIsNotCalled() } - private suspend fun TestScope.testGatingCheckForDetect( - isFaceStrong: Boolean = false, - gatingCheckModifier: () -> Unit - ) { + private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) { initCollectors() allPreconditionsToRunFaceAuthAreTrue() - if (isFaceStrong) { - biometricSettingsRepository.setStrongBiometricAllowed(false) - } else { - // This will stop face auth from running but is required to be false for detect. - biometricSettingsRepository.setIsNonStrongBiometricAllowed(false) - } + // This will stop face auth from running but is required to be false for detect. + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false) runCurrent() assertThat(canFaceAuthRun()).isFalse() @@ -1123,13 +1078,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true } } - private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue( - isFaceStrong: Boolean = false - ) { + private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() { verify(faceManager, atLeastOnce()) .addLockoutResetCallback(faceLockoutResetCallback.capture()) - biometricSettingsRepository.setFaceEnrolled(true) - biometricSettingsRepository.setIsFaceAuthEnabled(true) underTest.resumeFaceAuth() trustRepository.setCurrentUserTrusted(false) keyguardRepository.setKeyguardGoingAway(false) @@ -1140,14 +1091,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { WakeSleepReason.OTHER ) ) - if (isFaceStrong) { - biometricSettingsRepository.setStrongBiometricAllowed(true) - } else { - biometricSettingsRepository.setIsNonStrongBiometricAllowed(true) - } + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) + biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true) + biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true) biometricSettingsRepository.setIsUserInLockdown(false) fakeUserRepository.setSelectedUserInfo(primaryUser) - biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true) faceLockoutResetCallback.value.onLockoutReset(0) bouncerRepository.setAlternateVisible(true) keyguardRepository.setKeyguardShowing(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt index dcaafe8dd052..6fcf54c0e54a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.keyguard.data.repository import android.graphics.Point -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase @@ -47,7 +47,7 @@ import org.mockito.MockitoAnnotations @SmallTest @RoboPilotTest @OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class LightRevealScrimRepositoryTest : SysuiTestCase() { private lateinit var fakeKeyguardRepository: FakeKeyguardRepository private lateinit var underTest: LightRevealScrimRepositoryImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt new file mode 100644 index 000000000000..e0ae0c359f07 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt @@ -0,0 +1,297 @@ +/* + * 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.keyguard.domain.interactor + +import android.media.AudioManager +import android.media.session.MediaSessionLegacyHelper +import android.view.KeyEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.back.domain.interactor.BackActionInteractor +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState +import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.ShadeController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@SmallTest +@RunWith(AndroidJUnit4::class) +class KeyguardKeyEventInteractorTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + private val actionDownVolumeDownKeyEvent = + KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN) + private val actionDownVolumeUpKeyEvent = + KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP) + private val backKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK) + private val awakeWakefulnessMode = + WakefulnessModel(WakefulnessState.AWAKE, WakeSleepReason.OTHER, WakeSleepReason.OTHER) + private val asleepWakefulnessMode = + WakefulnessModel(WakefulnessState.ASLEEP, WakeSleepReason.OTHER, WakeSleepReason.OTHER) + + private lateinit var keyguardInteractorWithDependencies: + KeyguardInteractorFactory.WithDependencies + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var shadeController: ShadeController + @Mock private lateinit var mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper + @Mock private lateinit var mediaSessionLegacyHelper: MediaSessionLegacyHelper + @Mock private lateinit var backActionInteractor: BackActionInteractor + + private lateinit var underTest: KeyguardKeyEventInteractor + + @Before + fun setup() { + whenever(mediaSessionLegacyHelperWrapper.getHelper(any())) + .thenReturn(mediaSessionLegacyHelper) + keyguardInteractorWithDependencies = KeyguardInteractorFactory.create() + underTest = + KeyguardKeyEventInteractor( + context, + statusBarStateController, + keyguardInteractorWithDependencies.keyguardInteractor, + statusBarKeyguardViewManager, + shadeController, + mediaSessionLegacyHelperWrapper, + backActionInteractor, + ) + } + + @Test + fun dispatchKeyEvent_volumeKey_dozing_handlesEvents() { + whenever(statusBarStateController.isDozing).thenReturn(true) + + assertThat(underTest.dispatchKeyEvent(actionDownVolumeDownKeyEvent)).isTrue() + verify(mediaSessionLegacyHelper) + .sendVolumeKeyEvent( + eq(actionDownVolumeDownKeyEvent), + eq(AudioManager.USE_DEFAULT_STREAM_TYPE), + eq(true) + ) + + assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isTrue() + verify(mediaSessionLegacyHelper) + .sendVolumeKeyEvent( + eq(actionDownVolumeUpKeyEvent), + eq(AudioManager.USE_DEFAULT_STREAM_TYPE), + eq(true) + ) + } + + @Test + fun dispatchKeyEvent_volumeKey_notDozing_doesNotHandleEvents() { + whenever(statusBarStateController.isDozing).thenReturn(false) + + assertThat(underTest.dispatchKeyEvent(actionDownVolumeDownKeyEvent)).isFalse() + verify(mediaSessionLegacyHelper, never()) + .sendVolumeKeyEvent( + eq(actionDownVolumeDownKeyEvent), + eq(AudioManager.USE_DEFAULT_STREAM_TYPE), + eq(true) + ) + + assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isFalse() + verify(mediaSessionLegacyHelper, never()) + .sendVolumeKeyEvent( + eq(actionDownVolumeUpKeyEvent), + eq(AudioManager.USE_DEFAULT_STREAM_TYPE), + eq(true) + ) + } + + @Test + fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_showsPrimaryBouncer() { + keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) + + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU) + } + + @Test + fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() { + keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode) + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) + + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU) + } + + @Test + fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_doNothing() { + keyguardInteractorWithDependencies.repository.setWakefulnessModel(asleepWakefulnessMode) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) + + verifyActionsDoNothing(KeyEvent.KEYCODE_MENU) + } + + @Test + fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_showsPrimaryBouncer() { + keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE) + } + + @Test + fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() { + keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode) + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE) + } + + @Test + fun dispatchKeyEvent_enterActionUp_interactiveKeyguard_showsPrimaryBouncer() { + keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER) + } + + @Test + fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() { + keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode) + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER) + } + + @Test + fun dispatchKeyEventPreIme_back_keyguard_onBackRequested() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(true) + + whenever(backActionInteractor.onBackRequested()).thenReturn(false) + assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse() + verify(backActionInteractor).onBackRequested() + clearInvocations(backActionInteractor) + + whenever(backActionInteractor.onBackRequested()).thenReturn(true) + assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isTrue() + verify(backActionInteractor).onBackRequested() + } + + @Test + fun dispatchKeyEventPreIme_back_keyguard_SBKVMdoesNotHandle_neverOnBackRequested() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(false) + whenever(backActionInteractor.onBackRequested()).thenReturn(true) + + assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse() + verify(backActionInteractor, never()).onBackRequested() + } + + @Test + fun dispatchKeyEventPreIme_back_shade_neverOnBackRequested() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE) + whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(true) + whenever(backActionInteractor.onBackRequested()).thenReturn(true) + + assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse() + verify(backActionInteractor, never()).onBackRequested() + } + + @Test + fun interceptMediaKey_keyguard_SBKVMdoesNotHandle_doesNotHandleMediaKey() { + val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.interceptMediaKey(eq(keyEvent))).thenReturn(false) + + assertThat(underTest.interceptMediaKey(keyEvent)).isFalse() + verify(statusBarKeyguardViewManager).interceptMediaKey(eq(keyEvent)) + } + + @Test + fun interceptMediaKey_keyguard_handleMediaKey() { + val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.interceptMediaKey(eq(keyEvent))).thenReturn(true) + + assertThat(underTest.interceptMediaKey(keyEvent)).isTrue() + verify(statusBarKeyguardViewManager).interceptMediaKey(eq(keyEvent)) + } + + @Test + fun interceptMediaKey_shade_doesNotHandleMediaKey() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE) + + assertThat( + underTest.interceptMediaKey( + KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP) + ) + ) + .isFalse() + verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any()) + } + + private fun verifyActionUpCollapsesTheShade(keycode: Int) { + // action down: does NOT collapse the shade + val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) + assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() + verify(shadeController, never()).animateCollapseShadeForced() + + // action up: collapses the shade + val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) + assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() + verify(shadeController).animateCollapseShadeForced() + } + + private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) { + // action down: does NOT collapse the shade + val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) + assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() + verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + + // action up: collapses the shade + val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) + assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() + verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true)) + } + + private fun verifyActionsDoNothing(keycode: Int) { + // action down: does nothing + val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) + assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() + verify(shadeController, never()).animateCollapseShadeForced() + verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + + // action up: doesNothing + val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) + assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse() + verify(shadeController, never()).animateCollapseShadeForced() + verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 4b221a0e81bf..ca93246e8d9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.domain.interactor +import android.app.StatusBarManager import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN @@ -24,6 +25,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel @@ -71,6 +73,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var bouncerRepository: FakeKeyguardBouncerRepository + private lateinit var commandQueue: FakeCommandQueue private lateinit var shadeRepository: ShadeRepository private lateinit var transitionRepository: FakeKeyguardTransitionRepository private lateinit var transitionInteractor: KeyguardTransitionInteractor @@ -99,6 +102,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { keyguardRepository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() + commandQueue = FakeCommandQueue() shadeRepository = FakeShadeRepository() transitionRepository = spy(FakeKeyguardTransitionRepository()) @@ -1152,6 +1156,39 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { coroutineContext.cancelChildren() } + @Test + fun lockscreenToOccluded_fromCameraGesture() = + testScope.runTest { + // GIVEN a prior transition has run to LOCKSCREEN + runTransition(KeyguardState.AOD, KeyguardState.LOCKSCREEN) + runCurrent() + + // WHEN the device begins to sleep (first power button press)... + keyguardRepository.setWakefulnessModel(startingToSleep()) + runCurrent() + reset(transitionRepository) + + // ...AND WHEN the camera gesture is detected quickly afterwards + commandQueue.doForEachCallback { + it.onCameraLaunchGestureDetected( + StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP + ) + } + runCurrent() + + // THEN a transition from LOCKSCREEN => OCCLUDED should occur + val info = + withArgCaptor<TransitionInfo> { + verify(transitionRepository).startTransition(capture(), anyBoolean()) + } + assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") + assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN) + assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED) + assertThat(info.animator).isNotNull() + + coroutineContext.cancelChildren() + } + private fun startingToWake() = WakefulnessModel( WakefulnessState.STARTING_TO_WAKE, @@ -1170,6 +1207,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { return KeyguardInteractorFactory.create( featureFlags = featureFlags, repository = keyguardRepository, + commandQueue = commandQueue, bouncerRepository = bouncerRepository, ) .keyguardInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index ef38d54dc42f..3576ec9601e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -281,11 +281,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { underTest.onPreviewSlotSelected( KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START ) - keyguardInteractor.previewMode.value = - KeyguardInteractor.PreviewMode( - isInPreviewMode = true, - shouldHighlightSelectedAffordance = true, - ) + underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true) repository.setKeyguardShowing(false) val latest = collectLastValue(underTest.startButton) @@ -330,11 +326,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { underTest.onPreviewSlotSelected( KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START ) - keyguardInteractor.previewMode.value = - KeyguardInteractor.PreviewMode( - isInPreviewMode = true, - shouldHighlightSelectedAffordance = true, - ) + underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true) repository.setKeyguardShowing(false) val endButton = collectLastValue(underTest.endButton) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 05e933b9d572..a14a1c536f87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -45,7 +45,6 @@ class KeyguardRootViewModelTest : SysuiTestCase() { private lateinit var testScope: TestScope private lateinit var repository: FakeKeyguardRepository private lateinit var keyguardInteractor: KeyguardInteractor - @Mock private lateinit var keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel @Before fun setUp() { @@ -63,10 +62,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() { keyguardInteractor = withDeps.keyguardInteractor repository = withDeps.repository - underTest = KeyguardRootViewModel( - keyguardInteractor, - keyguardQuickAffordancesCombinedViewModel, - ) + underTest = KeyguardRootViewModel(keyguardInteractor) } @Test @@ -89,10 +85,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() { fun alpha_inPreviewMode_doesNotChange() = testScope.runTest { val value = collectLastValue(underTest.alpha) - underTest.enablePreviewMode( - initiallySelectedSlotId = null, - shouldHighlightSelectedAffordance = false, - ) + underTest.enablePreviewMode() Truth.assertThat(value()).isEqualTo(1f) repository.setKeyguardAlpha(0.1f) @@ -104,4 +97,4 @@ class KeyguardRootViewModelTest : SysuiTestCase() { repository.setKeyguardAlpha(0f) Truth.assertThat(value()).isEqualTo(1f) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 9781baae2f89..64d3b822f13c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -53,7 +53,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.nano.SystemUIProtoDump; import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.PluginManager; @@ -65,6 +64,7 @@ import com.android.systemui.qs.external.CustomTileStatePersister; import com.android.systemui.qs.external.TileLifecycleManager; import com.android.systemui.qs.external.TileServiceKey; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserFileManager; import com.android.systemui.settings.UserTracker; @@ -131,6 +131,8 @@ public class QSTileHostTest extends SysuiTestCase { private FakeFeatureFlags mFeatureFlags; + private QSPipelineFlagsRepository mQSPipelineFlagsRepository; + private FakeExecutor mMainExecutor; private QSTileHost mQSTileHost; @@ -142,6 +144,7 @@ public class QSTileHostTest extends SysuiTestCase { mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false); mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false); + mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(mFeatureFlags); mMainExecutor = new FakeExecutor(new FakeSystemClock()); @@ -164,7 +167,7 @@ public class QSTileHostTest extends SysuiTestCase { mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor, mPluginManager, mTunerService, () -> mAutoTiles, mShadeController, mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister, - mTileLifecycleManagerFactory, mUserFileManager, mFeatureFlags); + mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository); mMainExecutor.runAllReady(); mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) { @@ -708,7 +711,7 @@ public class QSTileHostTest extends SysuiTestCase { UserTracker userTracker, SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, TileLifecycleManager.Factory tileLifecycleManagerFactory, - UserFileManager userFileManager, FeatureFlags featureFlags) { + UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) { super(context, defaultFactory, mainExecutor, pluginManager, tunerService, autoTiles, shadeController, qsLogger, userTracker, secureSettings, customTileStatePersister, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 66895145b5b9..54a93608a10d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesCompon import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.domain.model.TileModel +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger import com.android.systemui.qs.toProto @@ -79,6 +80,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private val customTileAddedRepository: CustomTileAddedRepository = FakeCustomTileAddedRepository() private val featureFlags = FakeFeatureFlags() + private val pipelineFlags = QSPipelineFlagsRepository(featureFlags) private val tileLifecycleManagerFactory = TLMFactory() @Mock private lateinit var customTileStatePersister: CustomTileStatePersister @@ -120,7 +122,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { backgroundDispatcher = testDispatcher, scope = testScope.backgroundScope, logger = logger, - featureFlags = featureFlags, + featureFlags = pipelineFlags, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt new file mode 100644 index 000000000000..489221e86d0b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt @@ -0,0 +1,61 @@ +package com.android.systemui.qs.pipeline.shared + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameter +import org.junit.runners.Parameterized.Parameters + +@SmallTest +@RunWith(Parameterized::class) +class QSPipelineFlagsRepositoryTest : SysuiTestCase() { + companion object { + @Parameters( + name = + """ +WHEN: qs_pipeline_new_host = {0}, qs_pipeline_auto_add = {1} +THEN: pipelineNewHost = {2}, pipelineAutoAdd = {3} + """ + ) + @JvmStatic + fun data(): List<Array<Boolean>> = + (0 until 4).map { combination -> + val qs_pipeline_new_host = combination and 0b10 != 0 + val qs_pipeline_auto_add = combination and 0b01 != 0 + arrayOf( + qs_pipeline_new_host, + qs_pipeline_auto_add, + /* pipelineNewHost = */ qs_pipeline_new_host, + /* pipelineAutoAdd = */ qs_pipeline_new_host && qs_pipeline_auto_add, + ) + } + } + + @JvmField @Parameter(0) var qsPipelineNewHostFlag: Boolean = false + @JvmField @Parameter(1) var qsPipelineAutoAddFlag: Boolean = false + @JvmField @Parameter(2) var pipelineNewHostExpected: Boolean = false + @JvmField @Parameter(3) var pipelineAutoAddExpected: Boolean = false + + private val featureFlags = FakeFeatureFlags() + private val pipelineFlags = QSPipelineFlagsRepository(featureFlags) + + @Before + fun setUp() { + featureFlags.apply { + set(Flags.QS_PIPELINE_NEW_HOST, qsPipelineNewHostFlag) + set(Flags.QS_PIPELINE_AUTO_ADD, qsPipelineAutoAddFlag) + } + } + + @Test + fun flagLogic() { + assertThat(pipelineFlags.pipelineHostEnabled).isEqualTo(pipelineNewHostExpected) + assertThat(pipelineFlags.pipelineAutoAddEnabled).isEqualTo(pipelineAutoAddExpected) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 16cc924b5754..713c6027d642 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -147,16 +147,4 @@ class SceneInteractorTest : SysuiTestCase() { underTest.setVisible(true, "reason") assertThat(isVisible).isTrue() } - - @Test - fun remoteUserInput() = - testScope.runTest { - val remoteUserInput by collectLastValue(underTest.remoteUserInput) - assertThat(remoteUserInput).isNull() - - for (input in SceneTestUtils.REMOTE_INPUT_DOWN_GESTURE) { - underTest.onRemoteUserInput(input) - assertThat(remoteUserInput).isEqualTo(input) - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt index da6c42694666..88abb642f7c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt @@ -18,19 +18,14 @@ package com.android.systemui.scene.ui.viewmodel -import android.view.MotionEvent import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.model.RemoteUserInput -import com.android.systemui.scene.shared.model.RemoteUserInputAction import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.currentTime import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -73,35 +68,4 @@ class SceneContainerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade)) } - - @Test - fun onRemoteUserInput() = runTest { - val remoteUserInput by collectLastValue(underTest.remoteUserInput) - assertThat(remoteUserInput).isNull() - - val inputs = - SceneTestUtils.REMOTE_INPUT_DOWN_GESTURE.map { remoteUserInputToMotionEvent(it) } - - inputs.forEachIndexed { index, input -> - underTest.onRemoteUserInput(input) - assertThat(remoteUserInput).isEqualTo(SceneTestUtils.REMOTE_INPUT_DOWN_GESTURE[index]) - } - } - - private fun TestScope.remoteUserInputToMotionEvent(input: RemoteUserInput): MotionEvent { - return MotionEvent.obtain( - currentTime, - currentTime, - when (input.action) { - RemoteUserInputAction.DOWN -> MotionEvent.ACTION_DOWN - RemoteUserInputAction.MOVE -> MotionEvent.ACTION_MOVE - RemoteUserInputAction.UP -> MotionEvent.ACTION_UP - RemoteUserInputAction.CANCEL -> MotionEvent.ACTION_CANCEL - RemoteUserInputAction.UNKNOWN -> MotionEvent.ACTION_OUTSIDE - }, - input.x, - input.y, - 0 - ) - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt index 2d3ee0e5cff9..ca4486b533ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt @@ -20,12 +20,13 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.net.Uri -import androidx.test.ext.truth.content.IntentSubject.assertThat +import androidx.test.ext.truth.content.IntentSubject.assertThat as assertThatIntent import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import org.junit.Test import org.mockito.Mockito.`when` as whenever @@ -39,23 +40,23 @@ class ActionIntentCreatorTest : SysuiTestCase() { val output = ActionIntentCreator.createShare(uri) - assertThat(output).hasAction(Intent.ACTION_CHOOSER) - assertThat(output) + assertThatIntent(output).hasAction(Intent.ACTION_CHOOSER) + assertThatIntent(output) .hasFlags( Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION ) - assertThat(output).extras().parcelable<Intent>(Intent.EXTRA_INTENT).isNotNull() + assertThatIntent(output).extras().parcelable<Intent>(Intent.EXTRA_INTENT).isNotNull() val wrappedIntent = output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) - assertThat(wrappedIntent).hasAction(Intent.ACTION_SEND) - assertThat(wrappedIntent).hasData(uri) - assertThat(wrappedIntent).hasType("image/png") - assertThat(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_SUBJECT) - assertThat(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_TEXT) - assertThat(wrappedIntent).extras().parcelable<Uri>(Intent.EXTRA_STREAM).isEqualTo(uri) + assertThatIntent(wrappedIntent).hasAction(Intent.ACTION_SEND) + assertThatIntent(wrappedIntent).hasData(uri) + assertThatIntent(wrappedIntent).hasType("image/png") + assertThatIntent(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_SUBJECT) + assertThatIntent(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_TEXT) + assertThatIntent(wrappedIntent).extras().parcelable<Uri>(Intent.EXTRA_STREAM).isEqualTo(uri) } @Test @@ -64,7 +65,7 @@ class ActionIntentCreatorTest : SysuiTestCase() { val output = ActionIntentCreator.createShare(uri) - assertThat(output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)) + assertThatIntent(output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)) .hasData(Uri.parse("content://fake")) } @@ -75,8 +76,8 @@ class ActionIntentCreatorTest : SysuiTestCase() { val output = ActionIntentCreator.createShareWithSubject(uri, subject) - assertThat(output).hasAction(Intent.ACTION_CHOOSER) - assertThat(output) + assertThatIntent(output).hasAction(Intent.ACTION_CHOOSER) + assertThatIntent(output) .hasFlags( Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or @@ -84,12 +85,12 @@ class ActionIntentCreatorTest : SysuiTestCase() { ) val wrappedIntent = output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) - assertThat(wrappedIntent).hasAction(Intent.ACTION_SEND) - assertThat(wrappedIntent).hasData(uri) - assertThat(wrappedIntent).hasType("image/png") - assertThat(wrappedIntent).extras().string(Intent.EXTRA_SUBJECT).isEqualTo(subject) - assertThat(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_TEXT) - assertThat(wrappedIntent).extras().parcelable<Uri>(Intent.EXTRA_STREAM).isEqualTo(uri) + assertThatIntent(wrappedIntent).hasAction(Intent.ACTION_SEND) + assertThatIntent(wrappedIntent).hasData(uri) + assertThatIntent(wrappedIntent).hasType("image/png") + assertThatIntent(wrappedIntent).extras().string(Intent.EXTRA_SUBJECT).isEqualTo(subject) + assertThatIntent(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_TEXT) + assertThatIntent(wrappedIntent).extras().parcelable<Uri>(Intent.EXTRA_STREAM).isEqualTo(uri) } @Test @@ -99,8 +100,8 @@ class ActionIntentCreatorTest : SysuiTestCase() { val output = ActionIntentCreator.createShareWithText(uri, extraText) - assertThat(output).hasAction(Intent.ACTION_CHOOSER) - assertThat(output) + assertThatIntent(output).hasAction(Intent.ACTION_CHOOSER) + assertThatIntent(output) .hasFlags( Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or @@ -108,12 +109,12 @@ class ActionIntentCreatorTest : SysuiTestCase() { ) val wrappedIntent = output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) - assertThat(wrappedIntent).hasAction(Intent.ACTION_SEND) - assertThat(wrappedIntent).hasData(uri) - assertThat(wrappedIntent).hasType("image/png") - assertThat(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_SUBJECT) - assertThat(wrappedIntent).extras().string(Intent.EXTRA_TEXT).isEqualTo(extraText) - assertThat(wrappedIntent).extras().parcelable<Uri>(Intent.EXTRA_STREAM).isEqualTo(uri) + assertThatIntent(wrappedIntent).hasAction(Intent.ACTION_SEND) + assertThatIntent(wrappedIntent).hasData(uri) + assertThatIntent(wrappedIntent).hasType("image/png") + assertThatIntent(wrappedIntent).extras().doesNotContainKey(Intent.EXTRA_SUBJECT) + assertThatIntent(wrappedIntent).extras().string(Intent.EXTRA_TEXT).isEqualTo(extraText) + assertThatIntent(wrappedIntent).extras().parcelable<Uri>(Intent.EXTRA_STREAM).isEqualTo(uri) } @Test @@ -125,11 +126,12 @@ class ActionIntentCreatorTest : SysuiTestCase() { val output = ActionIntentCreator.createEdit(uri, context) - assertThat(output).hasAction(Intent.ACTION_EDIT) - assertThat(output).hasData(uri) - assertThat(output).hasType("image/png") + assertThatIntent(output).hasAction(Intent.ACTION_EDIT) + assertThatIntent(output).hasData(uri) + assertThatIntent(output).hasType("image/png") assertWithMessage("getComponent()").that(output.component).isNull() - assertThat(output) + assertThat(output.getStringExtra("edit_source")).isEqualTo("screenshot") + assertThatIntent(output) .hasFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION or @@ -146,7 +148,7 @@ class ActionIntentCreatorTest : SysuiTestCase() { val output = ActionIntentCreator.createEdit(uri, context) - assertThat(output).hasData(Uri.parse("content://fake")) + assertThatIntent(output).hasData(Uri.parse("content://fake")) } @Test @@ -160,6 +162,6 @@ class ActionIntentCreatorTest : SysuiTestCase() { val output = ActionIntentCreator.createEdit(uri, context) - assertThat(output).hasComponent(component) + assertThatIntent(output).hasComponent(component) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index eb4ae1a743ef..7aeafeb2a752 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -539,6 +539,23 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo } @Test + public void onKeyguardStatusViewHeightChange_animatesNextTopPaddingChangeForNSSL() { + ArgumentCaptor<View.OnLayoutChangeListener> captor = + ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); + verify(mKeyguardStatusView).addOnLayoutChangeListener(captor.capture()); + View.OnLayoutChangeListener listener = captor.getValue(); + + clearInvocations(mNotificationStackScrollLayoutController); + + when(mKeyguardStatusView.getHeight()).thenReturn(0); + listener.onLayoutChange(mKeyguardStatusView, /* left= */ 0, /* top= */ 0, /* right= */ + 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */ + 0, /* oldBottom = */ 200); + + verify(mNotificationStackScrollLayoutController).animateNextTopPaddingChange(); + } + + @Test public void testCanCollapsePanelOnTouch_trueForKeyGuard() { mStatusBarStateController.setState(KEYGUARD); 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 1edeeffe5217..dc506a5b1d25 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.shade import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import android.view.KeyEvent import android.view.MotionEvent import android.view.ViewGroup import androidx.test.filters.SmallTest @@ -38,6 +39,7 @@ import com.android.systemui.dock.DockManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.TransitionStep @@ -118,6 +120,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel + @Mock lateinit var keyEventInteractor: KeyEventInteractor private val notificationExpansionRepository = NotificationExpansionRepository() private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> @@ -188,7 +191,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { CountDownTimerUtil(), featureFlags ), - BouncerLogger(logcatLogBuffer("BouncerLog")) + BouncerLogger(logcatLogBuffer("BouncerLog")), + keyEventInteractor, ) underTest.setupExpandedStatusBar() @@ -345,6 +349,27 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area) } + @Test + fun forwardsDispatchKeyEvent() { + val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B) + interactionEventHandler.dispatchKeyEvent(keyEvent) + verify(keyEventInteractor).dispatchKeyEvent(keyEvent) + } + + @Test + fun forwardsDispatchKeyEventPreIme() { + val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B) + interactionEventHandler.dispatchKeyEventPreIme(keyEvent) + verify(keyEventInteractor).dispatchKeyEventPreIme(keyEvent) + } + + @Test + fun forwardsInterceptMediaKey() { + val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP) + interactionEventHandler.interceptMediaKey(keyEvent) + verify(keyEventInteractor).interceptMediaKey(keyEvent) + } + companion object { private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) private const val VIEW_BOTTOM = 100 diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 829184c4f05a..66d48d64b3a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.dock.DockManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel @@ -198,7 +199,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { CountDownTimerUtil(), featureFlags ), - BouncerLogger(logcatLogBuffer("BouncerLog")) + BouncerLogger(logcatLogBuffer("BouncerLog")), + Mockito.mock(KeyEventInteractor::class.java), ) controller.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index 9495fdd50a7e..ecaf13711b52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -227,6 +227,25 @@ class ClockRegistryTest : SysuiTestCase() { } @Test + fun activeClockId_changeAfterPluginConnected() { + val plugin1 = FakeClockPlugin() + .addClock("clock_1", "clock 1") + .addClock("clock_2", "clock 2") + + val plugin2 = FakeClockPlugin() + .addClock("clock_3", "clock 3", { mockClock }) + .addClock("clock_4", "clock 4") + + registry.applySettings(ClockSettings("clock_3", null)) + + pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle) + assertEquals(DEFAULT_CLOCK_ID, registry.activeClockId) + + pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle) + assertEquals("clock_3", registry.activeClockId) + } + + @Test fun createDefaultClock_pluginDisconnected() { val plugin1 = FakeClockPlugin() .addClock("clock_1", "clock 1") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt index 4a94dc819a9e..38a8f414b0fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt @@ -21,11 +21,21 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.withArgCaptor import org.junit.Assert import org.junit.Before import org.junit.Test +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @SmallTest @@ -36,13 +46,43 @@ class GroupExpansionManagerTest : SysuiTestCase() { private val groupMembershipManager: GroupMembershipManager = mock() private val featureFlags = FakeFeatureFlags() - private val entry1 = NotificationEntryBuilder().build() - private val entry2 = NotificationEntryBuilder().build() + private val pipeline: NotifPipeline = mock() + private lateinit var beforeRenderListListener: OnBeforeRenderListListener + + private val summary1 = notificationEntry("foo", 1) + private val summary2 = notificationEntry("bar", 1) + private val entries = + listOf<ListEntry>( + GroupEntryBuilder() + .setSummary(summary1) + .setChildren( + listOf( + notificationEntry("foo", 2), + notificationEntry("foo", 3), + notificationEntry("foo", 4) + ) + ) + .build(), + GroupEntryBuilder() + .setSummary(summary2) + .setChildren( + listOf( + notificationEntry("bar", 2), + notificationEntry("bar", 3), + notificationEntry("bar", 4) + ) + ) + .build(), + notificationEntry("baz", 1) + ) + + private fun notificationEntry(pkg: String, id: Int) = + NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply { row = mock() } @Before fun setUp() { - whenever(groupMembershipManager.getGroupSummary(entry1)).thenReturn(entry1) - whenever(groupMembershipManager.getGroupSummary(entry2)).thenReturn(entry2) + whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1) + whenever(groupMembershipManager.getGroupSummary(summary2)).thenReturn(summary2) gem = GroupExpansionManagerImpl(dumpManager, groupMembershipManager, featureFlags) } @@ -54,15 +94,15 @@ class GroupExpansionManagerTest : SysuiTestCase() { var listenerCalledCount = 0 gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ } - gem.setGroupExpanded(entry1, false) + gem.setGroupExpanded(summary1, false) Assert.assertEquals(0, listenerCalledCount) - gem.setGroupExpanded(entry1, true) + gem.setGroupExpanded(summary1, true) Assert.assertEquals(1, listenerCalledCount) - gem.setGroupExpanded(entry2, true) + gem.setGroupExpanded(summary2, true) Assert.assertEquals(2, listenerCalledCount) - gem.setGroupExpanded(entry1, true) + gem.setGroupExpanded(summary1, true) Assert.assertEquals(2, listenerCalledCount) - gem.setGroupExpanded(entry2, false) + gem.setGroupExpanded(summary2, false) Assert.assertEquals(3, listenerCalledCount) } @@ -73,15 +113,39 @@ class GroupExpansionManagerTest : SysuiTestCase() { var listenerCalledCount = 0 gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ } - gem.setGroupExpanded(entry1, false) + gem.setGroupExpanded(summary1, false) Assert.assertEquals(1, listenerCalledCount) - gem.setGroupExpanded(entry1, true) + gem.setGroupExpanded(summary1, true) Assert.assertEquals(2, listenerCalledCount) - gem.setGroupExpanded(entry2, true) + gem.setGroupExpanded(summary2, true) Assert.assertEquals(3, listenerCalledCount) - gem.setGroupExpanded(entry1, true) + gem.setGroupExpanded(summary1, true) Assert.assertEquals(4, listenerCalledCount) - gem.setGroupExpanded(entry2, false) + gem.setGroupExpanded(summary2, false) Assert.assertEquals(5, listenerCalledCount) } + + @Test + fun testSyncWithPipeline() { + featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true) + gem.attach(pipeline) + beforeRenderListListener = withArgCaptor { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + val listener: OnGroupExpansionChangeListener = mock() + gem.registerGroupExpansionChangeListener(listener) + + beforeRenderListListener.onBeforeRenderList(entries) + verify(listener, never()).onGroupExpansionChange(any(), any()) + + // Expand one of the groups. + gem.setGroupExpanded(summary1, true) + verify(listener).onGroupExpansionChange(summary1.row, true) + + // Empty the pipeline list and verify that the group is no longer expanded. + beforeRenderListListener.onBeforeRenderList(emptyList()) + verify(listener).onGroupExpansionChange(summary1.row, false) + verifyNoMoreInteractions(listener) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt new file mode 100644 index 000000000000..b8792a8aeacd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt @@ -0,0 +1,120 @@ +/* + * 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.statusbar.notification.icon.ui.viewbinder + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationListener +import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.util.mockito.whenever +import com.android.wm.shell.bubbles.Bubbles +import java.util.Optional +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() { + @Mock private lateinit var notifListener: NotificationListener + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var wakeUpCoordinator: NotificationWakeUpCoordinator + @Mock private lateinit var keyguardBypassController: KeyguardBypassController + @Mock private lateinit var notifMediaManager: NotificationMediaManager + @Mock private lateinit var dozeParams: DozeParameters + @Mock private lateinit var sectionStyleProvider: SectionStyleProvider + @Mock private lateinit var darkIconDispatcher: DarkIconDispatcher + @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController + @Mock private lateinit var bubbles: Bubbles + @Mock private lateinit var demoModeController: DemoModeController + @Mock private lateinit var aodIcons: NotificationIconContainer + @Mock private lateinit var featureFlags: FeatureFlags + + private val shelfViewModel = NotificationIconContainerShelfViewModel() + private val statusBarViewModel = NotificationIconContainerStatusBarViewModel() + private val aodViewModel = NotificationIconContainerAlwaysOnDisplayViewModel() + + private lateinit var underTest: NotificationIconAreaControllerViewBinderWrapperImpl + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = + NotificationIconAreaControllerViewBinderWrapperImpl( + mContext, + statusBarStateController, + wakeUpCoordinator, + keyguardBypassController, + notifMediaManager, + notifListener, + dozeParams, + sectionStyleProvider, + Optional.of(bubbles), + demoModeController, + darkIconDispatcher, + featureFlags, + statusBarWindowController, + screenOffAnimController, + shelfViewModel, + statusBarViewModel, + aodViewModel, + ) + } + + @Test + fun testNotificationIcons_settingHideIcons() { + underTest.settingsListener.onStatusBarIconsBehaviorChanged(true) + assertFalse(underTest.shouldShowLowPriorityIcons()) + } + + @Test + fun testNotificationIcons_settingShowIcons() { + underTest.settingsListener.onStatusBarIconsBehaviorChanged(false) + assertTrue(underTest.shouldShowLowPriorityIcons()) + } + + @Test + fun testAppearResetsTranslation() { + underTest.setupAodIcons(aodIcons) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) + underTest.appearAodIcons() + verify(aodIcons).translationY = 0f + verify(aodIcons).alpha = 1.0f + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index 987861d3f133..77c9b8b48296 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -1,6 +1,7 @@ package com.android.systemui.statusbar.notification.stack import android.annotation.DimenRes +import android.content.pm.PackageManager import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress @@ -21,6 +22,7 @@ import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue +import org.junit.Assume import org.junit.Before import org.junit.Rule import org.junit.Test @@ -66,6 +68,8 @@ class StackScrollAlgorithmTest : SysuiTestCase() { @Before fun setUp() { + Assume.assumeFalse(isTv()) + whenever(notificationShelf.viewState).thenReturn(ExpandableViewState()) whenever(notificationRow.viewState).thenReturn(ExpandableViewState()) ambientState.isSmallScreen = true @@ -73,6 +77,10 @@ class StackScrollAlgorithmTest : SysuiTestCase() { hostView.addView(notificationRow) } + private fun isTv(): Boolean { + return context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) + } + @Test fun resetViewStates_defaultHun_yTranslationIsInset() { whenever(notificationRow.isPinned).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index 8545b894ad41..3ad3c15f158a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; + import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -29,8 +31,10 @@ import android.app.ActivityManager; import android.app.StatusBarManager; import android.os.PowerManager; import android.os.UserHandle; +import android.os.VibrationEffect; import android.os.Vibrator; import android.testing.AndroidTestingRunner; +import android.view.HapticFeedbackConstants; import android.view.WindowInsets; import androidx.test.filters.SmallTest; @@ -42,6 +46,7 @@ import com.android.internal.view.AppearanceRegion; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSHost; @@ -98,6 +103,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { @Mock private UserTracker mUserTracker; @Mock private QSHost mQSHost; @Mock private ActivityStarter mActivityStarter; + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); CentralSurfacesCommandQueueCallbacks mSbcqCallbacks; @@ -134,7 +140,8 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { mCameraLauncherLazy, mUserTracker, mQSHost, - mActivityStarter); + mActivityStarter, + mFeatureFlags); when(mUserTracker.getUserHandle()).thenReturn( UserHandle.of(ActivityManager.getCurrentUser())); @@ -241,4 +248,24 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { verifyZeroInteractions(mSystemBarAttributesListener); } + + @Test + public void vibrateOnNavigationKeyDown_oneWayHapticsDisabled_usesVibrate() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); + + mSbcqCallbacks.vibrateOnNavigationKeyDown(); + + verify(mVibratorHelper).vibrate(VibrationEffect.EFFECT_TICK); + } + + @Test + public void vibrateOnNavigationKeyDown_oneWayHapticsEnabled_usesPerformHapticFeedback() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + + mSbcqCallbacks.vibrateOnNavigationKeyDown(); + + verify(mShadeViewController).performHapticFeedback( + HapticFeedbackConstants.GESTURE_START + ); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java index 8e1dcf0b1bb7..1b8cfd43e6b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java @@ -48,7 +48,7 @@ import java.util.Optional; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class NotificationIconAreaControllerTest extends SysuiTestCase { +public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase { @Mock private NotificationListener mListener; @@ -70,7 +70,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { StatusBarWindowController mStatusBarWindowController; @Mock ScreenOffAnimationController mScreenOffAnimationController; - private NotificationIconAreaController mController; + private LegacyNotificationIconAreaControllerImpl mController; @Mock private Bubbles mBubbles; @Mock private DemoModeController mDemoModeController; @@ -82,7 +82,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); - mController = new NotificationIconAreaController( + mController = new LegacyNotificationIconAreaControllerImpl( mContext, mStatusBarStateController, mWakeUpCoordinator, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 2e92bb948c60..0b31523e9b98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -27,7 +27,7 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.ShadeControllerImpl import com.android.systemui.shade.ShadeLogger import com.android.systemui.shade.ShadeViewController @@ -77,7 +77,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Mock private lateinit var shadeControllerImpl: ShadeControllerImpl @Mock - private lateinit var sceneInteractor: Provider<SceneInteractor> + private lateinit var windowRootView: Provider<WindowRootView> @Mock private lateinit var shadeLogger: ShadeLogger @Mock @@ -203,7 +203,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { centralSurfacesImpl, shadeControllerImpl, shadeViewController, - sceneInteractor, + windowRootView, shadeLogger, viewUtil, configurationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 0dc1d9a4b177..6b3bd22d5e62 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -1802,6 +1802,15 @@ public class ScrimControllerTest extends SysuiTestCase { assertFalse(ScrimState.UNLOCKED.mAnimateChange); } + @Test + public void testNotifScrimAlpha_1f_afterUnlockFinishedAndExpanded() { + mScrimController.transitionTo(ScrimState.KEYGUARD); + when(mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(true); + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.onUnlockAnimationFinished(); + assertAlphaAfterExpansion(mNotificationsScrim, 1f, 1f); + } + private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) { mScrimController.setRawPanelExpansionFraction(expansion); finishAnimationsImmediately(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 823155b0d7e6..b8f2cab3019e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -81,7 +81,7 @@ import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; -import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -136,8 +136,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private StatusBarWindowStateController mStatusBarWindowStateController; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @ClassRule - public static AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + @Rule + public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); private List<StatusBarWindowStateListener> mStatusBarWindowStateListeners = new ArrayList<>(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index ef39ff8ed521..79feb417a062 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -82,7 +82,7 @@ import com.android.systemui.statusbar.phone.LightBarController; import org.junit.After; import org.junit.Before; -import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -111,8 +111,8 @@ public class RemoteInputViewTest extends SysuiTestCase { private BlockingQueueIntentReceiver mReceiver; private final UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake(); - @ClassRule - public static AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + @Rule + public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); @Before public void setUp() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt index b698e70eb379..b78e83990411 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -105,6 +106,7 @@ class VariableDateViewControllerTest : SysuiTestCase() { systemClock, broadcastDispatcher, shadeExpansionStateManager, + mock(), testableHandler, view ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index 462fd0a2410b..69d7586e6ab4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -104,8 +104,6 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private DumpManager mDumpManager; - @Mock - private Handler mHandler; @Before @@ -130,7 +128,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager, mPackageManager, mWakefullnessLifcycle, mKeyguardManager, - mActivityManager, mUserTracker, mDumpManager, mHandler, mCallback); + mActivityManager, mUserTracker, mDumpManager, mCallback); mVolumeController.setEnableDialogs(true, true); } @@ -245,12 +243,11 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { ActivityManager activityManager, UserTracker userTracker, DumpManager dumpManager, - Handler mainHandler, C callback) { super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, notificationManager, optionalVibrator, iAudioService, accessibilityManager, packageManager, wakefulnessLifecycle, keyguardManager, - activityManager, userTracker, dumpManager, mainHandler); + activityManager, userTracker, dumpManager); mCallbacks = callback; ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor = diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index fa18e575220c..fdc4f372b329 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -31,14 +31,13 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.animation.AnimatorTestRule; import android.app.KeyguardManager; import android.content.res.Configuration; import android.media.AudioManager; @@ -85,14 +84,14 @@ import java.util.function.Predicate; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class VolumeDialogImplTest extends SysuiTestCase { - private static final AnimatorTestRule sAnimatorTestRule = new AnimatorTestRule(); - VolumeDialogImpl mDialog; View mActiveRinger; View mDrawerContainer; View mDrawerVibrate; View mDrawerMute; View mDrawerNormal; + CaptionsToggleImageButton mODICaptionsIcon; + private TestableLooper mTestableLooper; private ConfigurationController mConfigurationController; private int mOriginalOrientation; @@ -177,9 +176,14 @@ public class VolumeDialogImplTest extends SysuiTestCase { mActiveRinger = mDialog.getDialogView().findViewById( R.id.volume_new_ringer_active_icon_container); mDrawerContainer = mDialog.getDialogView().findViewById(R.id.volume_drawer_container); - mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate); - mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute); - mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal); + + // Drawer is not always available, e.g. on TVs + if (mDrawerContainer != null) { + mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate); + mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute); + mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal); + } + mODICaptionsIcon = mDialog.getDialogView().findViewById(R.id.odi_captions_icon); Prefs.putInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, @@ -188,6 +192,10 @@ public class VolumeDialogImplTest extends SysuiTestCase { Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false); } + private void assumeHasDrawer() { + assumeNotNull("Layout does not contain drawer", mDrawerContainer); + } + private State createShellState() { State state = new VolumeDialogController.State(); for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) { @@ -359,6 +367,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectVibrateFromDrawer() { + assumeHasDrawer(); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; @@ -374,6 +384,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectVibrateFromDrawer_OnewayAPI_On() { + assumeHasDrawer(); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; @@ -389,6 +401,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectMuteFromDrawer() { + assumeHasDrawer(); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; @@ -404,6 +418,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectMuteFromDrawer_OnewayAPI_On() { + assumeHasDrawer(); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; @@ -419,6 +435,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectNormalFromDrawer() { + assumeHasDrawer(); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; @@ -434,6 +452,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectNormalFromDrawer_OnewayAPI_On() { + assumeHasDrawer(); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; @@ -479,123 +499,45 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void ifPortraitHalfOpen_drawVerticallyTop() { - DevicePostureController devicePostureController = mock(DevicePostureController.class); - when(devicePostureController.getDevicePosture()) - .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED); - - VolumeDialogImpl dialog = new VolumeDialogImpl( - getContext(), - mVolumeDialogController, - mAccessibilityMgr, - mDeviceProvisionedController, - mConfigurationController, - mMediaOutputDialogFactory, - mVolumePanelFactory, - mActivityStarter, - mInteractionJankMonitor, - false, - mCsdWarningDialogFactory, - devicePostureController, - mTestableLooper.getLooper(), - mDumpManager, - mFeatureFlags - ); - dialog.init(0 , null); - - verify(devicePostureController).addCallback(any()); - dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED); + mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED); mTestableLooper.processAllMessages(); // let dismiss() finish setOrientation(Configuration.ORIENTATION_PORTRAIT); // Call show() to trigger layout updates before verifying position - dialog.show(SHOW_REASON_UNKNOWN); + mDialog.show(SHOW_REASON_UNKNOWN); mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect - int gravity = dialog.getWindowGravity(); + int gravity = mDialog.getWindowGravity(); assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK); - - cleanUp(dialog); } @Test public void ifPortraitAndOpen_drawCenterVertically() { - DevicePostureController devicePostureController = mock(DevicePostureController.class); - when(devicePostureController.getDevicePosture()) - .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED); - - VolumeDialogImpl dialog = new VolumeDialogImpl( - getContext(), - mVolumeDialogController, - mAccessibilityMgr, - mDeviceProvisionedController, - mConfigurationController, - mMediaOutputDialogFactory, - mVolumePanelFactory, - mActivityStarter, - mInteractionJankMonitor, - false, - mCsdWarningDialogFactory, - devicePostureController, - mTestableLooper.getLooper(), - mDumpManager, - mFeatureFlags - ); - dialog.init(0, null); - - verify(devicePostureController).addCallback(any()); - dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED); + mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED); mTestableLooper.processAllMessages(); // let dismiss() finish setOrientation(Configuration.ORIENTATION_PORTRAIT); - dialog.show(SHOW_REASON_UNKNOWN); + mDialog.show(SHOW_REASON_UNKNOWN); mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect - int gravity = dialog.getWindowGravity(); + int gravity = mDialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); - - cleanUp(dialog); } @Test public void ifLandscapeAndHalfOpen_drawCenterVertically() { - DevicePostureController devicePostureController = mock(DevicePostureController.class); - when(devicePostureController.getDevicePosture()) - .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED); - - VolumeDialogImpl dialog = new VolumeDialogImpl( - getContext(), - mVolumeDialogController, - mAccessibilityMgr, - mDeviceProvisionedController, - mConfigurationController, - mMediaOutputDialogFactory, - mVolumePanelFactory, - mActivityStarter, - mInteractionJankMonitor, - false, - mCsdWarningDialogFactory, - devicePostureController, - mTestableLooper.getLooper(), - mDumpManager, - mFeatureFlags - ); - dialog.init(0, null); - - verify(devicePostureController).addCallback(any()); - dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED); + mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED); mTestableLooper.processAllMessages(); // let dismiss() finish setOrientation(Configuration.ORIENTATION_LANDSCAPE); - dialog.show(SHOW_REASON_UNKNOWN); + mDialog.show(SHOW_REASON_UNKNOWN); mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect - int gravity = dialog.getWindowGravity(); + int gravity = mDialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); - - cleanUp(dialog); } @Test @@ -606,31 +548,9 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void dialogDestroy_removesPostureControllerCallback() { - VolumeDialogImpl dialog = new VolumeDialogImpl( - getContext(), - mVolumeDialogController, - mAccessibilityMgr, - mDeviceProvisionedController, - mConfigurationController, - mMediaOutputDialogFactory, - mVolumePanelFactory, - mActivityStarter, - mInteractionJankMonitor, - false, - mCsdWarningDialogFactory, - mPostureController, - mTestableLooper.getLooper(), - mDumpManager, - mFeatureFlags - ); - dialog.init(0, null); - verify(mPostureController, never()).removeCallback(any()); - dialog.destroy(); - + mDialog.destroy(); verify(mPostureController).removeCallback(any()); - - cleanUp(dialog); } private void setOrientation(int orientation) { @@ -688,6 +608,28 @@ public class VolumeDialogImplTest extends SysuiTestCase { assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE); } + @Test + public void testOnCaptionEnabledStateChanged_checkBeforeSwitchTrue_setCaptionsEnabledState() { + ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture = + ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class); + verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any()); + VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue(); + + callbacks.onCaptionEnabledStateChanged(true, true); + verify(mVolumeDialogController).setCaptionsEnabledState(eq(false)); + } + + @Test + public void testOnCaptionEnabledStateChanged_checkBeforeSwitchFalse_getCaptionsEnabledTrue() { + ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture = + ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class); + verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any()); + VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue(); + + callbacks.onCaptionEnabledStateChanged(true, false); + assertTrue(mODICaptionsIcon.getCaptionsEnabled()); + } + /** * The content description should include ringer state, and the correct one. */ @@ -727,7 +669,6 @@ public class VolumeDialogImplTest extends SysuiTestCase { public void teardown() { cleanUp(mDialog); setOrientation(mOriginalOrientation); - sAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration); mTestableLooper.moveTimeForward(mLongestHideShowAnimationDuration); mTestableLooper.processAllMessages(); reset(mPostureController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java index fc5f78228f2b..1125d41856c6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java @@ -109,6 +109,7 @@ public class WallpaperLocalColorExtractorTest extends SysuiTestCase { WallpaperLocalColorExtractor colorExtractor = new WallpaperLocalColorExtractor( mBackgroundExecutor, + new Object(), new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() { @Override public void onColorsProcessed(List<RectF> regions, diff --git a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java index 19c68e86e5dc..41dbc147dfc5 100644 --- a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java +++ b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java @@ -49,7 +49,7 @@ import java.util.function.Consumer; * public class SampleAnimatorTest { * * {@literal @}Rule - * public AnimatorTestRule sAnimatorTestRule = new AnimatorTestRule(); + * public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); * * {@literal @}UiThreadTest * {@literal @}Test @@ -58,7 +58,7 @@ import java.util.function.Consumer; * animator.setDuration(1000L); * assertThat(animator.getAnimatedValue(), is(0)); * animator.start(); - * sAnimatorTestRule.advanceTimeBy(500L); + * mAnimatorTestRule.advanceTimeBy(500L); * assertThat(animator.getAnimatedValue(), is(500)); * } * } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt new file mode 100644 index 000000000000..1a8c5830e453 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt @@ -0,0 +1,15 @@ +package com.android.systemui.communal.data.repository + +import com.android.systemui.communal.shared.CommunalAppWidgetInfo +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +/** Fake implementation of [CommunalWidgetRepository] */ +class FakeCommunalWidgetRepository : CommunalWidgetRepository { + private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null) + override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo + + fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) { + _stopwatchAppWidgetInfo.value = appWidgetInfo + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt index 8c98aea6a990..e91e9559fa1e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt @@ -22,32 +22,24 @@ import com.android.systemui.keyguard.shared.model.AuthenticationFlags import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map class FakeBiometricSettingsRepository : BiometricSettingsRepository { + private val _isFingerprintEnrolledAndEnabled = MutableStateFlow(false) + override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> + get() = _isFingerprintEnrolledAndEnabled - private val _isFingerprintEnrolled = MutableStateFlow<Boolean>(false) - override val isFingerprintEnrolled: StateFlow<Boolean> = _isFingerprintEnrolled.asStateFlow() + private val _isFingerprintAuthCurrentlyAllowed = MutableStateFlow(false) + override val isFingerprintAuthCurrentlyAllowed: StateFlow<Boolean> + get() = _isFingerprintAuthCurrentlyAllowed - private val _isFaceEnrolled = MutableStateFlow(false) - override val isFaceEnrolled: Flow<Boolean> - get() = _isFaceEnrolled + private val _isFaceAuthEnrolledAndEnabled = MutableStateFlow(false) + override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> + get() = _isFaceAuthEnrolledAndEnabled - private val _isFaceAuthEnabled = MutableStateFlow(false) - override val isFaceAuthenticationEnabled: Flow<Boolean> - get() = _isFaceAuthEnabled - - private val _isStrongBiometricAllowed = MutableStateFlow(false) - override val isStrongBiometricAllowed = _isStrongBiometricAllowed.asStateFlow() - - private val _isNonStrongBiometricAllowed = MutableStateFlow(false) - override val isNonStrongBiometricAllowed: StateFlow<Boolean> - get() = _isNonStrongBiometricAllowed - - private val _isFingerprintEnabledByDevicePolicy = MutableStateFlow(false) - override val isFingerprintEnabledByDevicePolicy = - _isFingerprintEnabledByDevicePolicy.asStateFlow() + private val _isFaceAuthCurrentlyAllowed = MutableStateFlow(false) + override val isFaceAuthCurrentlyAllowed: Flow<Boolean> + get() = _isFaceAuthCurrentlyAllowed private val _isFaceAuthSupportedInCurrentPosture = MutableStateFlow(false) override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> @@ -59,34 +51,33 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository { private val _authFlags = MutableStateFlow(AuthenticationFlags(0, 0)) override val authenticationFlags: Flow<AuthenticationFlags> get() = _authFlags - fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) { - _isFingerprintEnrolled.value = isFingerprintEnrolled + + fun setAuthenticationFlags(value: AuthenticationFlags) { + _authFlags.value = value } - fun setStrongBiometricAllowed(isStrongBiometricAllowed: Boolean) { - _isStrongBiometricAllowed.value = isStrongBiometricAllowed + fun setIsFingerprintAuthEnrolledAndEnabled(value: Boolean) { + _isFingerprintEnrolledAndEnabled.value = value + _isFingerprintAuthCurrentlyAllowed.value = _isFingerprintAuthCurrentlyAllowed.value && value } - fun setFingerprintEnabledByDevicePolicy(isFingerprintEnabledByDevicePolicy: Boolean) { - _isFingerprintEnabledByDevicePolicy.value = isFingerprintEnabledByDevicePolicy + fun setIsFingerprintAuthCurrentlyAllowed(value: Boolean) { + _isFingerprintAuthCurrentlyAllowed.value = value } - fun setAuthenticationFlags(value: AuthenticationFlags) { - _authFlags.value = value + fun setIsFaceAuthEnrolledAndEnabled(value: Boolean) { + _isFaceAuthEnrolledAndEnabled.value = value + _isFaceAuthCurrentlyAllowed.value = _isFaceAuthCurrentlyAllowed.value && value } - fun setFaceEnrolled(isFaceEnrolled: Boolean) { - _isFaceEnrolled.value = isFaceEnrolled + fun setIsFaceAuthCurrentlyAllowed(value: Boolean) { + _isFaceAuthCurrentlyAllowed.value = value } fun setIsFaceAuthSupportedInCurrentPosture(value: Boolean) { _isFaceAuthSupportedInCurrentPosture.value = value } - fun setIsFaceAuthEnabled(enabled: Boolean) { - _isFaceAuthEnabled.value = enabled - } - fun setIsUserInLockdown(value: Boolean) { if (value) { setAuthenticationFlags( @@ -105,12 +96,4 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository { ) } } - - fun setIsNonStrongBiometricAllowed(value: Boolean) { - _isNonStrongBiometricAllowed.value = value - } - - fun setIsStrongBiometricAllowed(value: Boolean) { - _isStrongBiometricAllowed.value = value - } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index dd45331df4b3..f0e1111c6e12 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -39,8 +39,6 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.RemoteUserInput -import com.android.systemui.scene.shared.model.RemoteUserInputAction import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.user.data.repository.FakeUserRepository @@ -196,15 +194,6 @@ class SceneTestUtils( } companion object { - val REMOTE_INPUT_DOWN_GESTURE = - listOf( - RemoteUserInput(10f, 10f, RemoteUserInputAction.DOWN), - RemoteUserInput(10f, 20f, RemoteUserInputAction.MOVE), - RemoteUserInput(10f, 30f, RemoteUserInputAction.MOVE), - RemoteUserInput(10f, 40f, RemoteUserInputAction.MOVE), - RemoteUserInput(10f, 40f, RemoteUserInputAction.UP), - ) - fun DomainLayerAuthenticationMethodModel.toDataLayer(): DataLayerAuthenticationMethodModel { return when (this) { DomainLayerAuthenticationMethodModel.None -> DataLayerAuthenticationMethodModel.None 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 9b9593b2b99a..8060d5a96aa6 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -637,7 +637,7 @@ public class TouchExplorer extends BaseEventStreamTransformation MotionEvent event, MotionEvent rawEvent, int policyFlags) { switch (event.getActionMasked()) { case ACTION_DOWN: - // We should have already received ACTION_DOWN. Ignore. + handleActionDownStateTouchExploring(event, rawEvent, policyFlags); break; case ACTION_POINTER_DOWN: handleActionPointerDown(event, rawEvent, policyFlags); @@ -843,6 +843,15 @@ public class TouchExplorer extends BaseEventStreamTransformation } } + private void handleActionDownStateTouchExploring( + MotionEvent event, MotionEvent rawEvent, int policyFlags) { + // This is an interrupted and continued touch exploration. Maintain the consistency of the + // event stream. + mSendTouchExplorationEndDelayed.cancel(); + mSendTouchInteractionEndDelayed.cancel(); + sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); + } + /** * Handles move events while touch exploring. this is also where we drag or delegate based on * the number of fingers moving on the screen. @@ -1100,12 +1109,15 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** - * Sends the enter events if needed. Such events are hover enter and touch explore - * gesture start. + * Sends the enter events if needed. Such events are hover enter and touch explore gesture + * start. * * @param policyFlags The policy flags associated with the event. */ private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { + if (!mState.isTouchExploring()) { + mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START); + } MotionEvent event = mState.getLastInjectedHoverEvent(); if (event != null && event.getActionMasked() == ACTION_HOVER_EXIT) { final int pointerIdBits = event.getPointerIdBits(); @@ -1118,7 +1130,6 @@ public class TouchExplorer extends BaseEventStreamTransformation } } - /** * Determines whether a two pointer gesture is a dragging one. * diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index ee6c28ed3fe1..d1be5a9e971d 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -43,6 +43,7 @@ import android.companion.virtual.audio.IAudioConfigChangedCallback; import android.companion.virtual.audio.IAudioRoutingCallback; import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorEvent; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -125,6 +126,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final VirtualDeviceManagerService mService; private final PendingTrampolineCallback mPendingTrampolineCallback; private final int mOwnerUid; + private final String mOwnerPackageName; private int mDeviceId; private @Nullable String mPersistentDeviceId; // Thou shall not hold the mVirtualDeviceLock over the mInputController calls. @@ -196,7 +198,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub AssociationInfo associationInfo, VirtualDeviceManagerService service, IBinder token, - int ownerUid, + AttributionSource attributionSource, int deviceId, CameraAccessController cameraAccessController, PendingTrampolineCallback pendingTrampolineCallback, @@ -209,7 +211,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub associationInfo, service, token, - ownerUid, + attributionSource, deviceId, /* inputController= */ null, cameraAccessController, @@ -227,7 +229,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub AssociationInfo associationInfo, VirtualDeviceManagerService service, IBinder token, - int ownerUid, + AttributionSource attributionSource, int deviceId, InputController inputController, CameraAccessController cameraAccessController, @@ -238,7 +240,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub VirtualDeviceParams params, DisplayManagerGlobal displayManager) { super(PermissionEnforcer.fromContext(context)); - UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid); + mOwnerPackageName = attributionSource.getPackageName(); + UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid()); mContext = context.createContextAsUser(ownerUserHandle, 0); mAssociationInfo = associationInfo; mPersistentDeviceId = PERSISTENT_ID_PREFIX_CDM_ASSOCIATION + associationInfo.getId(); @@ -247,7 +250,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mActivityListener = activityListener; mSoundEffectListener = soundEffectListener; mRunningAppsChangedCallback = runningAppsChangedCallback; - mOwnerUid = ownerUid; + mOwnerUid = attributionSource.getUid(); mDeviceId = deviceId; mAppToken = token; mParams = params; @@ -771,7 +774,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub fout.println(" VirtualDevice: "); fout.println(" mDeviceId: " + mDeviceId); fout.println(" mAssociationId: " + mAssociationInfo.getId()); - fout.println(" mParams: " + mParams); + fout.println(" mOwnerPackageName: " + mOwnerPackageName); + fout.println(" mParams: "); + mParams.dump(fout, " "); fout.println(" mVirtualDisplayIds: "); synchronized (mVirtualDeviceLock) { for (int i = 0; i < mVirtualDisplays.size(); i++) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 77508a8fcc0d..e558498e4715 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -35,6 +35,7 @@ import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.sensor.VirtualSensor; +import android.content.AttributionSource; import android.content.Context; import android.content.Intent; import android.hardware.display.IVirtualDisplayCallback; @@ -314,13 +315,15 @@ public class VirtualDeviceManagerService extends SystemService { @Override // Binder call public IVirtualDevice createVirtualDevice( IBinder token, - String packageName, + AttributionSource attributionSource, int associationId, @NonNull VirtualDeviceParams params, @NonNull IVirtualDeviceActivityListener activityListener, @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) { createVirtualDevice_enforcePermission(); + attributionSource.enforceCallingUid(); final int callingUid = getCallingUid(); + final String packageName = attributionSource.getPackageName(); if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { throw new SecurityException( "Package name " + packageName + " does not belong to calling uid " @@ -340,10 +343,9 @@ public class VirtualDeviceManagerService extends SystemService { final int deviceId = sNextUniqueIndex.getAndIncrement(); final Consumer<ArraySet<Integer>> runningAppsChangedCallback = runningUids -> notifyRunningAppsChanged(deviceId, runningUids); - VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), - associationInfo, VirtualDeviceManagerService.this, token, callingUid, - deviceId, cameraAccessController, - mPendingTrampolineCallback, activityListener, + VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo, + VirtualDeviceManagerService.this, token, attributionSource, deviceId, + cameraAccessController, mPendingTrampolineCallback, activityListener, soundEffectListener, runningAppsChangedCallback, params); synchronized (mVirtualDeviceManagerLock) { if (mVirtualDevices.size() == 0) { diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 315972cde76b..f59417046c85 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -1225,15 +1225,13 @@ public class ContentCaptureManagerService extends public ContentCaptureOptions getOptions(@UserIdInt int userId, @NonNull String packageName) { boolean isContentCaptureReceiverEnabled; - boolean isContentProtectionReceiverEnabled; + boolean isContentProtectionReceiverEnabled = + isContentProtectionReceiverEnabled(userId, packageName); ArraySet<ComponentName> whitelistedComponents = null; synchronized (mGlobalWhitelistStateLock) { isContentCaptureReceiverEnabled = isContentCaptureReceiverEnabled(userId, packageName); - isContentProtectionReceiverEnabled = - isContentProtectionReceiverEnabled(userId, packageName); - if (!isContentCaptureReceiverEnabled) { // Full package is not allowlisted: check individual components next whitelistedComponents = getWhitelistedComponents(userId, packageName); diff --git a/services/core/Android.bp b/services/core/Android.bp index c2774e5c4400..66a77a36dca9 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -190,6 +190,8 @@ java_library_static { "securebox", "apache-commons-math", "power_optimization_flags_lib", + "notification_flags_lib", + "pm_flags_lib", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index fc51e2ec0954..557e4ac843d2 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1374,7 +1374,7 @@ public abstract class PackageManagerInternal { * and the package should get out of stopped state and be enabled. */ public abstract void notifyComponentUsed(@NonNull String packageName, - @UserIdInt int userId, @NonNull String recentCallingPackage); + @UserIdInt int userId, @NonNull String recentCallingPackage, @NonNull String debugInfo); /** @deprecated For legacy shell command only. */ @Deprecated @@ -1403,4 +1403,10 @@ public abstract class PackageManagerInternal { */ public abstract int[] getDistractingPackageRestrictionsAsUser( @NonNull String[] packageNames, int userId); + + /** + * Checks if package is quarantined for a specific user. + */ + public abstract boolean isPackageQuarantined(@NonNull String packageName, + @UserIdInt int userId); } diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index b67e62703067..d47a3991a966 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -81,7 +81,7 @@ option java_package com.android.server # when a notification has been clicked 27520 notification_clicked (key|3),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1) # when a notification action button has been clicked -27521 notification_action_clicked (key|3),(action_index|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1) +27521 notification_action_clicked (key|3),(piIdentifier|3),(pendingIntent|3),(action_index|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1) # when a notification has been canceled 27530 notification_canceled (key|3),(reason|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1),(listener|3) # replaces 27510 with a row per notification diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 830d55ad866b..7fb9580b28ab 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1289,6 +1289,16 @@ class StorageManagerService extends IStorageManager.Stub return mVold.supportsBlockCheckpoint(); } + private void prepareUserStorageForMoveInternal(String fromVolumeUuid, String toVolumeUuid, + List<UserInfo> users) throws Exception { + + final int flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; + for (UserInfo user : users) { + prepareUserStorageInternal(fromVolumeUuid, user.id, user.serialNumber, flags); + prepareUserStorageInternal(toVolumeUuid, user.id, user.serialNumber, flags); + } + } + @Override public void onAwakeStateChanged(boolean isAwake) { // Ignored @@ -2912,6 +2922,7 @@ class StorageManagerService extends IStorageManager.Stub final VolumeInfo from; final VolumeInfo to; + final List<UserInfo> users; synchronized (mLock) { if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) { @@ -2925,7 +2936,7 @@ class StorageManagerService extends IStorageManager.Stub mMoveTargetUuid = volumeUuid; // We need all the users unlocked to move their primary storage - final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); + users = mContext.getSystemService(UserManager.class).getUsers(); for (UserInfo user : users) { if (StorageManager.isFileEncrypted() && !isUserKeyUnlocked(user.id)) { Slog.w(TAG, "Failing move due to locked user " + user.id); @@ -2961,6 +2972,19 @@ class StorageManagerService extends IStorageManager.Stub } } + // Prepare the storage before move, this is required to unlock adoptable storage (as the + // keys are tied to prepare user data step) & also is required for the destination files to + // end up with the correct SELinux labels and encryption policies for directories + try { + prepareUserStorageForMoveInternal(mPrimaryStorageUuid, volumeUuid, users); + } catch (Exception e) { + Slog.w(TAG, "Failing move due to failure on prepare user data", e); + synchronized (mLock) { + onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR); + } + return; + } + try { mVold.moveStorage(from.id, to.id, new IVoldTaskListener.Stub() { @Override @@ -4904,5 +4928,16 @@ class StorageManagerService extends IStorageManager.Stub mCloudProviderChangeListeners.add(listener); mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener).sendToTarget(); } + + @Override + public void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid, + List<UserInfo> users) { + try { + prepareUserStorageForMoveInternal(fromVolumeUuid, toVolumeUuid, users); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index d39826077395..330742a2d748 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -5309,11 +5309,12 @@ public class AccountManagerService if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); } - int flags = Context.BIND_AUTO_CREATE; + long flags = Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE; if (mAuthenticatorCache.getBindInstantServiceAllowed(mAccounts.userId)) { flags |= Context.BIND_ALLOW_INSTANT; } - if (!mContext.bindServiceAsUser(intent, this, flags, UserHandle.of(mAccounts.userId))) { + if (!mContext.bindServiceAsUser(intent, this, Context.BindServiceFlags.of(flags), + UserHandle.of(mAccounts.userId))) { Log.w(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); // Perform unbind as per documentation at Context.bindServiceAsUser mContext.unbindService(this); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index cb246f68cc6e..93fe0c9c18c3 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -3601,6 +3601,8 @@ public final class ActiveServices { || (flags & Context.BIND_EXTERNAL_SERVICE_LONG) != 0; final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0; final boolean inSharedIsolatedProcess = (flags & Context.BIND_SHARED_ISOLATED_PROCESS) != 0; + final boolean filterOutQuarantined = + (flags & Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS) != 0; ProcessRecord attributedApp = null; if (sdkSandboxClientAppUid > 0) { @@ -3610,7 +3612,7 @@ public final class ActiveServices { isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, isBindExternal, allowInstant, null /* fgsDelegateOptions */, - inSharedIsolatedProcess); + inSharedIsolatedProcess, filterOutQuarantined); if (res == null) { return 0; } @@ -4119,6 +4121,20 @@ public final class ActiveServices { boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions, boolean inSharedIsolatedProcess) { + return retrieveServiceLocked(service, instanceName, isSdkSandboxService, + sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, callingPackage, + callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal, + allowInstant, fgsDelegateOptions, inSharedIsolatedProcess, + false /* filterOutQuarantined */); + } + + private ServiceLookupResult retrieveServiceLocked(Intent service, + String instanceName, boolean isSdkSandboxService, int sdkSandboxClientAppUid, + String sdkSandboxClientAppPackage, String resolvedType, + String callingPackage, int callingPid, int callingUid, int userId, + boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, + boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions, + boolean inSharedIsolatedProcess, boolean filterOutQuarantined) { if (isSdkSandboxService && instanceName == null) { throw new IllegalArgumentException("No instanceName provided for sdk sandbox process"); } @@ -4235,11 +4251,14 @@ public final class ActiveServices { if (r == null) { try { - int flags = ActivityManagerService.STOCK_PM_FLAGS + long flags = ActivityManagerService.STOCK_PM_FLAGS | PackageManager.MATCH_DEBUG_TRIAGED_MISSING; if (allowInstant) { flags |= PackageManager.MATCH_INSTANT; } + if (filterOutQuarantined) { + flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; + } // TODO: come back and remove this assumption to triage all services ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service, resolvedType, flags, userId, callingUid); @@ -5118,7 +5137,7 @@ public final class ActiveServices { try { mAm.mPackageManagerInt.notifyComponentUsed( - r.packageName, r.userId, r.mRecentCallingPackage); + r.packageName, r.userId, r.mRecentCallingPackage, r.toString()); } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " + r.packageName + ": " + e); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a53c2fb44873..c4816fb1dd71 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -58,6 +58,7 @@ import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_INSTRUMENTAT import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; +import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_ANY_USER; @@ -7409,6 +7410,9 @@ public class ActivityManagerService extends IActivityManager.Stub case BugreportParams.BUGREPORT_MODE_WIFI: type = "bugreportwifi"; break; + case BugreportParams.BUGREPORT_MODE_ONBOARDING: + type = "bugreportonboarding"; + break; default: throw new IllegalArgumentException( "Provided bugreport type is not correct, value: " @@ -14218,7 +14222,8 @@ public class ActivityManagerService extends IActivityManager.Stub private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, int callingUid, int[] users, int[] broadcastAllowList) { // TODO: come back and remove this assumption to triage all broadcasts - int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING; + long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING + | FILTER_OUT_QUARANTINED_COMPONENTS; List<ResolveInfo> receivers = null; HashSet<ComponentName> singleUserReceivers = null; @@ -14846,6 +14851,16 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal.onPackagesSuspendedChanged(packageNames, suspended, userIdExtra); + + final boolean quarantined = intent.getBooleanExtra( + Intent.EXTRA_QUARANTINED, false); + if (suspended && quarantined && packageNames != null) { + for (int i = 0; i < packageNames.length; i++) { + forceStopPackageLocked(packageNames[i], -1, false, true, true, + false, false, userId, "suspended"); + } + } + break; } break; diff --git a/services/core/java/com/android/server/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java index 6e93ce4eddcf..3fa6102b64e0 100644 --- a/services/core/java/com/android/server/am/ComponentAliasResolver.java +++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java @@ -457,7 +457,7 @@ public class ComponentAliasResolver { @Nullable public Resolution<ResolveInfo> resolveReceiver(@NonNull Intent intent, @NonNull ResolveInfo receiver, @Nullable String resolvedType, - int packageFlags, int userId, int callingUid, boolean forSend) { + long packageFlags, int userId, int callingUid, boolean forSend) { // Resolve this alias. final Resolution<ComponentName> resolution = resolveComponentAlias(() -> receiver.activityInfo.getComponentName()); diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 65fd54afbbfb..4cc147fcea03 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -509,7 +509,8 @@ public class ContentProviderHelper { checkTime(startTime, "getContentProviderImpl: before set stopped state"); mService.mPackageManagerInt.notifyComponentUsed( - cpr.appInfo.packageName, userId, callingPackage); + cpr.appInfo.packageName, userId, callingPackage, + cpr.toString()); checkTime(startTime, "getContentProviderImpl: after set stopped state"); } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index f7bbc8b50bc0..70a1c915f326 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -286,6 +286,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN + ")"); } + private String getFgsInfoForWtf() { + return " cmp: " + this.getComponentName().toShortString() + + " sdk: " + this.appInfo.targetSdkVersion + ; + } + void maybeLogFgsLogicChange() { final int origWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding, mAllowWIUInBindService); @@ -311,7 +317,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN + " OS:" // Orig-start + changeMessage(mAllowStartForegroundNoBinding, mAllowStartInBindService) + " NS:" // New-start - + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings); + + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings) + + getFgsInfoForWtf(); Slog.wtf(TAG_SERVICE, message); } @@ -1524,7 +1531,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN sb.append("ServiceRecord{") .append(Integer.toHexString(System.identityHashCode(this))) .append(" u").append(userId) - .append(' ').append(shortInstanceName).append('}'); + .append(' ').append(shortInstanceName); + if (mRecentCallingPackage != null) { + sb.append(" c:").append(mRecentCallingPackage); + } + sb.append('}'); return stringName = sb.toString(); } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 0e4465dd7f6a..1d09dce1132a 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -87,7 +87,6 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_CAMERA_NATIVE, DeviceConfig.NAMESPACE_CONFIGURATION, DeviceConfig.NAMESPACE_CONNECTIVITY, - DeviceConfig.NAMESPACE_CORE_EXPERIMENTS_TEAM_INTERNAL, DeviceConfig.NAMESPACE_EDGETPU_NATIVE, DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS, @@ -115,19 +114,29 @@ public class SettingsToPropertiesMapper { NAMESPACE_TETHERING_U_OR_LATER_NATIVE }; + // All the aconfig flags under the listed DeviceConfig scopes will be synced to native level. + @VisibleForTesting + static final String[] sDeviceConfigAconfigScopes = new String[] { + DeviceConfig.NAMESPACE_CORE_EXPERIMENTS_TEAM_INTERNAL, + }; + private final String[] mGlobalSettings; private final String[] mDeviceConfigScopes; + private final String[] mDeviceConfigAconfigScopes; + private final ContentResolver mContentResolver; @VisibleForTesting protected SettingsToPropertiesMapper(ContentResolver contentResolver, String[] globalSettings, - String[] deviceConfigScopes) { + String[] deviceConfigScopes, + String[] deviceConfigAconfigScopes) { mContentResolver = contentResolver; mGlobalSettings = globalSettings; mDeviceConfigScopes = deviceConfigScopes; + mDeviceConfigAconfigScopes = deviceConfigAconfigScopes; } @VisibleForTesting @@ -173,6 +182,36 @@ public class SettingsToPropertiesMapper { return; } setProperty(propertyName, properties.getString(key, null)); + + // for legacy namespaces, they can also be used for trunk stable + // purposes. so push flag also into trunk stable slot in sys prop, + // later all legacy usage will be refactored and the sync to old + // sys prop slot can be removed. + String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key); + if (aconfigPropertyName == null) { + log("unable to construct system property for " + scope + "/" + + key); + return; + } + setProperty(aconfigPropertyName, properties.getString(key, null)); + } + }); + } + + for (String deviceConfigAconfigScope : mDeviceConfigAconfigScopes) { + DeviceConfig.addOnPropertiesChangedListener( + deviceConfigAconfigScope, + AsyncTask.THREAD_POOL_EXECUTOR, + (DeviceConfig.Properties properties) -> { + String scope = properties.getNamespace(); + for (String key : properties.getKeyset()) { + String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key); + if (aconfigPropertyName == null) { + log("unable to construct system property for " + scope + "/" + + key); + return; + } + setProperty(aconfigPropertyName, properties.getString(key, null)); } }); } @@ -180,7 +219,10 @@ public class SettingsToPropertiesMapper { public static SettingsToPropertiesMapper start(ContentResolver contentResolver) { SettingsToPropertiesMapper mapper = new SettingsToPropertiesMapper( - contentResolver, sGlobalSettings, sDeviceConfigScopes); + contentResolver, + sGlobalSettings, + sDeviceConfigScopes, + sDeviceConfigAconfigScopes); mapper.updatePropertiesFromSettings(); return mapper; } @@ -243,6 +285,28 @@ public class SettingsToPropertiesMapper { return propertyName; } + /** + * system property name constructing rule for aconfig flags: + * "persist.device_config.aconfig_flags.[category_name].[flag_name]". + * If the name contains invalid characters or substrings for system property name, + * will return null. + * @param categoryName + * @param flagName + * @return + */ + @VisibleForTesting + static String makeAconfigFlagPropertyName(String categoryName, String flagName) { + String propertyName = SYSTEM_PROPERTY_PREFIX + "aconfig_flags." + + categoryName + "." + flagName; + + if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX) + || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) { + return null; + } + + return propertyName; + } + private void setProperty(String key, String value) { // Check if need to clear the property if (value == null) { diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index 86da39b07c0c..a17b3d579890 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -97,7 +97,7 @@ import java.util.concurrent.ScheduledExecutorService; */ public final class AppHibernationService extends SystemService { private static final String TAG = "AppHibernationService"; - private static final int PACKAGE_MATCH_FLAGS = + private static final long PACKAGE_MATCH_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index d89171d94478..4e01997e678f 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -391,7 +391,6 @@ public class AudioDeviceBroker { final boolean wasBtScoRequested = isBluetoothScoRequested(); CommunicationRouteClient client; - // Save previous client route in case of failure to start BT SCO audio AudioDeviceAttributes prevClientDevice = null; boolean prevPrivileged = false; @@ -1043,7 +1042,7 @@ public class AudioDeviceBroker { synchronized (mBluetoothAudioStateLock) { mBluetoothScoOn = on; updateAudioHalBluetoothState(); - postUpdateCommunicationRouteClient(eventSource); + postUpdateCommunicationRouteClient(isBluetoothScoRequested(), eventSource); } } @@ -1395,8 +1394,10 @@ public class AudioDeviceBroker { MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset); } - /*package*/ void postUpdateCommunicationRouteClient(String eventSource) { - sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, eventSource); + /*package*/ void postUpdateCommunicationRouteClient( + boolean wasBtScoRequested, String eventSource) { + sendILMsgNoDelay(MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, + wasBtScoRequested ? 1 : 0, eventSource); } /*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) { @@ -1708,7 +1709,8 @@ public class AudioDeviceBroker { : AudioSystem.STREAM_DEFAULT); if (btInfo.mProfile == BluetoothProfile.LE_AUDIO || btInfo.mProfile == BluetoothProfile.HEARING_AID) { - onUpdateCommunicationRouteClient("setBluetoothActiveDevice"); + onUpdateCommunicationRouteClient(isBluetoothScoRequested(), + "setBluetoothActiveDevice"); } } } @@ -1762,9 +1764,11 @@ public class AudioDeviceBroker { case MSG_I_SET_MODE_OWNER: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { + boolean wasBtScoRequested = isBluetoothScoRequested(); mAudioModeOwner = (AudioModeInfo) msg.obj; if (mAudioModeOwner.mMode != AudioSystem.MODE_RINGTONE) { - onUpdateCommunicationRouteClient("setNewModeOwner"); + onUpdateCommunicationRouteClient( + wasBtScoRequested, "setNewModeOwner"); } } } @@ -1787,10 +1791,10 @@ public class AudioDeviceBroker { } break; - case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT: + case MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { - onUpdateCommunicationRouteClient((String) msg.obj); + onUpdateCommunicationRouteClient(msg.arg1 == 1, (String) msg.obj); } } break; @@ -1971,7 +1975,7 @@ public class AudioDeviceBroker { private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38; private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42; - private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43; + private static final int MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43; private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44; private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45; @@ -2328,16 +2332,20 @@ public class AudioDeviceBroker { */ // @GuardedBy("mSetModeLock") @GuardedBy("mDeviceStateLock") - private void onUpdateCommunicationRouteClient(String eventSource) { - updateCommunicationRoute(eventSource); + private void onUpdateCommunicationRouteClient(boolean wasBtScoRequested, String eventSource) { CommunicationRouteClient crc = topCommunicationRouteClient(); if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "onUpdateCommunicationRouteClient, crc: " - + crc + " eventSource: " + eventSource); + Log.v(TAG, "onUpdateCommunicationRouteClient, crc: " + crc + + " wasBtScoRequested: " + wasBtScoRequested + " eventSource: " + eventSource); } if (crc != null) { setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(), BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource); + } else { + if (!isBluetoothScoRequested() && wasBtScoRequested) { + mBtHelper.stopBluetoothSco(eventSource); + } + updateCommunicationRoute(eventSource); } } @@ -2431,6 +2439,7 @@ public class AudioDeviceBroker { List<AudioRecordingConfiguration> recordConfigs) { synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { + final boolean wasBtScoRequested = isBluetoothScoRequested(); boolean updateCommunicationRoute = false; for (CommunicationRouteClient crc : mCommunicationRouteClients) { boolean wasActive = crc.isActive(); @@ -2459,7 +2468,8 @@ public class AudioDeviceBroker { } } if (updateCommunicationRoute) { - postUpdateCommunicationRouteClient("updateCommunicationRouteClientsActivity"); + postUpdateCommunicationRouteClient( + wasBtScoRequested, "updateCommunicationRouteClientsActivity"); } } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 76c4cfe929bb..0bc047271bab 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7636,6 +7636,10 @@ public class AudioService extends IAudioService.Stub throw new IllegalArgumentException("Illegal BluetoothProfile profile for device " + previousDevice + " -> " + newDevice + ". Got: " + profile); } + + sDeviceLogger.enqueue(new EventLogger.StringEvent("BlutoothActiveDeviceChanged for " + + BluetoothProfile.getProfileName(profile) + ", device update " + previousDevice + + " -> " + newDevice)); AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData(newDevice, previousDevice, info, "AudioService"); @@ -9685,6 +9689,9 @@ public class AudioService extends IAudioService.Stub } } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); + sDeviceLogger.enqueue(new EventLogger.StringEvent( + "BluetoothAdapter ACTION_STATE_CHANGED with state " + state)); + if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_TURNING_OFF) { mDeviceBroker.disconnectAllBluetoothProfiles(); @@ -10742,6 +10749,27 @@ public class AudioService extends IAudioService.Stub @Override @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public boolean isCsdAsAFeatureAvailable() { + super.isCsdAsAFeatureAvailable_enforcePermission(); + return mSoundDoseHelper.isCsdAsAFeatureAvailable(); + } + + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public boolean isCsdAsAFeatureEnabled() { + super.isCsdAsAFeatureEnabled_enforcePermission(); + return mSoundDoseHelper.isCsdAsAFeatureEnabled(); + } + + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public void setCsdAsAFeatureEnabled(boolean csdToggleValue) { + super.setCsdAsAFeatureEnabled_enforcePermission(); + mSoundDoseHelper.setCsdAsAFeatureEnabled(csdToggleValue); + } + + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle, @AudioDeviceCategory int btAudioDeviceCategory) { super.setBluetoothAudioDeviceCategory_enforcePermission(); diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 3560797ce2cf..b350363f32e1 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -329,7 +329,7 @@ public class BtHelper { default: break; } - if(broadcast) { + if (broadcast) { broadcastScoConnectionState(scoAudioState); //FIXME: this is to maintain compatibility with deprecated intent // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. @@ -459,6 +459,8 @@ public class BtHelper { //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onBtProfileDisconnected(int profile) { + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "BT profile " + BluetoothProfile.getProfileName(profile) + " disconnected")); switch (profile) { case BluetoothProfile.A2DP: mA2dp = null; @@ -487,6 +489,9 @@ public class BtHelper { @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) { + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy " + + proxy)); if (profile == BluetoothProfile.HEADSET) { onHeadsetProfileConnected((BluetoothHeadset) proxy); return; @@ -718,8 +723,10 @@ public class BtHelper { checkScoAudioState(); if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { // Make sure that the state transitions to CONNECTING even if we cannot initiate - // the connection. - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); + // the connection except if already connected internally + if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); + } switch (mScoAudioState) { case SCO_STATE_INACTIVE: mScoAudioMode = scoAudioMode; @@ -775,7 +782,7 @@ public class BtHelper { broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); break; case SCO_STATE_ACTIVE_INTERNAL: - Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return"); + // Already in ACTIVE mode, simply return break; case SCO_STATE_ACTIVE_EXTERNAL: /* Confirm SCO Audio connection to requesting app as it is already connected diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 1578193e6e7c..95b6c2cef963 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -37,13 +37,11 @@ import android.media.ISoundDose; import android.media.ISoundDoseCallback; import android.media.SoundDoseRecord; import android.os.Binder; -import android.os.HandlerExecutor; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -143,8 +141,6 @@ public class SoundDoseHelper { private static final int SAFE_MEDIA_VOLUME_UNINITIALIZED = -1; - private static final String FEATURE_FLAG_ENABLE_CSD = "enable_csd"; - private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE, "CSD updates"); @@ -193,7 +189,15 @@ public class SoundDoseHelper { private final AtomicBoolean mEnableCsd = new AtomicBoolean(false); - private ArrayList<ISoundDose.AudioDeviceCategory> mCachedAudioDeviceCategories = + private final Object mCsdAsAFeatureLock = new Object(); + + @GuardedBy("mCsdAsAFeatureLock") + private boolean mIsCsdAsAFeatureAvailable = false; + + @GuardedBy("mCsdAsAFeatureLock") + private boolean mIsCsdAsAFeatureEnabled = false; + + private final ArrayList<ISoundDose.AudioDeviceCategory> mCachedAudioDeviceCategories = new ArrayList<>(); private final Object mCsdStateLock = new Object(); @@ -315,10 +319,6 @@ public class SoundDoseHelper { mAlarmManager = (AlarmManager) mContext.getSystemService( Context.ALARM_SERVICE); - - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_MEDIA, - new HandlerExecutor(mAudioHandler), - p -> updateCsdEnabled("onPropertiesChanged")); } void initSafeVolumes() { @@ -493,6 +493,38 @@ public class SoundDoseHelper { return false; } + boolean isCsdAsAFeatureAvailable() { + synchronized (mCsdAsAFeatureLock) { + return mIsCsdAsAFeatureAvailable; + } + } + + boolean isCsdAsAFeatureEnabled() { + synchronized (mCsdAsAFeatureLock) { + return mIsCsdAsAFeatureEnabled; + } + } + + void setCsdAsAFeatureEnabled(boolean csdAsAFeatureEnabled) { + boolean doUpdate; + synchronized (mCsdAsAFeatureLock) { + doUpdate = mIsCsdAsAFeatureEnabled != csdAsAFeatureEnabled && mIsCsdAsAFeatureAvailable; + mIsCsdAsAFeatureEnabled = csdAsAFeatureEnabled; + final long callingIdentity = Binder.clearCallingIdentity(); + try { + mSettings.putSecureIntForUser(mAudioService.getContentResolver(), + Settings.Secure.AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED, + mIsCsdAsAFeatureEnabled ? 1 : 0, UserHandle.USER_CURRENT); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + if (doUpdate) { + updateCsdEnabled("setCsdAsAFeatureEnabled"); + } + } + void setAudioDeviceCategory(String address, int internalAudioType, boolean isHeadphone) { if (!mEnableCsd.get()) { return; @@ -863,6 +895,13 @@ public class SoundDoseHelper { Log.e(TAG, "Exception while forcing the internal MEL computation", e); } + synchronized (mCsdAsAFeatureLock) { + mIsCsdAsAFeatureEnabled = mSettings.getSecureIntForUser( + mAudioService.getContentResolver(), + Settings.Secure.AUDIO_SAFE_CSD_AS_A_FEATURE_ENABLED, 0, + UserHandle.USER_CURRENT) != 0; + } + synchronized (mCsdStateLock) { if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) { mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L; @@ -907,18 +946,23 @@ public class SoundDoseHelper { @GuardedBy("mSafeMediaVolumeStateLock") private void updateSafeMediaVolume_l(String caller) { - boolean safeMediaVolumeEnabled = - SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_FORCE, false) - || (mContext.getResources().getBoolean( - com.android.internal.R.bool.config_safe_media_volume_enabled) - && !mEnableCsd.get()); boolean safeMediaVolumeBypass = - SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_BYPASS, false); + SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_BYPASS, false) + || mEnableCsd.get(); + boolean safeMediaVolumeForce = SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_FORCE, + false); + // we are using the MCC overlaid legacy flag used for the safe volume enablement + // to determine whether the MCC enforces any safe hearing standard. + boolean mccEnforcedSafeMediaVolume = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_safe_media_volume_enabled); + + boolean safeVolumeEnabled = + (mccEnforcedSafeMediaVolume || safeMediaVolumeForce) && !safeMediaVolumeBypass; // The persisted state is either "disabled" or "active": this is the state applied // next time we boot and cannot be "inactive" int persistedState; - if (safeMediaVolumeEnabled && !safeMediaVolumeBypass) { + if (safeVolumeEnabled) { persistedState = SAFE_MEDIA_VOLUME_ACTIVE; // The state can already be "inactive" here if the user has forced it before // the 30 seconds timeout for forced configuration. In this case we don't reset @@ -945,22 +989,28 @@ public class SoundDoseHelper { } private void updateCsdEnabled(String caller) { - boolean newEnableCsd = SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_CSD_FORCE, - false); - if (!newEnableCsd) { - final String featureFlagEnableCsdValue = DeviceConfig.getProperty( - DeviceConfig.NAMESPACE_MEDIA, - FEATURE_FLAG_ENABLE_CSD); - if (featureFlagEnableCsdValue != null) { - newEnableCsd = Boolean.parseBoolean(featureFlagEnableCsdValue); + boolean csdForce = SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_CSD_FORCE, false); + // we are using the MCC overlaid legacy flag used for the safe volume enablement + // to determine whether the MCC enforces any safe hearing standard. + boolean mccEnforcedSafeMedia = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_safe_media_volume_enabled); + boolean csdEnable = mContext.getResources().getBoolean( + R.bool.config_safe_sound_dosage_enabled); + boolean newEnabledCsd = (mccEnforcedSafeMedia && csdEnable) || csdForce; + + synchronized (mCsdAsAFeatureLock) { + if (!mccEnforcedSafeMedia && csdEnable) { + mIsCsdAsAFeatureAvailable = true; + newEnabledCsd = mIsCsdAsAFeatureEnabled || csdForce; + Log.v(TAG, caller + ": CSD as a feature is not enforced and enabled: " + + newEnabledCsd); } else { - newEnableCsd = mContext.getResources().getBoolean( - R.bool.config_safe_sound_dosage_enabled); + mIsCsdAsAFeatureAvailable = false; } } - if (mEnableCsd.compareAndSet(!newEnableCsd, newEnableCsd)) { - Log.i(TAG, caller + ": enable CSD " + newEnableCsd); + if (mEnableCsd.compareAndSet(!newEnabledCsd, newEnabledCsd)) { + Log.i(TAG, caller + ": enabled CSD " + newEnabledCsd); initCsd(); synchronized (mSafeMediaVolumeStateLock) { diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStats.java b/services/core/java/com/android/server/biometrics/AuthenticationStats.java index 137a418e31ab..e109cc8011e7 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationStats.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationStats.java @@ -22,6 +22,8 @@ package com.android.server.biometrics; */ public class AuthenticationStats { + private static final float FRR_NOT_ENOUGH_ATTEMPTS = -1.0f; + private final int mUserId; private int mTotalAttempts; private int mRejectedAttempts; @@ -70,7 +72,7 @@ public class AuthenticationStats { if (mTotalAttempts > 0) { return mRejectedAttempts / (float) mTotalAttempts; } else { - return -1.0f; + return FRR_NOT_ENOUGH_ATTEMPTS; } } @@ -87,4 +89,32 @@ public class AuthenticationStats { mTotalAttempts = 0; mRejectedAttempts = 0; } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof AuthenticationStats)) { + return false; + } + + AuthenticationStats target = (AuthenticationStats) obj; + return this.getUserId() == target.getUserId() + && this.getTotalAttempts() + == target.getTotalAttempts() + && this.getRejectedAttempts() + == target.getRejectedAttempts() + && this.getEnrollmentNotifications() + == target.getEnrollmentNotifications() + && this.getModality() == target.getModality(); + } + + @Override + public int hashCode() { + return String.format("userId: %d, totalAttempts: %d, rejectedAttempts: %d, " + + "enrollmentNotifications: %d, modality: %d", mUserId, mTotalAttempts, + mRejectedAttempts, mEnrollmentNotifications, mModality).hashCode(); + } } diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java index c9cd8148c0f1..97e5c6fbd8c3 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java @@ -18,10 +18,18 @@ package com.android.server.biometrics; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; +import android.os.UserHandle; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.biometrics.sensors.BiometricNotification; import java.util.HashMap; import java.util.Map; @@ -37,23 +45,60 @@ public class AuthenticationStatsCollector { // The minimum number of attempts that will calculate the FRR and trigger the notification. private static final int MINIMUM_ATTEMPTS = 500; + // Upload the data every 50 attempts (average number of daily authentications). + private static final int AUTHENTICATION_UPLOAD_INTERVAL = 50; // The maximum number of eligible biometric enrollment notification can be sent. private static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2; + @NonNull private final Context mContext; + private final float mThreshold; private final int mModality; @NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap; - public AuthenticationStatsCollector(@NonNull Context context, int modality) { + // TODO(b/295582896): Find a way to make this NonNull + @Nullable private AuthenticationStatsPersister mAuthenticationStatsPersister; + @NonNull private BiometricNotification mBiometricNotification; + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(@NonNull Context context, @NonNull Intent intent) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId != UserHandle.USER_NULL + && intent.getAction().equals(Intent.ACTION_USER_REMOVED)) { + onUserRemoved(userId); + } + } + }; + + public AuthenticationStatsCollector(@NonNull Context context, int modality, + @NonNull BiometricNotification biometricNotification) { + mContext = context; mThreshold = context.getResources() .getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1); mUserAuthenticationStatsMap = new HashMap<>(); mModality = modality; + mBiometricNotification = biometricNotification; + + context.registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED)); + } + + private void initializeUserAuthenticationStatsMap() { + mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext); + for (AuthenticationStats stats : mAuthenticationStatsPersister.getAllFrrStats(mModality)) { + mUserAuthenticationStatsMap.put(stats.getUserId(), stats); + } } /** Update total authentication and rejected attempts. */ public void authenticate(int userId, boolean authenticated) { + // SharedPreference is not ready when starting system server, initialize + // mUserAuthenticationStatsMap in authentication to ensure SharedPreference + // is ready for application use. + if (mUserAuthenticationStatsMap.isEmpty()) { + initializeUserAuthenticationStatsMap(); + } // Check if this is a new user. if (!mUserAuthenticationStatsMap.containsKey(userId)) { mUserAuthenticationStatsMap.put(userId, new AuthenticationStats(userId, mModality)); @@ -67,24 +112,65 @@ public class AuthenticationStatsCollector { sendNotificationIfNeeded(userId); } + /** Check if a notification should be sent after a calculation cycle. */ private void sendNotificationIfNeeded(int userId) { AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId); - if (authenticationStats.getTotalAttempts() >= MINIMUM_ATTEMPTS) { - // Send notification if FRR exceeds the threshold - if (authenticationStats.getEnrollmentNotifications() < MAXIMUM_ENROLLMENT_NOTIFICATIONS - && authenticationStats.getFrr() >= mThreshold) { - // TODO(wenhuiy): Send notifications. - } + if (authenticationStats.getTotalAttempts() < MINIMUM_ATTEMPTS) { + return; + } + // Don't send notification if FRR below the threshold. + if (authenticationStats.getEnrollmentNotifications() >= MAXIMUM_ENROLLMENT_NOTIFICATIONS + || authenticationStats.getFrr() < mThreshold) { authenticationStats.resetData(); + return; + } + + authenticationStats.resetData(); + + final PackageManager packageManager = mContext.getPackageManager(); + + // Don't send notification to single-modality devices. + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT) + || !packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { + return; + } + + final FaceManager faceManager = mContext.getSystemService(FaceManager.class); + final boolean hasEnrolledFace = faceManager.hasEnrolledTemplates(userId); + + final FingerprintManager fingerprintManager = mContext + .getSystemService(FingerprintManager.class); + final boolean hasEnrolledFingerprint = fingerprintManager.hasEnrolledTemplates(userId); + + // Don't send notification when both face and fingerprint are enrolled. + if (hasEnrolledFace && hasEnrolledFingerprint) { + return; + } + if (hasEnrolledFace && !hasEnrolledFingerprint) { + mBiometricNotification.sendFpEnrollNotification(mContext); + } else if (!hasEnrolledFace && hasEnrolledFingerprint) { + mBiometricNotification.sendFaceEnrollNotification(mContext); } } private void persistDataIfNeeded(int userId) { AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId); - if (authenticationStats.getTotalAttempts() % 50 == 0) { - // TODO(wenhuiy): Persist data. + if (authenticationStats.getTotalAttempts() % AUTHENTICATION_UPLOAD_INTERVAL == 0) { + mAuthenticationStatsPersister.persistFrrStats(authenticationStats.getUserId(), + authenticationStats.getTotalAttempts(), + authenticationStats.getRejectedAttempts(), + authenticationStats.getEnrollmentNotifications(), + authenticationStats.getModality()); + } + } + + private void onUserRemoved(final int userId) { + if (mAuthenticationStatsPersister == null) { + initializeUserAuthenticationStatsMap(); } + mUserAuthenticationStatsMap.remove(userId); + mAuthenticationStatsPersister.removeFrrStats(userId); } /** diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java new file mode 100644 index 000000000000..21e93a8bc024 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.SharedPreferences; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.os.Environment; +import android.util.Slog; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Persists and retrieves stats for Biometric Authentication. + * Authentication stats include userId, total attempts, rejected attempts, + * and the number of sent enrollment notifications. + * Data are stored in SharedPreferences in a form of a set of JSON objects, + * where it's one element per user. + */ +public class AuthenticationStatsPersister { + + private static final String TAG = "AuthenticationStatsPersister"; + private static final String FILE_NAME = "authentication_stats"; + private static final String USER_ID = "user_id"; + private static final String FACE_ATTEMPTS = "face_attempts"; + private static final String FACE_REJECTIONS = "face_rejections"; + private static final String FINGERPRINT_ATTEMPTS = "fingerprint_attempts"; + private static final String FINGERPRINT_REJECTIONS = "fingerprint_rejections"; + private static final String ENROLLMENT_NOTIFICATIONS = "enrollment_notifications"; + private static final String KEY = "frr_stats"; + + @NonNull private final SharedPreferences mSharedPreferences; + + AuthenticationStatsPersister(@NonNull Context context) { + // The package info in the context isn't initialized in the way it is for normal apps, + // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we + // build the path manually below using the same policy that appears in ContextImpl. + final File prefsFile = new File(Environment.getDataSystemDeDirectory(), FILE_NAME); + mSharedPreferences = context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE); + } + + /** + * Get all frr data from SharedPreference. + */ + public List<AuthenticationStats> getAllFrrStats(int modality) { + List<AuthenticationStats> authenticationStatsList = new ArrayList<>(); + for (String frrStats : readFrrStats()) { + try { + JSONObject frrStatsJson = new JSONObject(frrStats); + if (modality == BiometricsProtoEnums.MODALITY_FACE) { + authenticationStatsList.add(new AuthenticationStats( + getIntValue(frrStatsJson, USER_ID, -1 /* defaultValue */), + getIntValue(frrStatsJson, FACE_ATTEMPTS), + getIntValue(frrStatsJson, FACE_REJECTIONS), + getIntValue(frrStatsJson, ENROLLMENT_NOTIFICATIONS), + modality)); + } else if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) { + authenticationStatsList.add(new AuthenticationStats( + getIntValue(frrStatsJson, USER_ID, -1 /* defaultValue */), + getIntValue(frrStatsJson, FINGERPRINT_ATTEMPTS), + getIntValue(frrStatsJson, FINGERPRINT_REJECTIONS), + getIntValue(frrStatsJson, ENROLLMENT_NOTIFICATIONS), + modality)); + } + } catch (JSONException e) { + Slog.w(TAG, String.format("Unable to resolve authentication stats JSON: %s", + frrStats)); + } + } + return authenticationStatsList; + } + + /** + * Remove frr data for a specific user. + */ + public void removeFrrStats(int userId) { + try { + // Copy into a new HashSet to allow modification. + Set<String> frrStatsSet = new HashSet<>(readFrrStats()); + + // Remove the old authentication stat for the user if it exists. + for (Iterator<String> iterator = frrStatsSet.iterator(); iterator.hasNext();) { + String frrStats = iterator.next(); + JSONObject frrStatJson = new JSONObject(frrStats); + if (getValue(frrStatJson, USER_ID).equals(String.valueOf(userId))) { + iterator.remove(); + break; + } + } + + mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply(); + } catch (JSONException ignored) { + } + } + + /** + * Persist frr data for a specific user. + */ + public void persistFrrStats(int userId, int totalAttempts, int rejectedAttempts, + int enrollmentNotifications, int modality) { + try { + // Copy into a new HashSet to allow modification. + Set<String> frrStatsSet = new HashSet<>(readFrrStats()); + + // Remove the old authentication stat for the user if it exists. + JSONObject frrStatJson = null; + for (Iterator<String> iterator = frrStatsSet.iterator(); iterator.hasNext();) { + String frrStats = iterator.next(); + frrStatJson = new JSONObject(frrStats); + if (getValue(frrStatJson, USER_ID).equals(String.valueOf(userId))) { + iterator.remove(); + break; + } + } + + // If there's existing frr stats in the file, we want to update the stats for the given + // modality and keep the stats for other modalities. + if (frrStatJson != null) { + frrStatsSet.add(buildFrrStats(frrStatJson, totalAttempts, rejectedAttempts, + enrollmentNotifications, modality)); + } else { + frrStatsSet.add(buildFrrStats(userId, totalAttempts, rejectedAttempts, + enrollmentNotifications, modality)); + } + + mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply(); + + } catch (JSONException e) { + Slog.e(TAG, "Unable to persist authentication stats"); + } + } + + private Set<String> readFrrStats() { + return mSharedPreferences.getStringSet(KEY, Set.of()); + } + + // Update frr stats for existing frrStats JSONObject and build the new string. + private String buildFrrStats(JSONObject frrStats, int totalAttempts, int rejectedAttempts, + int enrollmentNotifications, int modality) throws JSONException { + if (modality == BiometricsProtoEnums.MODALITY_FACE) { + return frrStats + .put(FACE_ATTEMPTS, totalAttempts) + .put(FACE_REJECTIONS, rejectedAttempts) + .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications) + .toString(); + } else if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) { + return frrStats + .put(FINGERPRINT_ATTEMPTS, totalAttempts) + .put(FINGERPRINT_REJECTIONS, rejectedAttempts) + .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications) + .toString(); + } else { + return frrStats.toString(); + } + } + + // Build string for new user and new authentication stats. + private String buildFrrStats(int userId, int totalAttempts, int rejectedAttempts, + int enrollmentNotifications, int modality) + throws JSONException { + if (modality == BiometricsProtoEnums.MODALITY_FACE) { + return new JSONObject() + .put(USER_ID, userId) + .put(FACE_ATTEMPTS, totalAttempts) + .put(FACE_REJECTIONS, rejectedAttempts) + .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications) + .toString(); + } else if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) { + return new JSONObject() + .put(USER_ID, userId) + .put(FINGERPRINT_ATTEMPTS, totalAttempts) + .put(FINGERPRINT_REJECTIONS, rejectedAttempts) + .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications) + .toString(); + } else { + return ""; + } + } + + private String getValue(JSONObject jsonObject, String key) throws JSONException { + return jsonObject.has(key) ? jsonObject.getString(key) : ""; + } + + private int getIntValue(JSONObject jsonObject, String key) throws JSONException { + return getIntValue(jsonObject, key, 0 /* defaultValue */); + } + + private int getIntValue(JSONObject jsonObject, String key, int defaultValue) + throws JSONException { + return jsonObject.has(key) ? jsonObject.getInt(key) : defaultValue; + } +} diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java index 4a10e8e428a3..f78ca43253f6 100644 --- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java +++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java @@ -103,8 +103,14 @@ public class OperationContextExt { */ @NonNull public OperationContext toAidlContext(@NonNull FingerprintAuthenticateOptions options) { - mAidlContext.authenticateReason = AuthenticateReason - .fingerprintAuthenticateReason(getAuthReason(options)); + if (options.getVendorReason() != null) { + mAidlContext.authenticateReason = AuthenticateReason + .vendorAuthenticateReason(options.getVendorReason()); + + } else { + mAidlContext.authenticateReason = AuthenticateReason + .fingerprintAuthenticateReason(getAuthReason(options)); + } mAidlContext.wakeReason = getWakeReason(options); return mAidlContext; diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotification.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotification.java new file mode 100644 index 000000000000..90e18604d945 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotification.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.annotation.NonNull; +import android.content.Context; + +/** + * Interface for biometrics to send notifications. + */ +public interface BiometricNotification { + + /** + * Sends a face enrollment notification. + */ + void sendFaceEnrollNotification(@NonNull Context context); + + /** + * Sends a fingerprint enrollment notification. + */ + void sendFpEnrollNotification(@NonNull Context context); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationImpl.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationImpl.java new file mode 100644 index 000000000000..7b420468f628 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationImpl.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.annotation.NonNull; +import android.content.Context; + +import com.android.server.biometrics.AuthenticationStatsCollector; + +/** + * Implementation to send biometric notifications for {@link AuthenticationStatsCollector}. + */ +public class BiometricNotificationImpl implements BiometricNotification { + + @Override + public void sendFaceEnrollNotification(@NonNull Context context) { + BiometricNotificationUtils.showFaceEnrollNotification(context); + } + + @Override + public void sendFpEnrollNotification(@NonNull Context context) { + BiometricNotificationUtils.showFingerprintEnrollNotification(context); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java index f516a4930a58..2ff695d7b85d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java @@ -35,9 +35,22 @@ import com.android.internal.R; public class BiometricNotificationUtils { private static final String TAG = "BiometricNotificationUtils"; - private static final String RE_ENROLL_NOTIFICATION_TAG = "FaceService"; - private static final String BAD_CALIBRATION_NOTIFICATION_TAG = "FingerprintService"; + private static final String FACE_RE_ENROLL_NOTIFICATION_TAG = "FaceReEnroll"; + private static final String FACE_ENROLL_NOTIFICATION_TAG = "FaceEnroll"; + private static final String FINGERPRINT_ENROLL_NOTIFICATION_TAG = "FingerprintEnroll"; + private static final String BAD_CALIBRATION_NOTIFICATION_TAG = "FingerprintBadCalibration"; private static final String KEY_RE_ENROLL_FACE = "re_enroll_face_unlock"; + private static final String FACE_SETTINGS_ACTION = "android.settings.FACE_SETTINGS"; + private static final String FINGERPRINT_SETTINGS_ACTION = + "android.settings.FINGERPRINT_SETTINGS"; + private static final String FACE_ENROLL_ACTION = "android.settings.FACE_ENROLL"; + private static final String FINGERPRINT_ENROLL_ACTION = "android.settings.FINGERPRINT_ENROLL"; + private static final String SETTINGS_PACKAGE = "com.android.settings"; + private static final String FACE_ENROLL_CHANNEL = "FaceEnrollNotificationChannel"; + private static final String FACE_RE_ENROLL_CHANNEL = "FaceReEnrollNotificationChannel"; + private static final String FINGERPRINT_ENROLL_CHANNEL = "FingerprintEnrollNotificationChannel"; + private static final String FINGERPRINT_BAD_CALIBRATION_CHANNEL = + "FingerprintBadCalibrationNotificationChannel"; private static final int NOTIFICATION_ID = 1; private static final long NOTIFICATION_INTERVAL_MS = 24 * 60 * 60 * 1000; private static long sLastAlertTime = 0; @@ -56,18 +69,67 @@ public class BiometricNotificationUtils { final String content = context.getString(R.string.face_recalibrate_notification_content); - final Intent intent = new Intent("android.settings.FACE_SETTINGS"); - intent.setPackage("com.android.settings"); + final Intent intent = new Intent(FACE_SETTINGS_ACTION); + intent.setPackage(SETTINGS_PACKAGE); intent.putExtra(KEY_RE_ENROLL_FACE, true); final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, null /* options */, UserHandle.CURRENT); - final String channelName = "FaceEnrollNotificationChannel"; + showNotificationHelper(context, name, title, content, pendingIntent, FACE_RE_ENROLL_CHANNEL, + FACE_RE_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET); + } + + /** + * Shows a face enrollment notification. + */ + public static void showFaceEnrollNotification(@NonNull Context context) { + + final String name = + context.getString(R.string.device_unlock_notification_name); + final String title = + context.getString(R.string.alternative_unlock_setup_notification_title); + final String content = + context.getString(R.string.alternative_face_setup_notification_content); + + final Intent intent = new Intent(FACE_ENROLL_ACTION); + intent.setPackage(SETTINGS_PACKAGE); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + + final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, + 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, + null /* options */, UserHandle.CURRENT); + + showNotificationHelper(context, name, title, content, pendingIntent, FACE_ENROLL_CHANNEL, + FACE_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC); + } + + /** + * Shows a fingerprint enrollment notification. + */ + public static void showFingerprintEnrollNotification(@NonNull Context context) { + + final String name = + context.getString(R.string.device_unlock_notification_name); + final String title = + context.getString(R.string.alternative_unlock_setup_notification_title); + final String content = + context.getString(R.string.alternative_fp_setup_notification_content); + + final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION); + intent.setPackage(SETTINGS_PACKAGE); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + + final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, + 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, + null /* options */, UserHandle.CURRENT); - showNotificationHelper(context, name, title, content, pendingIntent, channelName, - RE_ENROLL_NOTIFICATION_TAG); + showNotificationHelper(context, name, title, content, pendingIntent, + FINGERPRINT_ENROLL_CHANNEL, FINGERPRINT_ENROLL_NOTIFICATION_TAG, + Notification.VISIBILITY_PUBLIC); } /** @@ -93,22 +155,21 @@ public class BiometricNotificationUtils { final String content = context.getString(R.string.fingerprint_recalibrate_notification_content); - final Intent intent = new Intent("android.settings.FINGERPRINT_SETTINGS"); - intent.setPackage("com.android.settings"); + final Intent intent = new Intent(FINGERPRINT_SETTINGS_ACTION); + intent.setPackage(SETTINGS_PACKAGE); final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, null /* options */, UserHandle.CURRENT); - final String channelName = "FingerprintBadCalibrationNotificationChannel"; - - showNotificationHelper(context, name, title, content, pendingIntent, channelName, - BAD_CALIBRATION_NOTIFICATION_TAG); + showNotificationHelper(context, name, title, content, pendingIntent, + FINGERPRINT_BAD_CALIBRATION_CHANNEL, BAD_CALIBRATION_NOTIFICATION_TAG, + Notification.VISIBILITY_SECRET); } private static void showNotificationHelper(Context context, String name, String title, String content, PendingIntent pendingIntent, String channelName, - String notificationTag) { + String notificationTag, int visibility) { final NotificationManager notificationManager = context.getSystemService(NotificationManager.class); final NotificationChannel channel = new NotificationChannel(channelName, name, @@ -123,7 +184,7 @@ public class BiometricNotificationUtils { .setAutoCancel(true) .setCategory(Notification.CATEGORY_SYSTEM) .setContentIntent(pendingIntent) - .setVisibility(Notification.VISIBILITY_SECRET) + .setVisibility(visibility) .build(); notificationManager.createNotificationChannel(channel); @@ -134,10 +195,30 @@ public class BiometricNotificationUtils { /** * Cancels a face re-enrollment notification */ - public static void cancelReEnrollNotification(@NonNull Context context) { + public static void cancelFaceReEnrollNotification(@NonNull Context context) { + final NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + notificationManager.cancelAsUser(FACE_RE_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID, + UserHandle.CURRENT); + } + + /** + * Cancels a face enrollment notification + */ + public static void cancelFaceEnrollNotification(@NonNull Context context) { + final NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + notificationManager.cancelAsUser(FACE_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID, + UserHandle.CURRENT); + } + + /** + * Cancels a fingerprint enrollment notification + */ + public static void cancelFingerprintEnrollNotification(@NonNull Context context) { final NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - notificationManager.cancelAsUser(RE_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID, + notificationManager.cancelAsUser(FINGERPRINT_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.CURRENT); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index 722c9afbeaf8..f55cf0549382 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -109,7 +109,8 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> { public void start(@NonNull ClientMonitorCallback callback) { super.start(callback); - BiometricNotificationUtils.cancelReEnrollNotification(getContext()); + BiometricNotificationUtils.cancelFaceEnrollNotification(getContext()); + BiometricNotificationUtils.cancelFaceReEnrollNotification(getContext()); } @NonNull diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index a7d160c4fa60..28f0a4dadbd5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -56,6 +56,7 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -177,7 +178,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mDaemon = daemon; mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FACE); + BiometricsProtoEnums.MODALITY_FACE, new BiometricNotificationImpl()); for (SensorProps prop : props) { final int sensorId = prop.commonProps.sensorId; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 10991d5f9133..808626120c1e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -62,7 +62,7 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; -import com.android.server.biometrics.sensors.BiometricNotificationUtils; +import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -367,7 +367,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { }); mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FACE); + BiometricsProtoEnums.MODALITY_FACE, new BiometricNotificationImpl()); try { ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); @@ -615,8 +615,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); - BiometricNotificationUtils.cancelReEnrollNotification(mContext); - final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java index 16d2f7a03c6d..27b9c79516af 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java @@ -33,6 +33,7 @@ import com.android.internal.R; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -71,6 +72,14 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); } + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + + BiometricNotificationUtils.cancelFaceEnrollNotification(getContext()); + BiometricNotificationUtils.cancelFaceReEnrollNotification(getContext()); + } + @NonNull @Override protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index f9e08d69ef48..46ff6b4fab1a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -104,6 +104,13 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps } } + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + + BiometricNotificationUtils.cancelFingerprintEnrollNotification(getContext()); + } + @NonNull @Override protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 2d062db12cdc..5f4b89439fd0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -64,6 +64,7 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -184,7 +185,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mDaemon = daemon; mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FINGERPRINT); + BiometricsProtoEnums.MODALITY_FINGERPRINT, new BiometricNotificationImpl()); final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 4b07dca75e9e..d0b71fcf2dbb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -66,6 +66,7 @@ import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.BiometricNotificationImpl; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -354,7 +355,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider }); mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - BiometricsProtoEnums.MODALITY_FINGERPRINT); + BiometricsProtoEnums.MODALITY_FINGERPRINT, new BiometricNotificationImpl()); try { ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 6fee84a5e057..382e7e2121f4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -81,6 +81,13 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint } } + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + + BiometricNotificationUtils.cancelFingerprintEnrollNotification(getContext()); + } + @NonNull @Override protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) { diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 3e31bd1e820f..0aac7c20b822 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -220,8 +220,10 @@ public class SyncManager { private static final int SYNC_OP_STATE_INVALID_SYNC_DISABLED = 5; /** Flags used when connecting to a sync adapter service */ - private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE - | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT; + private static final Context.BindServiceFlags SYNC_ADAPTER_CONNECTION_FLAGS = + Context.BindServiceFlags.of( + Context.BIND_FILTER_OUT_QUARANTINED_COMPONENTS | Context.BIND_AUTO_CREATE + | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT); /** Singleton instance. */ @GuardedBy("SyncManager.class") diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index d57dc471694e..8642fb888556 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -16,6 +16,9 @@ package com.android.server.display; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + import android.annotation.Nullable; import android.content.Context; import android.graphics.Point; @@ -132,12 +135,15 @@ abstract class DisplayDevice { /** * Returns the default size of the surface associated with the display, or null if the surface * is not provided for layer mirroring by SurfaceFlinger. For non virtual displays, this will - * be the actual display device's size. + * be the actual display device's size, reflecting the current rotation. */ @Nullable public Point getDisplaySurfaceDefaultSizeLocked() { DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked(); - return new Point(displayDeviceInfo.width, displayDeviceInfo.height); + final boolean isRotated = mCurrentOrientation == ROTATION_90 + || mCurrentOrientation == ROTATION_270; + return isRotated ? new Point(displayDeviceInfo.height, displayDeviceInfo.width) + : new Point(displayDeviceInfo.width, displayDeviceInfo.height); } /** @@ -358,7 +364,7 @@ abstract class DisplayDevice { } boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90 - || mCurrentOrientation == Surface.ROTATION_270); + || mCurrentOrientation == ROTATION_270); DisplayDeviceInfo info = getDisplayDeviceInfoLocked(); viewport.deviceWidth = isRotated ? info.height : info.width; viewport.deviceHeight = isRotated ? info.width : info.height; diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index c131226a60dd..01eceda202d2 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -2093,7 +2093,7 @@ public class DisplayDeviceConfig { /** Loads the refresh rate profiles. */ private void loadRefreshRateZoneProfiles(RefreshRateConfigs refreshRateConfigs) { - if (refreshRateConfigs == null) { + if (refreshRateConfigs == null || refreshRateConfigs.getRefreshRateZoneProfiles() == null) { return; } for (RefreshRateZone zone : diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index 4ad26c46d7ed..7ea576d1ed3a 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -63,10 +63,15 @@ public class DisplayWhiteBalanceController implements // high errors. This default is introduced to provide a fixed display color // temperature when sensor readings become unreliable. private final float mLowLightAmbientColorTemperature; + // As above, but used when in strong mode (idle screen brightness mode). + private final float mLowLightAmbientColorTemperatureStrong; + // In high brightness conditions certain color temperatures can cause peak display // brightness to drop. This fixed color temperature can be used to compensate for // this effect. private final float mHighLightAmbientColorTemperature; + // As above, but used when in strong mode (idle screen brightness mode). + private final float mHighLightAmbientColorTemperatureStrong; private final boolean mLightModeAllowed; @@ -97,9 +102,11 @@ public class DisplayWhiteBalanceController implements // ambient color temperature to the defaults. A piecewise linear relationship // between low light brightness and low light bias. private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline; + private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSplineStrong; // A piecewise linear relationship between high light brightness and high light bias. private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline; + private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSplineStrong; private float mLatestAmbientColorTemperature; private float mLatestAmbientBrightness; @@ -134,17 +141,29 @@ public class DisplayWhiteBalanceController implements * @param lowLightAmbientBrightnesses * The ambient brightness used to map the ambient brightnesses to the biases used to * interpolate to lowLightAmbientColorTemperature. + * @param lowLightAmbientBrightnessesStrong + * The ambient brightness used to map the ambient brightnesses to the biases used to + * interpolate to lowLightAmbientColorTemperature. * @param lowLightAmbientBiases * The biases used to map the ambient brightnesses to the biases used to interpolate to * lowLightAmbientColorTemperature. + * @param lowLightAmbientBiasesStrong + * The biases used to map the ambient brightnesses to the biases used to interpolate to + * lowLightAmbientColorTemperature. * @param lowLightAmbientColorTemperature * The ambient color temperature to which we interpolate to based on the low light curve. * @param highLightAmbientBrightnesses * The ambient brightness used to map the ambient brightnesses to the biases used to * interpolate to highLightAmbientColorTemperature. + * @param highLightAmbientBrightnessesStrong + * The ambient brightness used to map the ambient brightnesses to the biases used to + * interpolate to highLightAmbientColorTemperature. * @param highLightAmbientBiases * The biases used to map the ambient brightnesses to the biases used to interpolate to * highLightAmbientColorTemperature. + * @param highLightAmbientBiasesStrong + * The biases used to map the ambient brightnesses to the biases used to interpolate to + * highLightAmbientColorTemperature. * @param highLightAmbientColorTemperature * The ambient color temperature to which we interpolate to based on the high light curve. * @param ambientColorTemperatures @@ -170,11 +189,17 @@ public class DisplayWhiteBalanceController implements @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, float[] lowLightAmbientBrightnesses, + float[] lowLightAmbientBrightnessesStrong, float[] lowLightAmbientBiases, + float[] lowLightAmbientBiasesStrong, float lowLightAmbientColorTemperature, + float lowLightAmbientColorTemperatureStrong, float[] highLightAmbientBrightnesses, + float[] highLightAmbientBrightnessesStrong, float[] highLightAmbientBiases, + float[] highLightAmbientBiasesStrong, float highLightAmbientColorTemperature, + float highLightAmbientColorTemperatureStrong, float[] ambientColorTemperatures, float[] displayColorTemperatures, float[] strongAmbientColorTemperatures, @@ -188,7 +213,9 @@ public class DisplayWhiteBalanceController implements mColorTemperatureFilter = colorTemperatureFilter; mThrottler = throttler; mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature; + mLowLightAmbientColorTemperatureStrong = lowLightAmbientColorTemperatureStrong; mHighLightAmbientColorTemperature = highLightAmbientColorTemperature; + mHighLightAmbientColorTemperatureStrong = highLightAmbientColorTemperatureStrong; mAmbientColorTemperature = -1.0f; mPendingAmbientColorTemperature = -1.0f; mLastAmbientColorTemperature = -1.0f; @@ -214,6 +241,23 @@ public class DisplayWhiteBalanceController implements } try { + mLowLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline( + lowLightAmbientBrightnessesStrong, lowLightAmbientBiasesStrong); + } catch (Exception e) { + Slog.e(TAG, "failed to create strong low light ambient brightness to bias spline.", e); + mLowLightAmbientBrightnessToBiasSplineStrong = null; + } + if (mLowLightAmbientBrightnessToBiasSplineStrong != null) { + if (mLowLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f + || mLowLightAmbientBrightnessToBiasSplineStrong.interpolate( + Float.POSITIVE_INFINITY) != 1.0f) { + Slog.d(TAG, "invalid strong low light ambient brightness to bias spline, " + + "bias must begin at 0.0 and end at 1.0."); + mLowLightAmbientBrightnessToBiasSplineStrong = null; + } + } + + try { mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline( highLightAmbientBrightnesses, highLightAmbientBiases); } catch (Exception e) { @@ -230,6 +274,23 @@ public class DisplayWhiteBalanceController implements } } + try { + mHighLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline( + highLightAmbientBrightnessesStrong, highLightAmbientBiasesStrong); + } catch (Exception e) { + Slog.e(TAG, "failed to create strong high light ambient brightness to bias spline.", e); + mHighLightAmbientBrightnessToBiasSplineStrong = null; + } + if (mHighLightAmbientBrightnessToBiasSplineStrong != null) { + if (mHighLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f + || mHighLightAmbientBrightnessToBiasSplineStrong.interpolate( + Float.POSITIVE_INFINITY) != 1.0f) { + Slog.d(TAG, "invalid strong high light ambient brightness to bias spline, " + + "bias must begin at 0.0 and end at 1.0."); + mHighLightAmbientBrightnessToBiasSplineStrong = null; + } + } + if (mLowLightAmbientBrightnessToBiasSpline != null && mHighLightAmbientBrightnessToBiasSpline != null) { if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] > @@ -241,6 +302,18 @@ public class DisplayWhiteBalanceController implements } } + if (mLowLightAmbientBrightnessToBiasSplineStrong != null + && mHighLightAmbientBrightnessToBiasSplineStrong != null) { + if (lowLightAmbientBrightnessesStrong[lowLightAmbientBrightnessesStrong.length - 1] + > highLightAmbientBrightnessesStrong[0]) { + Slog.d(TAG, + "invalid strong low light and high light ambient brightness to bias " + + "spline combination, defined domains must not intersect."); + mLowLightAmbientBrightnessToBiasSplineStrong = null; + mHighLightAmbientBrightnessToBiasSplineStrong = null; + } + } + try { mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline( ambientColorTemperatures, displayColorTemperatures); @@ -365,7 +438,11 @@ public class DisplayWhiteBalanceController implements mColorTemperatureFilter.dump(writer); mThrottler.dump(writer); writer.println(" mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature); + writer.println(" mLowLightAmbientColorTemperatureStrong=" + + mLowLightAmbientColorTemperatureStrong); writer.println(" mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature); + writer.println(" mHighLightAmbientColorTemperatureStrong=" + + mHighLightAmbientColorTemperatureStrong); writer.println(" mAmbientColorTemperature=" + mAmbientColorTemperature); writer.println(" mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature); writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature); @@ -377,8 +454,12 @@ public class DisplayWhiteBalanceController implements + mStrongAmbientToDisplayColorTemperatureSpline); writer.println(" mLowLightAmbientBrightnessToBiasSpline=" + mLowLightAmbientBrightnessToBiasSpline); + writer.println(" mLowLightAmbientBrightnessToBiasSplineStrong=" + + mLowLightAmbientBrightnessToBiasSplineStrong); writer.println(" mHighLightAmbientBrightnessToBiasSpline=" + mHighLightAmbientBrightnessToBiasSpline); + writer.println(" mHighLightAmbientBrightnessToBiasSplineStrong=" + + mHighLightAmbientBrightnessToBiasSplineStrong); } @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks @@ -400,6 +481,17 @@ public class DisplayWhiteBalanceController implements */ public void updateAmbientColorTemperature() { final long time = System.currentTimeMillis(); + final float lowLightAmbientColorTemperature = mStrongModeEnabled + ? mLowLightAmbientColorTemperatureStrong : mLowLightAmbientColorTemperature; + final float highLightAmbientColorTemperature = mStrongModeEnabled + ? mHighLightAmbientColorTemperatureStrong : mHighLightAmbientColorTemperature; + final Spline.LinearSpline lowLightAmbientBrightnessToBiasSpline = mStrongModeEnabled + ? mLowLightAmbientBrightnessToBiasSplineStrong + : mLowLightAmbientBrightnessToBiasSpline; + final Spline.LinearSpline highLightAmbientBrightnessToBiasSpline = mStrongModeEnabled + ? mHighLightAmbientBrightnessToBiasSplineStrong + : mHighLightAmbientBrightnessToBiasSpline; + float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time); mLatestAmbientColorTemperature = ambientColorTemperature; @@ -423,19 +515,19 @@ public class DisplayWhiteBalanceController implements mLatestAmbientBrightness = ambientBrightness; if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f - && mLowLightAmbientBrightnessToBiasSpline != null) { - float bias = mLowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness); + && lowLightAmbientBrightnessToBiasSpline != null) { + float bias = lowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness); ambientColorTemperature = bias * ambientColorTemperature + (1.0f - bias) - * mLowLightAmbientColorTemperature; + * lowLightAmbientColorTemperature; mLatestLowLightBias = bias; } if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f - && mHighLightAmbientBrightnessToBiasSpline != null) { - float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness); + && highLightAmbientBrightnessToBiasSpline != null) { + float bias = highLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness); ambientColorTemperature = (1.0f - bias) * ambientColorTemperature + bias - * mHighLightAmbientColorTemperature; + * highLightAmbientColorTemperature; mLatestHighLightBias = bias; } diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index 62f813f2857a..39e6b3f288fb 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -70,21 +70,39 @@ public class DisplayWhiteBalanceFactory { final float[] displayWhiteBalanceLowLightAmbientBrightnesses = getFloatArray(resources, com.android.internal.R.array .config_displayWhiteBalanceLowLightAmbientBrightnesses); + final float[] displayWhiteBalanceLowLightAmbientBrightnessesStrong = getFloatArray( + resources, com.android.internal.R.array + .config_displayWhiteBalanceLowLightAmbientBrightnessesStrong); final float[] displayWhiteBalanceLowLightAmbientBiases = getFloatArray(resources, com.android.internal.R.array .config_displayWhiteBalanceLowLightAmbientBiases); + final float[] displayWhiteBalanceLowLightAmbientBiasesStrong = getFloatArray(resources, + com.android.internal.R.array + .config_displayWhiteBalanceLowLightAmbientBiasesStrong); final float lowLightAmbientColorTemperature = getFloat(resources, com.android.internal.R.dimen .config_displayWhiteBalanceLowLightAmbientColorTemperature); + final float lowLightAmbientColorTemperatureStrong = getFloat(resources, + com.android.internal.R.dimen + .config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong); final float[] displayWhiteBalanceHighLightAmbientBrightnesses = getFloatArray(resources, com.android.internal.R.array .config_displayWhiteBalanceHighLightAmbientBrightnesses); + final float[] displayWhiteBalanceHighLightAmbientBrightnessesStrong = getFloatArray( + resources, com.android.internal.R.array + .config_displayWhiteBalanceHighLightAmbientBrightnessesStrong); final float[] displayWhiteBalanceHighLightAmbientBiases = getFloatArray(resources, com.android.internal.R.array .config_displayWhiteBalanceHighLightAmbientBiases); + final float[] displayWhiteBalanceHighLightAmbientBiasesStrong = getFloatArray(resources, + com.android.internal.R.array + .config_displayWhiteBalanceHighLightAmbientBiasesStrong); final float highLightAmbientColorTemperature = getFloat(resources, com.android.internal.R.dimen .config_displayWhiteBalanceHighLightAmbientColorTemperature); + final float highLightAmbientColorTemperatureStrong = getFloat(resources, + com.android.internal.R.dimen + .config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong); final float[] ambientColorTemperatures = getFloatArray(resources, com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures); final float[] displayColorTemperatures = getFloatArray(resources, @@ -100,9 +118,15 @@ public class DisplayWhiteBalanceFactory { final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController( brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler, displayWhiteBalanceLowLightAmbientBrightnesses, - displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature, + displayWhiteBalanceLowLightAmbientBrightnessesStrong, + displayWhiteBalanceLowLightAmbientBiases, + displayWhiteBalanceLowLightAmbientBiasesStrong, lowLightAmbientColorTemperature, + lowLightAmbientColorTemperatureStrong, displayWhiteBalanceHighLightAmbientBrightnesses, - displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature, + displayWhiteBalanceHighLightAmbientBrightnessesStrong, + displayWhiteBalanceHighLightAmbientBiases, + displayWhiteBalanceHighLightAmbientBiasesStrong, highLightAmbientColorTemperature, + highLightAmbientColorTemperatureStrong, ambientColorTemperatures, displayColorTemperatures, strongAmbientColorTemperatures, strongDisplayColorTemperatures, lightModeAllowed); brightnessSensor.setCallbacks(controller); diff --git a/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java b/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java index d764ec41b3b9..9172dc02a4ac 100644 --- a/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java +++ b/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java @@ -32,6 +32,10 @@ final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction { private int mInitialAudioStatusRetriesLeft = 2; + // Flag to notify AudioService of the next audio status reported, + // regardless of whether the audio status changed. + private boolean mForceNextAudioStatusUpdate = false; + private static final int STATE_WAIT_FOR_INITIAL_AUDIO_STATUS = 1; private static final int STATE_MONITOR_AUDIO_STATUS = 2; @@ -70,6 +74,17 @@ final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction { return false; } + + /** + * If AVB has been enabled, send <Give Audio Status> and notify AudioService of the response. + */ + void requestAndUpdateAudioStatus() { + if (mState == STATE_MONITOR_AUDIO_STATUS) { + mForceNextAudioStatusUpdate = true; + sendGiveAudioStatus(); + } + } + private boolean handleReportAudioStatus(HdmiCecMessage cmd) { if (mTargetAddress != cmd.getSource() || cmd.getParams().length == 0) { return false; @@ -89,12 +104,15 @@ final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction { localDevice().getService().enableAbsoluteVolumeBehavior(audioStatus); mState = STATE_MONITOR_AUDIO_STATUS; } else if (mState == STATE_MONITOR_AUDIO_STATUS) { - if (audioStatus.getVolume() != mLastAudioStatus.getVolume()) { + if (mForceNextAudioStatusUpdate + || audioStatus.getVolume() != mLastAudioStatus.getVolume()) { localDevice().getService().notifyAvbVolumeChange(audioStatus.getVolume()); } - if (audioStatus.getMute() != mLastAudioStatus.getMute()) { + if (mForceNextAudioStatusUpdate + || audioStatus.getMute() != mLastAudioStatus.getMute()) { localDevice().getService().notifyAvbMuteChange(audioStatus.getMute()); } + mForceNextAudioStatusUpdate = false; } mLastAudioStatus = audioStatus; diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 77213bf256a6..b78f8a7d8ee7 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -357,7 +357,7 @@ final class Constants { static final int INVALID_PORT_ID = HdmiDeviceInfo.PORT_INVALID; static final int INVALID_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INVALID; - static final int PATH_INTERNAL = HdmiDeviceInfo.PATH_INTERNAL; + static final int TV_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INTERNAL; // The relationship from one path (physical address) to another. @IntDef({ diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 207d38eb17ac..0671464a1ed4 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -1048,6 +1048,19 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice { } /** + * If AVB has been enabled, request the System Audio device's audio status and notify + * AudioService of its response. + */ + @ServiceThreadOnly + void requestAndUpdateAvbAudioStatus() { + assertRunOnServiceThread(); + for (AbsoluteVolumeAudioStatusAction action : + getActions(AbsoluteVolumeAudioStatusAction.class)) { + action.requestAndUpdateAudioStatus(); + } + } + + /** * Determines whether {@code targetAddress} supports <Set Audio Volume Level>. Does two things * in parallel: send <Give Features> (to get <Report Features> in response), * and send <Set Audio Volume Level> (to see if it gets a <Feature Abort> in response). diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index dc416b24f3f3..824c8dbb144d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -518,6 +518,18 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) { assertRunOnServiceThread(); + // If the device is active source and received a <Routing Change> or <Routing Information> + // message to a physical address in the same active path do not change the Active Source + // status. + // E.g. TV [0.0.0.0] ------ Switch [2.0.0.0] ------ OTT [2.1.0.0] (Active Source) + // TV sends <Routing Change>[2.0.0.0] -> OTT is still Active Source + // TV sends <Routing Change>[0.0.0.0] -> OTT is not Active Source anymore. + // TV sends <Routing Change>[3.0.0.0] -> OTT is not Active Source anymore. + if (HdmiUtils.isInActiveRoutingPath(mService.getPhysicalAddress(), physicalAddress) + && physicalAddress != Constants.TV_PHYSICAL_ADDRESS + && isActiveSource()) { + return; + } if (physicalAddress != mService.getPhysicalAddress()) { setActiveSource(physicalAddress, "HdmiCecLocalDevicePlayback#handleRoutingChangeAndInformation()"); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 5abb2b5ae861..99fa3a3271aa 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -4661,6 +4661,13 @@ public class HdmiControlService extends SystemService { // same keycode for all three mute options. keyCode = KeyEvent.KEYCODE_VOLUME_MUTE; break; + case AudioManager.ADJUST_SAME: + // Query the current audio status of the Audio System and display UI for it + // Only for TVs, because Playback devices don't display UI when using AVB + if (tv() != null) { + tv().requestAndUpdateAvbAudioStatus(); + } + return; default: return; } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java index e7a3db75c9d0..e96963b9ae3f 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -113,7 +113,7 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction { } int param = tv().getActivePath(); return param != Constants.INVALID_PHYSICAL_ADDRESS - ? param : Constants.PATH_INTERNAL; + ? param : Constants.TV_PHYSICAL_ADDRESS; } private void handleSendSystemAudioModeRequestTimeout() { diff --git a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java b/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java new file mode 100644 index 000000000000..67c221f77037 --- /dev/null +++ b/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import android.view.Display; +import android.view.InputEvent; +import android.view.InputEventReceiver; +import android.view.MotionEvent; + +import com.android.server.UiThread; + +/** + * Receives input events before they are dispatched and reports them to FocusEventDebugView. + */ +class FocusEventDebugGlobalMonitor extends InputEventReceiver { + private final FocusEventDebugView mDebugView; + + FocusEventDebugGlobalMonitor(FocusEventDebugView debugView, InputManagerService service) { + super(service.monitorInput("FocusEventDebugGlobalMonitor", Display.DEFAULT_DISPLAY), + UiThread.getHandler().getLooper()); + mDebugView = debugView; + } + + @Override + public void onInputEvent(InputEvent event) { + try { + if (event instanceof MotionEvent) { + mDebugView.reportMotionEvent((MotionEvent) event); + } + } finally { + finishInputEvent(event, false); + } + } +} diff --git a/services/core/java/com/android/server/input/FocusEventDebugView.java b/services/core/java/com/android/server/input/FocusEventDebugView.java index fba2aa60b952..4b8fabde7d35 100644 --- a/services/core/java/com/android/server/input/FocusEventDebugView.java +++ b/services/core/java/com/android/server/input/FocusEventDebugView.java @@ -22,35 +22,47 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.animation.LayoutTransition; import android.annotation.AnyThread; +import android.annotation.Nullable; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; import android.graphics.Typeface; +import android.util.DisplayMetrics; import android.util.Pair; import android.util.Slog; import android.util.TypedValue; import android.view.Gravity; -import android.view.InputEvent; +import android.view.InputDevice; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.RoundedCorner; import android.view.View; +import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.animation.AccelerateInterpolator; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; /** * Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on * the screen. */ -class FocusEventDebugView extends LinearLayout { +class FocusEventDebugView extends RelativeLayout { private static final String TAG = FocusEventDebugView.class.getSimpleName(); @@ -63,43 +75,45 @@ class FocusEventDebugView extends LinearLayout { private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8; private static final int KEY_VIEW_MIN_WIDTH_DP = 32; private static final int KEY_VIEW_TEXT_SIZE_SP = 12; + private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5; + private final InputManagerService mService; private final int mOuterPadding; + private final DisplayMetrics mDm; // Tracks all keys that are currently pressed/down. private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView> mPressedKeys = new HashMap<>(); - private final PressedKeyContainer mPressedKeyContainer; - private final PressedKeyContainer mPressedModifierContainer; - - FocusEventDebugView(Context c) { + @Nullable + private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor; + @Nullable + private PressedKeyContainer mPressedKeyContainer; + @Nullable + private PressedKeyContainer mPressedModifierContainer; + private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory; + @Nullable + private RotaryInputValueView mRotaryInputValueView; + private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory; + @Nullable + private RotaryInputGraphView mRotaryInputGraphView; + + @VisibleForTesting + FocusEventDebugView(Context c, InputManagerService service, + Supplier<RotaryInputValueView> rotaryInputValueViewFactory, + Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) { super(c); setFocusableInTouchMode(true); - final var dm = mContext.getResources().getDisplayMetrics(); - mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, dm); - - setOrientation(HORIZONTAL); - setLayoutDirection(LAYOUT_DIRECTION_RTL); - setGravity(Gravity.START | Gravity.BOTTOM); - - mPressedKeyContainer = new PressedKeyContainer(mContext); - mPressedKeyContainer.setOrientation(HORIZONTAL); - mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM); - mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR); - final var scroller = new HorizontalScrollView(mContext); - scroller.addView(mPressedKeyContainer); - scroller.setHorizontalScrollBarEnabled(false); - scroller.addOnLayoutChangeListener( - (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT)); - scroller.setHorizontalFadingEdgeEnabled(true); - addView(scroller, new LayoutParams(0, WRAP_CONTENT, 1)); + mService = service; + mRotaryInputValueViewFactory = rotaryInputValueViewFactory; + mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory; + mDm = mContext.getResources().getDisplayMetrics(); + mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm); + } - mPressedModifierContainer = new PressedKeyContainer(mContext); - mPressedModifierContainer.setOrientation(VERTICAL); - mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM); - addView(mPressedModifierContainer, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + FocusEventDebugView(Context c, InputManagerService service) { + this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c)); } @Override @@ -108,13 +122,13 @@ class FocusEventDebugView extends LinearLayout { final RoundedCorner bottomLeft = insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); - if (bottomLeft != null) { + if (bottomLeft != null && !insets.isRound()) { paddingBottom = bottomLeft.getRadius(); } final RoundedCorner bottomRight = insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); - if (bottomRight != null) { + if (bottomRight != null && !insets.isRound()) { paddingBottom = Math.max(paddingBottom, bottomRight.getRadius()); } @@ -135,17 +149,106 @@ class FocusEventDebugView extends LinearLayout { return super.dispatchKeyEvent(event); } - /** Report an input event to the debug view. */ @AnyThread - public void reportEvent(InputEvent event) { - if (!(event instanceof KeyEvent)) { - // TODO: Support non-pointer MotionEvents. + public void updateShowKeyPresses(boolean enabled) { + post(() -> handleUpdateShowKeyPresses(enabled)); + } + + @AnyThread + public void updateShowRotaryInput(boolean enabled) { + post(() -> handleUpdateShowRotaryInput(enabled)); + } + + private void handleUpdateShowKeyPresses(boolean enabled) { + if (enabled == showKeyPresses()) { + return; + } + + if (!enabled) { + removeView(mPressedKeyContainer); + mPressedKeyContainer = null; + removeView(mPressedModifierContainer); + mPressedModifierContainer = null; + return; + } + + mPressedKeyContainer = new PressedKeyContainer(mContext); + mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL); + mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM); + mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR); + final var scroller = new HorizontalScrollView(mContext); + scroller.addView(mPressedKeyContainer); + scroller.setHorizontalScrollBarEnabled(false); + scroller.addOnLayoutChangeListener( + (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT)); + scroller.setHorizontalFadingEdgeEnabled(true); + LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT); + scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM); + scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT); + addView(scroller, scrollerLayoutParams); + + mPressedModifierContainer = new PressedKeyContainer(mContext); + mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL); + mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM); + LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT); + modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM); + modifierLayoutParams.addRule(ALIGN_PARENT_LEFT); + modifierLayoutParams.addRule(LEFT_OF, scroller.getId()); + addView(mPressedModifierContainer, modifierLayoutParams); + } + + @VisibleForTesting + void handleUpdateShowRotaryInput(boolean enabled) { + if (enabled == showRotaryInput()) { return; } + + if (!enabled) { + mFocusEventDebugGlobalMonitor.dispose(); + mFocusEventDebugGlobalMonitor = null; + removeView(mRotaryInputValueView); + mRotaryInputValueView = null; + removeView(mRotaryInputGraphView); + mRotaryInputGraphView = null; + return; + } + + mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService); + + mRotaryInputValueView = mRotaryInputValueViewFactory.get(); + LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT); + valueLayoutParams.addRule(CENTER_HORIZONTAL); + valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM); + addView(mRotaryInputValueView, valueLayoutParams); + + mRotaryInputGraphView = mRotaryInputGraphViewFactory.get(); + LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, + (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels)); + graphLayoutParams.addRule(CENTER_IN_PARENT); + addView(mRotaryInputGraphView, graphLayoutParams); + } + + /** Report a key event to the debug view. */ + @AnyThread + public void reportKeyEvent(KeyEvent event) { post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event))); } + /** Report a motion event to the debug view. */ + @AnyThread + public void reportMotionEvent(MotionEvent event) { + if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) { + return; + } + + post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event))); + } + private void handleKeyEvent(KeyEvent keyEvent) { + if (!showKeyPresses()) { + return; + } + final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode()); final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode()) ? mPressedModifierContainer @@ -185,6 +288,19 @@ class FocusEventDebugView extends LinearLayout { keyEvent.recycle(); } + @VisibleForTesting + void handleRotaryInput(MotionEvent motionEvent) { + if (!showRotaryInput()) { + return; + } + + float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL); + mRotaryInputValueView.updateValue(scrollAxisValue); + mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime()); + + motionEvent.recycle(); + } + private static String getLabel(KeyEvent event) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_SPACE: @@ -232,6 +348,23 @@ class FocusEventDebugView extends LinearLayout { return label; } + /** Determine whether to show key presses by checking one of the key-related objects. */ + private boolean showKeyPresses() { + return mPressedKeyContainer != null; + } + + /** Determine whether to show rotary input by checking one of the rotary-related objects. */ + private boolean showRotaryInput() { + return mRotaryInputValueView != null; + } + + /** + * Converts a dimension in scaled pixel units to integer display pixels. + */ + private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) { + return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm); + } + private static class PressedKeyView extends TextView { private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{ @@ -340,4 +473,376 @@ class FocusEventDebugView extends LinearLayout { invalidate(); } } + + // TODO(b/286086154): move RotaryInputGraphView and RotaryInputValueView to a subpackage. + + /** Draws the most recent rotary input value and indicates whether the source is active. */ + @VisibleForTesting + static class RotaryInputValueView extends TextView { + + private static final int INACTIVE_TEXT_COLOR = 0xffff00ff; + private static final int ACTIVE_TEXT_COLOR = 0xff420f28; + private static final int TEXT_SIZE_SP = 8; + private static final int SIDE_PADDING_SP = 4; + /** Determines how long the active status lasts. */ + private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */; + private static final ColorFilter ACTIVE_BACKGROUND_FILTER = + new ColorMatrixColorFilter(new float[]{ + 0, 0, 0, 0, 255, // red + 0, 0, 0, 0, 0, // green + 0, 0, 0, 0, 255, // blue + 0, 0, 0, 0, 200 // alpha + }); + + private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false); + private final float mScaledVerticalScrollFactor; + + @VisibleForTesting + RotaryInputValueView(Context c) { + super(c); + + DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); + mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); + + setText(getFormattedValue(0)); + setTextColor(INACTIVE_TEXT_COLOR); + setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm)); + setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0, + applyDimensionSp(SIDE_PADDING_SP, dm), 0); + setTypeface(null, Typeface.BOLD); + setBackgroundResource(R.drawable.focus_event_rotary_input_background); + } + + void updateValue(float value) { + removeCallbacks(mUpdateActivityStatusCallback); + + setText(getFormattedValue(value * mScaledVerticalScrollFactor)); + + updateActivityStatus(true); + postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION); + } + + @VisibleForTesting + void updateActivityStatus(boolean active) { + if (active) { + setTextColor(ACTIVE_TEXT_COLOR); + getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER); + } else { + setTextColor(INACTIVE_TEXT_COLOR); + getBackground().clearColorFilter(); + } + } + + private static String getFormattedValue(float value) { + return String.format("%s%.1f", value < 0 ? "-" : "+", Math.abs(value)); + } + } + + /** + * Shows a graph with the rotary input values as a function of time. + * The graph gets reset if no action is received for a certain amount of time. + */ + @VisibleForTesting + static class RotaryInputGraphView extends View { + + private static final int FRAME_COLOR = 0xbf741b47; + private static final int FRAME_WIDTH_SP = 2; + private static final int FRAME_BORDER_GAP_SP = 10; + private static final int FRAME_TEXT_SIZE_SP = 10; + private static final int FRAME_TEXT_OFFSET_SP = 2; + private static final int GRAPH_COLOR = 0xffff00ff; + private static final int GRAPH_LINE_WIDTH_SP = 1; + private static final int GRAPH_POINT_RADIUS_SP = 4; + private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5); + private static final float DEFAULT_FRAME_CENTER_POSITION = 0; + private static final int MAX_GRAPH_VALUES_SIZE = 400; + /** Maximum time between values so that they are considered part of the same gesture. */ + private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1); + + private final DisplayMetrics mDm; + /** + * Distance in position units (amount scrolled in display pixels) from the center to the + * top/bottom frame lines. + */ + private final float mFrameCenterToBorderDistance; + private final float mScaledVerticalScrollFactor; + private final Locale mDefaultLocale; + private final Paint mFramePaint = new Paint(); + private final Paint mFrameTextPaint = new Paint(); + private final Paint mGraphLinePaint = new Paint(); + private final Paint mGraphPointPaint = new Paint(); + + private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE); + /** Position at which graph values are placed at the center of the graph. */ + private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; + + @VisibleForTesting + RotaryInputGraphView(Context c) { + super(c); + + mDm = mContext.getResources().getDisplayMetrics(); + // This makes the center-to-border distance equivalent to the display height, meaning + // that the total height of the graph is equivalent to 2x the display height. + mFrameCenterToBorderDistance = mDm.heightPixels; + mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); + mDefaultLocale = Locale.getDefault(); + + mFramePaint.setColor(FRAME_COLOR); + mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm)); + + mFrameTextPaint.setColor(GRAPH_COLOR); + mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm)); + + mGraphLinePaint.setColor(GRAPH_COLOR); + mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm)); + mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND); + mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND); + + mGraphPointPaint.setColor(GRAPH_COLOR); + mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm)); + mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND); + mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND); + } + + /** + * Reads new scroll axis value and updates the list accordingly. Old positions are + * kept at the front (what you would get with getFirst), while the recent positions are + * kept at the back (what you would get with getLast). Also updates the frame center + * position to handle out-of-bounds cases. + */ + void addValue(float scrollAxisValue, long eventTime) { + // Remove values that are too old. + while (mGraphValues.getSize() > 0 + && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) { + mGraphValues.removeFirst(); + } + + // If there are no recent values, reset the frame center. + if (mGraphValues.getSize() == 0) { + mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; + } + + // Handle new value. We multiply the scroll axis value by the scaled scroll factor to + // get the amount of pixels to be scrolled. We also compute the accumulated position + // by adding the current value to the last one (if not empty). + final float displacement = scrollAxisValue * mScaledVerticalScrollFactor; + final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos); + final float pos = prevPos + displacement; + + mGraphValues.add(pos, eventTime); + + // The difference between the distance of the most recent position from the center + // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center + // frame (mFrameCenterToBorderDistance). + final float verticalDiff = Math.abs(pos - mFrameCenterPosition) + - mFrameCenterToBorderDistance; + // If needed, translate frame. + if (verticalDiff > 0) { + final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1; + // Here, we update the center frame position by the exact amount needed for us to + // stay within the maximum allowed distance from the center frame. + mFrameCenterPosition += sign * verticalDiff; + } + + // Redraw canvas. + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // Note: vertical coordinates in Canvas go from top to bottom, + // that is bottomY > middleY > topY. + final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm); + final int topY = verticalMargin; + final int bottomY = getHeight() - verticalMargin; + final int middleY = (topY + bottomY) / 2; + + // Note: horizontal coordinates in Canvas go from left to right, + // that is rightX > leftX. + final int leftX = 0; + final int rightX = getWidth(); + + // Draw the frame, which includes 3 lines that show the maximum, + // minimum and middle positions of the graph. + canvas.drawLine(leftX, topY, rightX, topY, mFramePaint); + canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint); + canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint); + + // Draw the position that each frame line corresponds to. + final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", + mFrameCenterPosition + mFrameCenterToBorderDistance), + leftX, + topY - frameTextOffset, mFrameTextPaint + ); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", mFrameCenterPosition), + leftX, + middleY - frameTextOffset, mFrameTextPaint + ); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", + mFrameCenterPosition - mFrameCenterToBorderDistance), + leftX, + bottomY - frameTextOffset, mFrameTextPaint + ); + + // If there are no graph values to be drawn, stop here. + if (mGraphValues.getSize() == 0) { + return; + } + + // Draw the graph using the times and positions. + // We start at the most recent value (which should be drawn at the right) and move + // to the older values (which should be drawn to the left of more recent ones). Negative + // indices are handled by circuling back to the end of the buffer. + final long mostRecentTime = mGraphValues.getLast().mTime; + float prevCoordX = 0; + float prevCoordY = 0; + float prevAge = 0; + for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) { + final GraphValue value = iter.next(); + + final int age = (int) (mostRecentTime - value.mTime); + final float pos = value.mPos; + + // We get the horizontal coordinate in time units from left to right with + // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas + // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL) + // and by multiplying it by the canvas length (rightX - leftX). Finally, we + // offset the coordinate by adding it to leftX. + final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age) + / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX); + + // We get the vertical coordinate in position units from middle to top with + // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas + // units by dividing it by half of the position-domain length + // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas + // length (middleY - topY). Finally, we offset the coordinate by subtracting + // it from middleY (we can't "add" here because the coordinate grows from top + // to bottom). + final float coordY = middleY - ((pos - mFrameCenterPosition) + / mFrameCenterToBorderDistance) * (middleY - topY); + + // Draw a point for this value. + canvas.drawPoint(coordX, coordY, mGraphPointPaint); + + // If this value is part of the same gesture as the previous one, draw a line + // between them. We ignore the first value (with age = 0). + if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) { + canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint); + } + + prevCoordX = coordX; + prevCoordY = coordY; + prevAge = age; + } + } + + @VisibleForTesting + float getFrameCenterPosition() { + return mFrameCenterPosition; + } + + /** + * Holds data needed to draw each entry in the graph. + */ + private static class GraphValue { + /** Position. */ + float mPos; + /** Time when this value was added. */ + long mTime; + + GraphValue(float pos, long time) { + this.mPos = pos; + this.mTime = time; + } + } + + /** + * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the + * old values with new ones to avoid creating new objects. + */ + private static class CyclicBuffer { + private final GraphValue[] mValues; + private final int mCapacity; + private int mSize = 0; + private int mLastIndex = 0; + + // The iteration index and counter are here to make it easier to reset them. + /** Determines the value currently pointed by the iterator. */ + private int mIteratorIndex; + /** Counts how many values have been iterated through. */ + private int mIteratorCount; + + /** Used traverse the values in reverse order. */ + private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() { + @Override + public boolean hasNext() { + return mIteratorCount <= mSize; + } + + @Override + public GraphValue next() { + // Returns the value currently pointed by the iterator and moves the iterator to + // the previous one. + mIteratorCount++; + return mValues[(mIteratorIndex-- + mCapacity) % mCapacity]; + } + }; + + CyclicBuffer(int capacity) { + mCapacity = capacity; + mValues = new GraphValue[capacity]; + } + + /** + * Add new graph value. If there is an existing object, we replace its data with the + * new one. With this, we re-use old objects instead of creating new ones. + */ + void add(float pos, long time) { + mLastIndex = (mLastIndex + 1) % mCapacity; + if (mValues[mLastIndex] == null) { + mValues[mLastIndex] = new GraphValue(pos, time); + } else { + final GraphValue oldValue = mValues[mLastIndex]; + oldValue.mPos = pos; + oldValue.mTime = time; + } + + // If needed, account for new value in the buffer size. + if (mSize != mCapacity) { + mSize++; + } + } + + int getSize() { + return mSize; + } + + GraphValue getFirst() { + final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1; + final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity; + return mValues[firstIndex]; + } + + GraphValue getLast() { + return mValues[mLastIndex]; + } + + void removeFirst() { + mSize--; + } + + /** Returns an iterator pointing at the last value. */ + Iterator<GraphValue> reverseIterator() { + mIteratorIndex = mLastIndex; + mIteratorCount = 1; + return mReverseIterator; + } + } + } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 6241ebbc60cc..b8e9d5dfb3bc 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -390,6 +390,8 @@ public class InputManagerService extends IInputManager.Stub @GuardedBy("mFocusEventDebugViewLock") @Nullable private FocusEventDebugView mFocusEventDebugView; + private boolean mShowKeyPresses = false; + private boolean mShowRotaryInput = false; /** Point of injection for test dependencies. */ @VisibleForTesting @@ -2476,7 +2478,7 @@ public class InputManagerService extends IInputManager.Stub private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { synchronized (mFocusEventDebugViewLock) { if (mFocusEventDebugView != null) { - mFocusEventDebugView.reportEvent(event); + mFocusEventDebugView.reportKeyEvent(event); } } return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags); @@ -3396,14 +3398,45 @@ public class InputManagerService extends IInputManager.Stub mWindowManagerCallbacks.notifyPointerLocationChanged(enabled); } - void updateFocusEventDebugViewEnabled(boolean enabled) { + void updateShowKeyPresses(boolean enabled) { + if (mShowKeyPresses == enabled) { + return; + } + + mShowKeyPresses = enabled; + updateFocusEventDebugViewEnabled(); + + synchronized (mFocusEventDebugViewLock) { + if (mFocusEventDebugView != null) { + mFocusEventDebugView.updateShowKeyPresses(enabled); + } + } + } + + void updateShowRotaryInput(boolean enabled) { + if (mShowRotaryInput == enabled) { + return; + } + + mShowRotaryInput = enabled; + updateFocusEventDebugViewEnabled(); + + synchronized (mFocusEventDebugViewLock) { + if (mFocusEventDebugView != null) { + mFocusEventDebugView.updateShowRotaryInput(enabled); + } + } + } + + private void updateFocusEventDebugViewEnabled() { + boolean enabled = mShowKeyPresses || mShowRotaryInput; FocusEventDebugView view; synchronized (mFocusEventDebugViewLock) { if (enabled == (mFocusEventDebugView != null)) { return; } if (enabled) { - mFocusEventDebugView = new FocusEventDebugView(mContext); + mFocusEventDebugView = new FocusEventDebugView(mContext, this); view = mFocusEventDebugView; } else { view = mFocusEventDebugView; diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index cf7c692d9a54..aab491ee1def 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -86,7 +86,9 @@ class InputSettingsObserver extends ContentObserver { Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_TIMEOUT_MS), (reason) -> updateKeyRepeatInfo(getLatestLongPressTimeoutValue())), Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_DELAY_MS), - (reason) -> updateKeyRepeatInfo(getLatestLongPressTimeoutValue()))); + (reason) -> updateKeyRepeatInfo(getLatestLongPressTimeoutValue())), + Map.entry(Settings.System.getUriFor(Settings.System.SHOW_ROTARY_INPUT), + (reason) -> updateShowRotaryInput())); } /** @@ -164,8 +166,11 @@ class InputSettingsObserver extends ContentObserver { } private void updateShowKeyPresses() { - mService.updateFocusEventDebugViewEnabled( - getBoolean(Settings.System.SHOW_KEY_PRESSES, false)); + mService.updateShowKeyPresses(getBoolean(Settings.System.SHOW_KEY_PRESSES, false)); + } + + private void updateShowRotaryInput() { + mService.updateShowRotaryInput(getBoolean(Settings.System.SHOW_ROTARY_INPUT, false)); } private void updateAccessibilityLargePointer() { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 2fc48294ae70..8e7baf26984a 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5959,6 +5959,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mVisibilityStateComputer.dump(pw); p.println(" mInFullscreenMode=" + mInFullscreenMode); p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive); + p.println(" ENABLE_HIDE_IME_CAPTION_BAR=" + + InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR); p.println(" mSettingsObserver=" + mSettingsObserver); p.println(" mStylusIds=" + (mStylusIds != null ? Arrays.toString(mStylusIds.toArray()) : "")); diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index e97a12a83b1f..6f9b7d6328c7 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -295,6 +295,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private final PowerManager.WakeLock mDownloadPsdsWakeLock; @GuardedBy("mLock") private final Set<Integer> mPendingDownloadPsdsTypes = new HashSet<>(); + @GuardedBy("mLock") + private final Set<Integer> mDownloadInProgressPsdsTypes = new HashSet<>(); /** * Properties loaded from PROPERTIES_FILE. @@ -767,8 +769,16 @@ public class GnssLocationProvider extends AbstractLocationProvider implements return; } synchronized (mLock) { + if (mDownloadInProgressPsdsTypes.contains(psdsType)) { + if (DEBUG) { + Log.d(TAG, + "PSDS type " + psdsType + " download in progress. Ignore the request."); + } + return; + } // hold wake lock while task runs mDownloadPsdsWakeLock.acquire(DOWNLOAD_PSDS_DATA_TIMEOUT_MS); + mDownloadInProgressPsdsTypes.add(psdsType); } Log.i(TAG, "WakeLock acquired by handleDownloadPsdsData()"); Executors.newSingleThreadExecutor().execute(() -> { @@ -818,6 +828,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements Log.e(TAG, "WakeLock expired before release in " + "handleDownloadPsdsData()"); } + mDownloadInProgressPsdsTypes.remove(psdsType); } }); } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 63dc59c125a3..37bcfbb03899 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -1291,13 +1291,18 @@ class MediaRouter2ServiceImpl { } long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); - if (managerRecord.mLastSessionCreationRequest != null) { + SessionCreationRequest lastRequest = managerRecord.mLastSessionCreationRequest; + if (lastRequest != null) { + Slog.i( + TAG, + TextUtils.formatSimple( + "requestCreateSessionWithManagerLocked: Notifying failure for pending" + + " session creation request - oldSession: %s, route: %s", + lastRequest.mOldSession, lastRequest.mRoute)); managerRecord.mUserRecord.mHandler.notifyRequestFailedToManager( managerRecord.mManager, - toOriginalRequestId(managerRecord.mLastSessionCreationRequest - .mManagerRequestId), + toOriginalRequestId(lastRequest.mManagerRequestId), REASON_UNKNOWN_ERROR); - managerRecord.mLastSessionCreationRequest = null; } managerRecord.mLastSessionCreationRequest = new SessionCreationRequest(routerRecord, MediaRoute2ProviderService.REQUEST_ID_NONE, uniqueRequestId, diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 3a20cd962667..f2242bf48dcd 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -2188,11 +2188,10 @@ public class MediaSessionService extends SystemService implements Monitor { MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); - boolean preferSuggestedStream = false; - if (isValidLocalStreamType(suggestedStream) - && AudioSystem.isStreamActive(suggestedStream, 0)) { - preferSuggestedStream = true; - } + boolean preferSuggestedStream = + isValidLocalStreamType(suggestedStream) + && AudioSystem.isStreamActive(suggestedStream, 0); + if (session == null || preferSuggestedStream) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Adjusting suggestedStream=" + suggestedStream + " by " + direction diff --git a/services/core/java/com/android/server/net/watchlist/OWNERS b/services/core/java/com/android/server/net/watchlist/OWNERS index a3d4b85367cf..d0c4e553ad8c 100644 --- a/services/core/java/com/android/server/net/watchlist/OWNERS +++ b/services/core/java/com/android/server/net/watchlist/OWNERS @@ -1,3 +1,2 @@ -rickywai@google.com alanstokes@google.com simonjw@google.com diff --git a/services/core/java/com/android/server/notification/Android.bp b/services/core/java/com/android/server/notification/Android.bp new file mode 100644 index 000000000000..f26a25b63207 --- /dev/null +++ b/services/core/java/com/android/server/notification/Android.bp @@ -0,0 +1,12 @@ +java_aconfig_library { + name: "notification_flags_lib", + aconfig_declarations: "notification_flags", +} + +aconfig_declarations { + name: "notification_flags", + package: "com.android.server.notification", + srcs: [ + "flags.aconfig", + ], +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e782ea937c50..009cc3b57594 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1239,7 +1239,9 @@ public class NotificationManagerService extends SystemService { mNotificationRecordLogger.log( NotificationRecordLogger.NotificationEvent.fromAction(actionIndex, generatedByAssistant, action.isContextual()), r); - EventLogTags.writeNotificationActionClicked(key, actionIndex, + EventLogTags.writeNotificationActionClicked(key, + action.actionIntent.getTarget().toString(), + action.actionIntent.getIntent().toString(), actionIndex, r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), nv.rank, nv.count); nv.recycle(); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index b2d3fca16112..100c63863f7f 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -25,8 +25,6 @@ import static android.service.notification.NotificationListenerService.Ranking.U import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE; import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.IActivityManager; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; @@ -48,6 +46,7 @@ import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.PowerManager; +import android.os.Trace; import android.os.UserHandle; import android.os.VibrationEffect; import android.provider.Settings; @@ -98,8 +97,7 @@ public final class NotificationRecord { // the period after which a notification is updated where it can make sound private static final int MAX_SOUND_DELAY_MS = 2000; private final StatusBarNotification sbn; - IActivityManager mAm; - UriGrantsManagerInternal mUgmInternal; + private final UriGrantsManagerInternal mUgmInternal; final int mTargetSdkVersion; final int mOriginalFlags; private final Context mContext; @@ -223,7 +221,6 @@ public final class NotificationRecord { this.sbn = sbn; mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class) .getPackageTargetSdkVersion(sbn.getPackageName()); - mAm = ActivityManager.getService(); mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mOriginalFlags = sbn.getNotification().flags; mRankingTimeMs = calculateRankingTimeMs(0L); @@ -1387,18 +1384,27 @@ public final class NotificationRecord { * Collect all {@link Uri} that should have permission granted to whoever * will be rendering it. */ - protected void calculateGrantableUris() { - final Notification notification = getNotification(); - notification.visitUris((uri) -> { - visitGrantableUri(uri, false, false); - }); - - if (notification.getChannelId() != null) { - NotificationChannel channel = getChannel(); - if (channel != null) { - visitGrantableUri(channel.getSound(), (channel.getUserLockedFields() - & NotificationChannel.USER_LOCKED_SOUND) != 0, true); + private void calculateGrantableUris() { + Trace.beginSection("NotificationRecord.calculateGrantableUris"); + try { + // We can't grant URI permissions from system. + final int sourceUid = getSbn().getUid(); + if (sourceUid == android.os.Process.SYSTEM_UID) return; + + final Notification notification = getNotification(); + notification.visitUris((uri) -> { + visitGrantableUri(uri, false, false); + }); + + if (notification.getChannelId() != null) { + NotificationChannel channel = getChannel(); + if (channel != null) { + visitGrantableUri(channel.getSound(), (channel.getUserLockedFields() + & NotificationChannel.USER_LOCKED_SOUND) != 0, true); + } } + } finally { + Trace.endSection(); } } @@ -1413,13 +1419,14 @@ public final class NotificationRecord { private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) { if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return; - // We can't grant Uri permissions from system - final int sourceUid = getSbn().getUid(); - if (sourceUid == android.os.Process.SYSTEM_UID) return; + if (mGrantableUris != null && mGrantableUris.contains(uri)) { + return; // already verified this URI + } + final int sourceUid = getSbn().getUid(); final long ident = Binder.clearCallingIdentity(); try { - // This will throw SecurityException if caller can't grant + // This will throw a SecurityException if the caller can't grant. mUgmInternal.checkGrantUriPermission(sourceUid, null, ContentProvider.getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig new file mode 100644 index 000000000000..c0bc6d14cd04 --- /dev/null +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.notification" + +flag { + name: "expire_bitmaps" + namespace: "systemui" + description: "This flag controls removing expired notification bitmaps" + bug: "290381858" +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index 3ba307be2311..1134714bce55 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -307,7 +307,8 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { && mode != BugreportParams.BUGREPORT_MODE_REMOTE && mode != BugreportParams.BUGREPORT_MODE_WEAR && mode != BugreportParams.BUGREPORT_MODE_TELEPHONY - && mode != BugreportParams.BUGREPORT_MODE_WIFI) { + && mode != BugreportParams.BUGREPORT_MODE_WIFI + && mode != BugreportParams.BUGREPORT_MODE_ONBOARDING) { Slog.w(TAG, "Unknown bugreport mode: " + mode); throw new IllegalArgumentException("Unknown bugreport mode: " + mode); } diff --git a/services/core/java/com/android/server/pm/Android.bp b/services/core/java/com/android/server/pm/Android.bp new file mode 100644 index 000000000000..89c0124d2cd4 --- /dev/null +++ b/services/core/java/com/android/server/pm/Android.bp @@ -0,0 +1,12 @@ +aconfig_declarations { + name: "pm_flags", + package: "com.android.server.pm", + srcs: [ + "*.aconfig", + ], +} + +java_aconfig_library { + name: "pm_flags_lib", + aconfig_declarations: "pm_flags", +} diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 6e7560563c8d..f9876299e8e0 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -487,6 +487,8 @@ public interface Computer extends PackageDataSnapshot { boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId); + boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId); + boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId); @NonNull diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 5b05b4800017..1cfc7d76919a 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -522,6 +522,7 @@ public class ComputerEngine implements Computer { comp != null || pkgName != null /*onlyExposedExplicitly*/, isImplicitImageCaptureIntentAndNotSetByDpc(intent, userId, resolvedType, flags)); + List<ResolveInfo> list = Collections.emptyList(); boolean skipPostResolution = false; if (comp != null) { @@ -643,6 +644,12 @@ public class ComputerEngine implements Computer { final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps, false /* isImplicitImageCaptureIntentAndNotSetByDpc */); + + // Only if the query is coming from the system process, + // it should be allowed to match quarantined components + if (callingUid != Process.SYSTEM_UID) { + flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; + } Intent originalIntent = null; ComponentName comp = intent.getComponent(); if (comp == null) { @@ -4031,6 +4038,9 @@ public class ComputerEngine implements Computer { flags = updateFlagsForComponent(flags, userId); enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get provider info"); + if (callingUid != Process.SYSTEM_UID) { + flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; + } ParsedProvider p = mComponentResolver.getProvider(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getProviderInfo " + component + ": " + p); @@ -4660,6 +4670,9 @@ public class ComputerEngine implements Computer { int callingUid) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId); + if (callingUid != Process.SYSTEM_UID) { + flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; + } final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags, userId); boolean checkedGrants = false; @@ -4772,6 +4785,13 @@ public class ComputerEngine implements Computer { false /* checkShell */, "queryContentProviders"); if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForComponent(flags, userId); + + // Only if the service query is coming from the system process, + // it should be allowed to match quarantined components + if (callingUid != Process.SYSTEM_UID) { + flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; + } + ArrayList<ProviderInfo> finalList = null; final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName, metaDataKey, uid, flags, userId); @@ -4905,8 +4925,8 @@ public class ComputerEngine implements Computer { } } - @Override - public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) { + private PackageUserStateInternal getUserStageOrDefaultForUser(@NonNull String packageName, + int userId) { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "isPackageSuspendedForUser for user " + userId); @@ -4914,7 +4934,17 @@ public class ComputerEngine implements Computer { if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { throw new IllegalArgumentException("Unknown target package: " + packageName); } - return ps.getUserStateOrDefault(userId).isSuspended(); + return ps.getUserStateOrDefault(userId); + } + + @Override + public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) { + return getUserStageOrDefaultForUser(packageName, userId).isSuspended(); + } + + @Override + public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) { + return getUserStageOrDefaultForUser(packageName, userId).isQuarantined(); } @Override diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java index fd47846af9ef..76203ac7650d 100644 --- a/services/core/java/com/android/server/pm/IPackageManagerBase.java +++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java @@ -955,6 +955,13 @@ public abstract class IPackageManagerBase extends IPackageManager.Stub { @Override @Deprecated + public final boolean isPackageQuarantinedForUser(@NonNull String packageName, + @UserIdInt int userId) { + return snapshot().isPackageQuarantinedForUser(packageName, userId); + } + + @Override + @Deprecated public final boolean isSafeMode() { // allow instant applications return mService.getSafeMode(); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 31869b8ea0b5..3e18387e81b9 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -3546,6 +3546,7 @@ final class InstallPackageHelper { + " no longer exists; its data will be wiped"); mInjector.getHandler().post( () -> mRemovePackageHelper.removePackageData(ps, userIds, null, 0, false)); + expectingBetter.put(ps.getPackageName(), ps.getPath()); } else { // we still have a disabled system package, but, it still might have // been removed. check the code path still exists and check there's diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index e73eced77f2a..11660a59afe6 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -379,7 +379,12 @@ public class LauncherAppsService extends SystemService { filter.addDataScheme("package"); mContext.registerReceiverAsUser(mPackageRemovedListener, UserHandle.ALL, filter, /* broadcastPermission= */ null, mCallbackHandler); - mPackageMonitor.register(mContext, UserHandle.ALL, mCallbackHandler); + final long identity = Binder.clearCallingIdentity(); + try { + mPackageMonitor.register(mContext, UserHandle.ALL, mCallbackHandler); + } finally { + Binder.restoreCallingIdentity(identity); + } mIsWatchingPackageBroadcasts = true; } } diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java index 1a5591c98269..01c27348db94 100644 --- a/services/core/java/com/android/server/pm/MovePackageHelper.java +++ b/services/core/java/com/android/server/pm/MovePackageHelper.java @@ -49,8 +49,8 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.os.storage.StorageManager; +import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; -import android.text.TextUtils; import android.util.MathUtils; import android.util.Slog; import android.util.SparseIntArray; @@ -64,6 +64,7 @@ import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageStateUtils; import java.io.File; +import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -220,9 +221,7 @@ public final class MovePackageHelper { } try { - for (int index = 0; index < installedUserIds.length; index++) { - prepareUserDataForVolumeIfRequired(volumeUuid, installedUserIds[index], storage); - } + prepareUserStorageForMove(currentVolumeUuid, volumeUuid, installedUserIds); } catch (RuntimeException e) { freezer.close(); throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR, @@ -376,27 +375,20 @@ public final class MovePackageHelper { return true; } - private void prepareUserDataForVolumeIfRequired(String volumeUuid, int userId, - StorageManager storageManager) { - if (TextUtils.isEmpty(volumeUuid) - || doesDataDirectoryExistForUser(volumeUuid, userId)) { - return; - } + private void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid, + int[] userIds) { if (DEBUG_INSTALL) { - Slog.d(TAG, "Preparing user directories for user u" + userId + " for UUID " - + volumeUuid); + Slog.d(TAG, "Preparing user directories before moving app, from UUID " + fromVolumeUuid + + " to UUID " + toVolumeUuid); } - final UserInfo user = mPm.mUserManager.getUserInfo(userId); - if (user == null) return; - // This call is same as StorageEventHelper#loadPrivatePackagesInner which prepares - // the storage before reconciling apps - storageManager.prepareUserStorage(volumeUuid, user.id, user.serialNumber, - StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); - } - - private boolean doesDataDirectoryExistForUser(String uuid, int userId) { - final File userDirectoryFile = Environment.getDataUserCeDirectory(uuid, userId); - return userDirectoryFile != null && userDirectoryFile.exists(); + final StorageManagerInternal smInternal = + mPm.mInjector.getLocalService(StorageManagerInternal.class); + final ArrayList<UserInfo> users = new ArrayList<>(); + for (int userId : userIds) { + final UserInfo user = mPm.mUserManager.getUserInfo(userId); + users.add(user); + } + smInternal.prepareUserStorageForMove(fromVolumeUuid, toVolumeUuid, users); } public static class MoveCallbacks extends Handler { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 303f3216ba75..627065587825 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -68,6 +68,7 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.compat.annotation.EnabledSince; import android.content.ComponentName; import android.content.Context; @@ -312,6 +313,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final long SILENT_INSTALL_ALLOWED = 265131695L; /** + * The system supports pre-approval and update ownership features from + * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34}. The change id is used to make sure + * the system includes the fix of pre-approval with update ownership case. When checking the + * change id, if it is disabled, it means the build includes the fix. The more detail is on + * b/293644536. + * See {@link PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)} and + * {@link #requestUserPreapproval(PreapprovalDetails, IntentSender)} for more details. + */ + @Disabled + @ChangeId + private static final long PRE_APPROVAL_WITH_UPDATE_OWNERSHIP_FIX = 293644536L; + + /** * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#S} before getting the * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared @@ -927,16 +941,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (mPermissionsManuallyAccepted) { return USER_ACTION_NOT_NEEDED; } - packageName = mPackageName; + // For pre-pappvoal case, the mPackageName would be null. + if (mPackageName != null) { + packageName = mPackageName; + } else if (mPreapprovalRequested.get() && mPreapprovalDetails != null) { + packageName = mPreapprovalDetails.getPackageName(); + } else { + packageName = null; + } hasDeviceAdminReceiver = mHasDeviceAdminReceiver; } - final boolean forcePermissionPrompt = + // For the below cases, force user action prompt + // 1. installFlags includes INSTALL_FORCE_PERMISSION_PROMPT + // 2. params.requireUserAction is USER_ACTION_REQUIRED + final boolean forceUserActionPrompt = (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0 || params.requireUserAction == SessionParams.USER_ACTION_REQUIRED; - if (forcePermissionPrompt) { - return USER_ACTION_REQUIRED; - } + final int userActionNotTypicallyNeededResponse = forceUserActionPrompt + ? USER_ACTION_REQUIRED + : USER_ACTION_NOT_NEEDED; + // It is safe to access mInstallerUid and mInstallSource without lock // because they are immutable after sealing. final Computer snapshot = mPm.snapshotComputer(); @@ -990,7 +1015,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { || isInstallerDeviceOwnerOrAffiliatedProfileOwner(); if (noUserActionNecessary) { - return USER_ACTION_NOT_NEEDED; + return userActionNotTypicallyNeededResponse; } if (isUpdateOwnershipEnforcementEnabled @@ -1003,7 +1028,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (isPermissionGranted) { - return USER_ACTION_NOT_NEEDED; + return userActionNotTypicallyNeededResponse; } if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 82587c529e29..651845e71924 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -746,10 +746,16 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { } @Override - public void notifyComponentUsed(@NonNull String packageName, - @UserIdInt int userId, @NonNull String recentCallingPackage) { + public void notifyComponentUsed(@NonNull String packageName, @UserIdInt int userId, + @NonNull String recentCallingPackage, @NonNull String debugInfo) { mService.notifyComponentUsed(snapshot(), packageName, userId, - recentCallingPackage); + recentCallingPackage, debugInfo); + } + + @Override + public boolean isPackageQuarantined(@NonNull String packageName, + @UserIdInt int userId) { + return snapshot().isPackageQuarantinedForUser(packageName, userId); } @NonNull diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index dc42644b04c0..6c0aeecbf68b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4583,7 +4583,18 @@ public class PackageManagerService implements PackageSender, TestUtilityService } void notifyComponentUsed(@NonNull Computer snapshot, @NonNull String packageName, - @UserIdInt int userId, @NonNull String recentCallingPackage) { + @UserIdInt int userId, @NonNull String recentCallingPackage, + @NonNull String debugInfo) { + synchronized (mLock) { + final PackageUserStateInternal userState = mSettings.getPackageLPr( + packageName).getUserStateOrDefault(userId); + if (userState.isQuarantined()) { + Slog.i(TAG, + "Component is quarantined+suspended but being used: " + + packageName + " by " + recentCallingPackage + ", debugInfo: " + + debugInfo); + } + } PackageManagerService.this .setPackageStoppedState(snapshot, packageName, false /* stopped */, userId); @@ -6120,14 +6131,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, - SuspendDialogInfo dialogInfo, String callingPackage, int userId) { + SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) { final int callingUid = Binder.getCallingUid(); final Computer snapshot = snapshotComputer(); enforceCanSetPackagesSuspendedAsUser(snapshot, callingPackage, callingUid, userId, "setPackagesSuspendedAsUser"); + boolean quarantined = ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) + && Flags.quarantinedEnabled(); return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended, appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid, - false /* forQuietMode */); + false /* forQuietMode */, quarantined); } @Override @@ -6251,7 +6264,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) { int uid = Binder.getCallingUid(); - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, userId, uid); + int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), uid, + userId, true, true, "registerPackageMonitorCallback", + mContext.getPackageName()); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, targetUserId, + uid); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index ce0e7ad7b201..8bdbe04ec4e6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -226,6 +226,8 @@ class PackageManagerShellCommand extends ShellCommand { return runPath(); case "dump": return runDump(); + case "dump-package": + return runDumpPackage(); case "list": return runList(); case "gc": @@ -288,9 +290,11 @@ class PackageManagerShellCommand extends ShellCommand { case "unhide": return runSetHiddenSetting(false); case "suspend": - return runSuspend(true); + return runSuspend(true, 0); + case "suspend-quarantine": + return runSuspend(true, PackageManager.FLAG_SUSPEND_QUARANTINED); case "unsuspend": - return runSuspend(false); + return runSuspend(false, 0); case "set-distracting-restriction": return runSetDistractingRestriction(); case "get-distracting-restriction": @@ -976,6 +980,7 @@ class PackageManagerShellCommand extends ShellCommand { boolean listInstaller = false; boolean showUid = false; boolean showVersionCode = false; + boolean listQuarantinedOnly = false; boolean listApexOnly = false; boolean showStopped = false; int uid = -1; @@ -1006,6 +1011,9 @@ class PackageManagerShellCommand extends ShellCommand { case "-s": listSystem = true; break; + case "-q": + listQuarantinedOnly = true; + break; case "-U": showUid = true; break; @@ -1091,6 +1099,10 @@ class PackageManagerShellCommand extends ShellCommand { || (listApexOnly && !isApex)) { continue; } + if (listQuarantinedOnly && !mInterface.isPackageQuarantinedForUser(info.packageName, + translatedUserId)) { + continue; + } String name = null; if (showSdks) { @@ -2644,7 +2656,7 @@ class PackageManagerShellCommand extends ShellCommand { } } - private int runSuspend(boolean suspendedState) { + private int runSuspend(boolean suspendedState, int flags) { final PrintWriter pw = getOutPrintWriter(); int userId = UserHandle.USER_SYSTEM; String dialogMessage = null; @@ -2712,7 +2724,7 @@ class PackageManagerShellCommand extends ShellCommand { mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}), suspendedState, ((appExtras.size() > 0) ? appExtras : null), ((launcherExtras.size() > 0) ? launcherExtras : null), - info, callingPackage, translatedUserId); + info, flags, callingPackage, translatedUserId); for (int i = 0; i < packageNames.size(); i++) { final String packageName = packageNames.get(i); pw.println("Package " + packageName + " new suspended state: " @@ -3596,6 +3608,23 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runDumpPackage() { + String pkg = getNextArg(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return 1; + } + try { + ((IBinder) mInterface).dump(getOutFileDescriptor(), new String[]{pkg}); + } catch (Throwable e) { + PrintWriter pw = getErrPrintWriter(); + pw.println("Failure dumping service:"); + e.printStackTrace(pw); + pw.flush(); + } + return 0; + } + private int runSetHarmfulAppWarning() throws RemoteException { int userId = UserHandle.USER_CURRENT; @@ -4280,6 +4309,9 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" dump PACKAGE"); pw.println(" Print various system state associated with the given PACKAGE."); pw.println(""); + pw.println(" dump-package PACKAGE"); + pw.println(" Print package manager state associated with the given PACKAGE."); + pw.println(""); pw.println(" has-feature FEATURE_NAME [version]"); pw.println(" Prints true and returns exit status 0 when system has a FEATURE_NAME,"); pw.println(" otherwise prints false and returns exit status 1"); @@ -4297,7 +4329,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" Options:"); pw.println(" -v: shows the location of the library in the device's filesystem"); pw.println(""); - pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] "); + pw.println(" list packages [-f] [-d] [-e] [-s] [-q] [-3] [-i] [-l] [-u] [-U] "); pw.println(" [--show-versioncode] [--apex-only] [--factory-only]"); pw.println(" [--uid UID] [--user USER_ID] [FILTER]"); pw.println(" Prints all packages; optionally only those whose name contains"); @@ -4307,6 +4339,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" -d: filter to only show disabled packages"); pw.println(" -e: filter to only show enabled packages"); pw.println(" -s: filter to only show system packages"); + pw.println(" -q: filter to only show quarantined packages"); pw.println(" -3: filter to only show third party packages"); pw.println(" -i: see the installer for the packages"); pw.println(" -l: ignored (used for compatibility with older releases)"); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index dee31ec460d7..14693a67f2a5 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -937,8 +937,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal otherState.isHidden(), otherState.getDistractionFlags(), otherState.getSuspendParams() == null ? null : otherState.getSuspendParams().untrackedStorage(), - otherState.isInstantApp(), - otherState.isVirtualPreload(), otherState.getLastDisableAppCaller(), + otherState.isInstantApp(), otherState.isVirtualPreload(), + otherState.getLastDisableAppCaller(), otherState.getEnabledComponentsNoCopy() == null ? null : otherState.getEnabledComponentsNoCopy().untrackedStorage(), otherState.getDisabledComponentsNoCopy() == null diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index c6135e0f582e..b6da4627c45a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1918,9 +1918,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile ArraySet<String> enabledComponents = null; ArraySet<String> disabledComponents = null; - PersistableBundle suspendedAppExtras = null; - PersistableBundle suspendedLauncherExtras = null; SuspendDialogInfo oldSuspendDialogInfo = null; + PersistableBundle oldSuspendedAppExtras = null; + PersistableBundle oldSuspendedLauncherExtras = null; ArchiveState archiveState = null; int packageDepth = parser.getDepth(); @@ -1939,16 +1939,17 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile case TAG_DISABLED_COMPONENTS: disabledComponents = readComponentsLPr(parser); break; + case TAG_SUSPENDED_DIALOG_INFO: + oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser); + break; case TAG_SUSPENDED_APP_EXTRAS: - suspendedAppExtras = PersistableBundle.restoreFromXml(parser); + oldSuspendedAppExtras = PersistableBundle.restoreFromXml( + parser); break; case TAG_SUSPENDED_LAUNCHER_EXTRAS: - suspendedLauncherExtras = PersistableBundle.restoreFromXml( + oldSuspendedLauncherExtras = PersistableBundle.restoreFromXml( parser); break; - case TAG_SUSPENDED_DIALOG_INFO: - oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser); - break; case TAG_SUSPEND_PARAMS: final String suspendingPackage = parser.getAttributeValue(null, ATTR_SUSPENDING_PACKAGE); @@ -1979,8 +1980,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (suspended && suspendParamsMap == null) { final SuspendParams suspendParams = new SuspendParams( oldSuspendDialogInfo, - suspendedAppExtras, - suspendedLauncherExtras); + oldSuspendedAppExtras, + oldSuspendedLauncherExtras, + false /* quarantined */); suspendParamsMap = new ArrayMap<>(); suspendParamsMap.put(oldSuspendingPackage, suspendParams); } @@ -1989,13 +1991,12 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile setBlockUninstallLPw(userId, name, true); } ps.setUserState(userId, ceDataInode, enabled, installed, stopped, - notLaunched, - hidden, distractionFlags, suspendParamsMap, instantApp, - virtualPreload, - enabledCaller, enabledComponents, disabledComponents, installReason, - uninstallReason, harmfulAppWarning, splashScreenTheme, - firstInstallTime != 0 ? firstInstallTime : - origFirstInstallTimes.getOrDefault(name, 0L), + notLaunched, hidden, distractionFlags, suspendParamsMap, instantApp, + virtualPreload, enabledCaller, enabledComponents, + disabledComponents, installReason, uninstallReason, + harmfulAppWarning, splashScreenTheme, + firstInstallTime != 0 ? firstInstallTime + : origFirstInstallTimes.getOrDefault(name, 0L), minAspectRatio, archiveState); mDomainVerificationManager.setLegacyUserState(name, userId, verifState); @@ -4824,6 +4825,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile pw.print(userState.isNotLaunched() ? "l" : "L"); pw.print(userState.isInstantApp() ? "IA" : "ia"); pw.print(userState.isVirtualPreload() ? "VPI" : "vpi"); + pw.print(userState.isQuarantined() ? "Q" : "q"); String harmfulAppWarning = userState.getHarmfulAppWarning(); pw.print(harmfulAppWarning != null ? "HA" : "ha"); pw.print(","); @@ -5185,6 +5187,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile pw.print(userState.isInstantApp()); pw.print(" virtual="); pw.println(userState.isVirtualPreload()); + pw.print(" quarantined="); + pw.print(userState.isQuarantined()); pw.print(" installReason="); pw.println(userState.getInstallReason()); @@ -5207,6 +5211,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (params != null) { pw.print(" dialogInfo="); pw.print(params.getDialogInfo()); + pw.print(" quarantined="); + pw.println(params.isQuarantined()); } pw.println(); } diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index 94e09f1d0bf0..ddb045df4eaf 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -115,17 +115,18 @@ public final class SuspendPackageHelper { boolean suspended, @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo, @NonNull String callingPackage, @UserIdInt int userId, int callingUid, - boolean forQuietMode) { + boolean forQuietMode, boolean quarantined) { if (ArrayUtils.isEmpty(packageNames)) { return packageNames; } - if (suspended && !forQuietMode && !isSuspendAllowedForUser(snapshot, userId, callingUid)) { + if (suspended && !quarantined && !forQuietMode && !isSuspendAllowedForUser(snapshot, userId, + callingUid)) { Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId); return packageNames; } final SuspendParams newSuspendParams = - new SuspendParams(dialogInfo, appExtras, launcherExtras); + new SuspendParams(dialogInfo, appExtras, launcherExtras, quarantined); final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length); @@ -160,7 +161,6 @@ public final class SuspendPackageHelper { final WatchedArrayMap<String, SuspendParams> suspendParamsMap = packageState.getUserStateOrDefault(userId).getSuspendParams(); - SuspendParams oldSuspendParams = suspendParamsMap == null ? null : suspendParamsMap.get(packageName); boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams); @@ -213,14 +213,15 @@ public final class SuspendPackageHelper { sendPackagesSuspendedForUser( suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, - changedPackages, notifyUids.toArray(), userId); + changedPackages, notifyUids.toArray(), quarantined, userId); sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId); mPm.scheduleWritePackageRestrictions(userId); } // Send the suspension changed broadcast to ensure suspension state is not stale. if (!changedPackagesList.isEmpty()) { sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, - changedPackagesList.toArray(new String[0]), changedUids.toArray(), userId); + changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined, + userId); } return unmodifiablePackages.toArray(new String[0]); } @@ -354,7 +355,7 @@ public final class SuspendPackageHelper { new String[unsuspendedPackages.size()]); sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId); sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED, - packageArray, unsuspendedUids.toArray(), userId); + packageArray, unsuspendedUids.toArray(), false, userId); } } @@ -618,11 +619,14 @@ public final class SuspendPackageHelper { */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList, - @NonNull int[] uidList, int userId) { + @NonNull int[] uidList, boolean quarantined, int userId) { final Handler handler = mInjector.getHandler(); final Bundle extras = new Bundle(3); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); + if (quarantined) { + extras.putBoolean(Intent.EXTRA_QUARANTINED, true); + } final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND; final Bundle options = new BroadcastOptions() .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) @@ -672,7 +676,7 @@ public final class SuspendPackageHelper { snapshot, toSuspend.toArray(new String[0]), suspend, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID, - false /* forQuietMode */))); + false /* forQuietMode */, false /* quarantined */))); } return unsuspendable.toArray(String[]::new); } @@ -716,7 +720,7 @@ public final class SuspendPackageHelper { setPackagesSuspended(snapshot, toSuspend.toArray(new String[0]), suspend, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID, - true /* forQuietMode */); + true /* forQuietMode */, false /* quarantined */); } private Set<String> packagesToSuspendInQuietMode(Computer snapshot, int userId) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 385dfcb8e4ec..f2797eb48305 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -4907,6 +4907,11 @@ public class UserManagerService extends IUserManager.Stub { USER_OPERATION_ERROR_UNKNOWN); } } + if (isMainUser && getMainUserIdUnchecked() != UserHandle.USER_NULL) { + throwCheckedUserOperationException( + "Cannot add user with FLAG_MAIN as main user already exists.", + UserManager.USER_OPERATION_ERROR_MAX_USERS); + } if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) { throwCheckedUserOperationException( "Cannot add more users of type " + userType diff --git a/services/core/java/com/android/server/pm/flags.aconfig b/services/core/java/com/android/server/pm/flags.aconfig new file mode 100644 index 000000000000..368a8432c805 --- /dev/null +++ b/services/core/java/com/android/server/pm/flags.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.pm" + +flag { + name: "quarantined_enabled" + namespace: "package_manager_service" + description: "Feature flag for Quarantined state" + bug: "269127435" +} diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index 94e959968179..27812dffd215 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -379,7 +379,10 @@ public class PackageInfoUtils { | flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD) | flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN); - if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + if ((flags & PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS) != 0 + && state.isQuarantined()) { + ai.enabled = false; + } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { ai.enabled = true; } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java index 81915b485982..7bc518c5516e 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java @@ -209,6 +209,13 @@ public interface PackageUserState { boolean isVirtualPreload(); /** + * @return whether the package is quarantined in order to minimize ad-spam and pop ups + * when-not-in-use. + * @hide + */ + boolean isQuarantined(); + + /** * The "package:type/entry" form of the theme resource ID previously set as the splash screen. * * @hide @@ -225,6 +232,7 @@ public interface PackageUserState { */ @PackageManager.UserMinAspectRatio int getMinAspectRatio(); + /** * Information about the archived state of an app. Set only if an app is archived. * @@ -233,4 +241,5 @@ public interface PackageUserState { @Immutable.Ignore @Nullable ArchiveState getArchiveState(); + } diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java index cce18a832ea2..3534d7564c50 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java @@ -129,6 +129,11 @@ class PackageUserStateDefault implements PackageUserStateInternal { return false; } + @Override + public boolean isQuarantined() { + return false; + } + @Nullable @Override public String getSplashScreenTheme() { diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java index d8c8af6c8fd1..2349fbff11a7 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java @@ -670,6 +670,21 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt return getBoolean(Booleans.VIRTUAL_PRELOADED); } + @Override + public boolean isQuarantined() { + if (!isSuspended()) { + return false; + } + final var suspendParams = mSuspendParams; + for (int i = 0, size = suspendParams.size(); i < size; i++) { + final SuspendParams params = suspendParams.valueAt(i); + if (params.isQuarantined()) { + return true; + } + } + return false; + } + @@ -879,7 +894,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt } @DataClass.Generated( - time = 1691186062924L, + time = 1691601685901L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java", inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)") diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java index 15e3d0c7c432..e3424534c275 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java @@ -16,6 +16,7 @@ package com.android.server.pm.pkg; +import static android.content.pm.PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS; import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; @@ -122,6 +123,10 @@ public class PackageUserStateUtils { return true; } + if ((flags & FILTER_OUT_QUARANTINED_COMPONENTS) != 0 && state.isQuarantined()) { + return false; + } + // First check if the overall package is disabled; if the package is // enabled then fall through to check specific component switch (state.getEnabledState()) { diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java index dc48a337d600..153238fa6866 100644 --- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java +++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java @@ -42,16 +42,25 @@ public final class SuspendParams { private static final String TAG_DIALOG_INFO = "dialog-info"; private static final String TAG_APP_EXTRAS = "app-extras"; private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras"; + private static final String ATTR_QUARANTINED = "quarantined"; - private final SuspendDialogInfo dialogInfo; - private final PersistableBundle appExtras; - private final PersistableBundle launcherExtras; + private final SuspendDialogInfo mDialogInfo; + private final PersistableBundle mAppExtras; + private final PersistableBundle mLauncherExtras; + + private final boolean mQuarantined; public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras, PersistableBundle launcherExtras) { - this.dialogInfo = dialogInfo; - this.appExtras = appExtras; - this.launcherExtras = launcherExtras; + this(dialogInfo, appExtras, launcherExtras, false /* quarantined */); + } + + public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras, + PersistableBundle launcherExtras, boolean quarantined) { + this.mDialogInfo = dialogInfo; + this.mAppExtras = appExtras; + this.mLauncherExtras = launcherExtras; + this.mQuarantined = quarantined; } @Override @@ -63,13 +72,16 @@ public final class SuspendParams { return false; } final SuspendParams other = (SuspendParams) obj; - if (!Objects.equals(dialogInfo, other.dialogInfo)) { + if (!Objects.equals(mDialogInfo, other.mDialogInfo)) { return false; } - if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) { + if (!BaseBundle.kindofEquals(mAppExtras, other.mAppExtras)) { return false; } - if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) { + if (!BaseBundle.kindofEquals(mLauncherExtras, other.mLauncherExtras)) { + return false; + } + if (mQuarantined != other.mQuarantined) { return false; } return true; @@ -77,9 +89,10 @@ public final class SuspendParams { @Override public int hashCode() { - int hashCode = Objects.hashCode(dialogInfo); - hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0); - hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0); + int hashCode = Objects.hashCode(mDialogInfo); + hashCode = 31 * hashCode + ((mAppExtras != null) ? mAppExtras.size() : 0); + hashCode = 31 * hashCode + ((mLauncherExtras != null) ? mLauncherExtras.size() : 0); + hashCode = 31 * hashCode + Boolean.hashCode(mQuarantined); return hashCode; } @@ -89,25 +102,26 @@ public final class SuspendParams { * @param out the {@link XmlSerializer} object */ public void saveToXml(TypedXmlSerializer out) throws IOException { - if (dialogInfo != null) { + out.attributeBoolean(null, ATTR_QUARANTINED, mQuarantined); + if (mDialogInfo != null) { out.startTag(null, TAG_DIALOG_INFO); - dialogInfo.saveToXml(out); + mDialogInfo.saveToXml(out); out.endTag(null, TAG_DIALOG_INFO); } - if (appExtras != null) { + if (mAppExtras != null) { out.startTag(null, TAG_APP_EXTRAS); try { - appExtras.saveToXml(out); + mAppExtras.saveToXml(out); } catch (XmlPullParserException e) { Slog.e(LOG_TAG, "Exception while trying to write appExtras." + " Will be lost on reboot", e); } out.endTag(null, TAG_APP_EXTRAS); } - if (launcherExtras != null) { + if (mLauncherExtras != null) { out.startTag(null, TAG_LAUNCHER_EXTRAS); try { - launcherExtras.saveToXml(out); + mLauncherExtras.saveToXml(out); } catch (XmlPullParserException e) { Slog.e(LOG_TAG, "Exception while trying to write launcherExtras." + " Will be lost on reboot", e); @@ -127,6 +141,8 @@ public final class SuspendParams { PersistableBundle readAppExtras = null; PersistableBundle readLauncherExtras = null; + final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false); + final int currentDepth = in.getDepth(); int type; try { @@ -157,18 +173,22 @@ public final class SuspendParams { Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams," + " some fields may default", e); } - return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras); + return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras, quarantined); } public SuspendDialogInfo getDialogInfo() { - return dialogInfo; + return mDialogInfo; } public PersistableBundle getAppExtras() { - return appExtras; + return mAppExtras; } public PersistableBundle getLauncherExtras() { - return launcherExtras; + return mLauncherExtras; + } + + public boolean isQuarantined() { + return mQuarantined; } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index faf132e0d80d..2f68021bf356 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -208,7 +208,6 @@ import com.android.internal.policy.LogDecelerateInterpolator; import com.android.internal.policy.PhoneWindow; import com.android.internal.policy.TransitionAnimation; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.server.AccessibilityManagerInternal; import com.android.server.ExtconStateObserver; @@ -622,9 +621,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mAllowTheaterModeWakeFromLidSwitch; private boolean mAllowTheaterModeWakeFromWakeGesture; - // Whether to support long press from power button in non-interactive mode + // If true, the power button long press behavior will be invoked even if the default display is + // non-interactive. If false, the power button long press behavior will be skipped if the + // default display is non-interactive. private boolean mSupportLongPressPowerWhenNonInteractive; + // If true, the power button short press behavior will be always invoked as long as the default + // display is on, even if the display is not interactive. If false, the power button short press + // behavior will be skipped if the default display is non-interactive. + private boolean mSupportShortPressPowerWhenDefaultDisplayOn; + // Whether to go to sleep entering theater mode from power button private boolean mGoToSleepOnButtonPressTheaterMode; @@ -1041,7 +1047,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) { + private void powerPress(long eventTime, int count) { // SideFPS still needs to know about suppressed power buttons, in case it needs to block // an auth attempt. if (count == 1) { @@ -1055,9 +1061,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean interactive = mDefaultDisplayPolicy.isAwake(); - Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive - + " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive - + " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior); + Slog.d( + TAG, + "powerPress: eventTime=" + + eventTime + + " interactive=" + + interactive + + " count=" + + count + + " mShortPressOnPowerBehavior=" + + mShortPressOnPowerBehavior); if (count == 2) { powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior); @@ -1065,12 +1078,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior); } else if (count > 3 && count <= getMaxMultiPressPowerCount()) { Slog.d(TAG, "No behavior defined for power press count " + count); - } else if (count == 1 && interactive && !beganFromNonInteractive) { - if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) { - Slog.i(TAG, "Suppressing power key because the user is interacting with the " - + "fingerprint sensor"); - return; - } + } else if (count == 1 && shouldHandleShortPressPowerAction(interactive, eventTime)) { switch (mShortPressOnPowerBehavior) { case SHORT_PRESS_POWER_NOTHING: break; @@ -1118,6 +1126,44 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private boolean shouldHandleShortPressPowerAction(boolean interactive, long eventTime) { + if (mSupportShortPressPowerWhenDefaultDisplayOn) { + final boolean defaultDisplayOn = Display.isOnState(mDefaultDisplay.getState()); + final boolean beganFromDefaultDisplayOn = + mSingleKeyGestureDetector.beganFromDefaultDisplayOn(); + if (!defaultDisplayOn || !beganFromDefaultDisplayOn) { + Slog.v( + TAG, + "Ignoring short press of power button because the default display is not" + + " on. defaultDisplayOn=" + + defaultDisplayOn + + ", beganFromDefaultDisplayOn=" + + beganFromDefaultDisplayOn); + return false; + } + return true; + } + final boolean beganFromNonInteractive = mSingleKeyGestureDetector.beganFromNonInteractive(); + if (!interactive || beganFromNonInteractive) { + Slog.v( + TAG, + "Ignoring short press of power button because the device is not interactive." + + " interactive=" + + interactive + + ", beganFromNonInteractive=" + + beganFromNonInteractive); + return false; + } + if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) { + Slog.i( + TAG, + "Suppressing power key because the user is interacting with the " + + "fingerprint sensor"); + return false; + } + return true; + } + /** * Attempt to dream from a power button press. * @@ -2231,6 +2277,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { mSupportLongPressPowerWhenNonInteractive = mContext.getResources().getBoolean( com.android.internal.R.bool.config_supportLongPressPowerWhenNonInteractive); + mSupportShortPressPowerWhenDefaultDisplayOn = + mContext.getResources() + .getBoolean( + com.android.internal.R.bool + .config_supportShortPressPowerWhenDefaultDisplayOn); mLongPressOnBackBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_longPressOnBackBehavior); @@ -2518,8 +2569,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override void onPress(long downTime) { - powerPress(downTime, 1 /*count*/, - mSingleKeyGestureDetector.beganFromNonInteractive()); + powerPress(downTime, 1 /*count*/); } @Override @@ -2550,7 +2600,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override void onMultiPress(long downTime, int count) { - powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive()); + powerPress(downTime, count); } } @@ -4323,10 +4373,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // This could prevent some wrong state in multi-displays environment, // the default display may turned off but interactive is true. - final boolean isDefaultDisplayOn = mDefaultDisplayPolicy.isAwake(); - final boolean interactiveAndOn = interactive && isDefaultDisplayOn; + final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState()); + final boolean isDefaultDisplayAwake = mDefaultDisplayPolicy.isAwake(); + final boolean interactiveAndAwake = interactive && isDefaultDisplayAwake; if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - handleKeyGesture(event, interactiveAndOn); + handleKeyGesture(event, interactiveAndAwake, isDefaultDisplayOn); } // Enable haptics if down and virtual key without multiple repetitions. If this is a hard @@ -4479,7 +4530,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately if (down) { - interceptPowerKeyDown(event, interactiveAndOn); + interceptPowerKeyDown(event, interactiveAndAwake); } else { interceptPowerKeyUp(event, canceled); } @@ -4695,7 +4746,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } - private void handleKeyGesture(KeyEvent event, boolean interactive) { + private void handleKeyGesture(KeyEvent event, boolean interactive, boolean defaultDisplayOn) { if (mKeyCombinationManager.interceptKey(event, interactive)) { // handled by combo keys manager. mSingleKeyGestureDetector.reset(); @@ -4711,7 +4762,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - mSingleKeyGestureDetector.interceptKey(event, interactive); + mSingleKeyGestureDetector.interceptKey(event, interactive, defaultDisplayOn); } // The camera gesture will be detected by GestureLauncherService. @@ -6167,6 +6218,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print("mTriplePressOnPowerBehavior="); pw.println(multiPressOnPowerBehaviorToString(mTriplePressOnPowerBehavior)); pw.print(prefix); + pw.print("mSupportShortPressPowerWhenDefaultDisplayOn="); + pw.println(mSupportShortPressPowerWhenDefaultDisplayOn); + pw.print(prefix); pw.print("mPowerVolUpBehavior="); pw.println(powerVolumeUpBehaviorToString(mPowerVolUpBehavior)); pw.print(prefix); diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java index b999bbb3dce2..5fc0637debea 100644 --- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java +++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java @@ -43,6 +43,7 @@ public final class SingleKeyGestureDetector { private int mKeyPressCounter; private boolean mBeganFromNonInteractive = false; + private boolean mBeganFromDefaultDisplayOn = false; private final ArrayList<SingleKeyRule> mRules = new ArrayList(); private SingleKeyRule mActiveRule = null; @@ -194,11 +195,12 @@ public final class SingleKeyGestureDetector { mRules.remove(rule); } - void interceptKey(KeyEvent event, boolean interactive) { + void interceptKey(KeyEvent event, boolean interactive, boolean defaultDisplayOn) { if (event.getAction() == KeyEvent.ACTION_DOWN) { - // Store the non interactive state when first down. + // Store the non interactive state and display on state when first down. if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) { mBeganFromNonInteractive = !interactive; + mBeganFromDefaultDisplayOn = defaultDisplayOn; } interceptKeyDown(event); } else { @@ -388,6 +390,10 @@ public final class SingleKeyGestureDetector { return mBeganFromNonInteractive; } + boolean beganFromDefaultDisplayOn() { + return mBeganFromDefaultDisplayOn; + } + void dump(String prefix, PrintWriter pw) { pw.println(prefix + "SingleKey rules:"); for (SingleKeyRule rule : mRules) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 1e6486a17fe7..4a4214f7af83 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -308,10 +308,8 @@ public final class PowerManagerService extends SystemService private final ServiceThread mHandlerThread; private final Handler mHandler; private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; - private final BatterySaverController mBatterySaverController; - private final BatterySaverPolicy mBatterySaverPolicy; + @Nullable private final BatterySaverStateMachine mBatterySaverStateMachine; - private final BatterySavingStats mBatterySavingStats; private final LowPowerStandbyController mLowPowerStandbyController; private final AttentionDetector mAttentionDetector; private final FaceDownDetector mFaceDownDetector; @@ -325,6 +323,8 @@ public final class PowerManagerService extends SystemService private final PermissionCheckerWrapper mPermissionCheckerWrapper; private final PowerPropertiesWrapper mPowerPropertiesWrapper; private final DeviceConfigParameterProvider mDeviceConfigProvider; + // True if battery saver is supported on this device. + private final boolean mBatterySaverSupported; private boolean mDisableScreenWakeLocksWhileCached; @@ -968,20 +968,13 @@ public final class PowerManagerService extends SystemService return suspendBlocker; } - BatterySaverPolicy createBatterySaverPolicy( - Object lock, Context context, BatterySavingStats batterySavingStats) { - return new BatterySaverPolicy(lock, context, batterySavingStats); - } - - BatterySaverController createBatterySaverController( - Object lock, Context context, BatterySaverPolicy batterySaverPolicy, - BatterySavingStats batterySavingStats) { - return new BatterySaverController(lock, context, BackgroundThread.get().getLooper(), + BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) { + BatterySavingStats batterySavingStats = new BatterySavingStats(lock); + BatterySaverPolicy batterySaverPolicy = new BatterySaverPolicy(lock, context, + batterySavingStats); + BatterySaverController batterySaverController = new BatterySaverController(lock, + context, BackgroundThread.get().getLooper(), batterySaverPolicy, batterySavingStats); - } - - BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context, - BatterySaverController batterySaverController) { return new BatterySaverStateMachine(lock, context, batterySaverController); } @@ -1155,13 +1148,11 @@ public final class PowerManagerService extends SystemService mFaceDownDetector = new FaceDownDetector(this::onFlip); mScreenUndimDetector = new ScreenUndimDetector(); - mBatterySavingStats = new BatterySavingStats(mLock); - mBatterySaverPolicy = - mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats); - mBatterySaverController = mInjector.createBatterySaverController(mLock, mContext, - mBatterySaverPolicy, mBatterySavingStats); - mBatterySaverStateMachine = mInjector.createBatterySaverStateMachine(mLock, mContext, - mBatterySaverController); + mBatterySaverSupported = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_batterySaverSupported); + mBatterySaverStateMachine = + mBatterySaverSupported ? mInjector.createBatterySaverStateMachine(mLock, mContext) + : null; mLowPowerStandbyController = mInjector.createLowPowerStandbyController(mContext, Looper.getMainLooper()); @@ -1300,7 +1291,9 @@ public final class PowerManagerService extends SystemService mBootCompleted = true; mDirty |= DIRTY_BOOT_COMPLETED; - mBatterySaverStateMachine.onBootCompleted(); + if (mBatterySaverSupported) { + mBatterySaverStateMachine.onBootCompleted(); + } userActivityNoUpdateLocked( now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); @@ -1390,8 +1383,9 @@ public final class PowerManagerService extends SystemService final ContentResolver resolver = mContext.getContentResolver(); mConstants.start(resolver); - mBatterySaverController.systemReady(); - mBatterySaverPolicy.systemReady(); + if (mBatterySaverSupported) { + mBatterySaverStateMachine.systemReady(); + } mFaceDownDetector.systemReady(mContext); mScreenUndimDetector.systemReady(mContext); @@ -2601,7 +2595,10 @@ public final class PowerManagerService extends SystemService } } - mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow); + if (mBatterySaverSupported) { + mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, + mBatteryLevelLow); + } } } @@ -3554,7 +3551,11 @@ public final class PowerManagerService extends SystemService mDozeScreenStateOverrideFromDreamManager, mDozeScreenBrightnessOverrideFromDreamManagerFloat, mDrawWakeLockOverrideFromSidekick, - mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS), + mBatterySaverSupported + ? + mBatterySaverStateMachine.getBatterySaverPolicy() + .getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS) + : new PowerSaveState.Builder().build(), sQuiescent, mDozeAfterScreenOff, mBootCompleted, mScreenBrightnessBoostInProgress, mRequestWaitForNegativeProximity); int wakefulness = powerGroup.getWakefulnessLocked(); @@ -3884,7 +3885,7 @@ public final class PowerManagerService extends SystemService if (DEBUG) { Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered); } - if (mIsPowered) { + if (mIsPowered || !mBatterySaverSupported) { return false; } @@ -4378,7 +4379,8 @@ public final class PowerManagerService extends SystemService private boolean setPowerModeInternal(int mode, boolean enabled) { // Maybe filter the event. - if (mode == Mode.LAUNCH && enabled && mBatterySaverController.isLaunchBoostDisabled()) { + if (mBatterySaverStateMachine == null || (mode == Mode.LAUNCH && enabled + && mBatterySaverStateMachine.getBatterySaverController().isLaunchBoostDisabled())) { return false; } return mNativeWrapper.nativeSetPowerMode(mode, enabled); @@ -4718,8 +4720,12 @@ public final class PowerManagerService extends SystemService pw.println(); pw.println("Display Power: " + mDisplayPowerCallbacks); - mBatterySaverPolicy.dump(pw); - mBatterySaverStateMachine.dump(pw); + if (mBatterySaverSupported) { + mBatterySaverStateMachine.getBatterySaverPolicy().dump(pw); + mBatterySaverStateMachine.dump(pw); + } else { + pw.println("Battery Saver: DISABLED"); + } mAttentionDetector.dump(pw); pw.println(); @@ -5101,8 +5107,10 @@ public final class PowerManagerService extends SystemService proto.end(uIDToken); } - mBatterySaverStateMachine.dumpProto(proto, - PowerManagerServiceDumpProto.BATTERY_SAVER_STATE_MACHINE); + if (mBatterySaverSupported) { + mBatterySaverStateMachine.dumpProto(proto, + PowerManagerServiceDumpProto.BATTERY_SAVER_STATE_MACHINE); + } mHandler.getLooper().dumpDebug(proto, PowerManagerServiceDumpProto.LOOPER); @@ -5986,7 +5994,8 @@ public final class PowerManagerService extends SystemService public boolean isPowerSaveMode() { final long ident = Binder.clearCallingIdentity(); try { - return mBatterySaverController.isEnabled(); + return mBatterySaverSupported + && mBatterySaverStateMachine.getBatterySaverController().isEnabled(); } finally { Binder.restoreCallingIdentity(ident); } @@ -5996,7 +6005,12 @@ public final class PowerManagerService extends SystemService public PowerSaveState getPowerSaveState(@ServiceType int serviceType) { final long ident = Binder.clearCallingIdentity(); try { - return mBatterySaverPolicy.getBatterySaverPolicy(serviceType); + // Return default PowerSaveState if battery saver is not supported. + return mBatterySaverSupported + ? + mBatterySaverStateMachine.getBatterySaverPolicy().getBatterySaverPolicy( + serviceType) + : new PowerSaveState.Builder().build(); } finally { Binder.restoreCallingIdentity(ident); } @@ -6017,11 +6031,24 @@ public final class PowerManagerService extends SystemService } } + @Override + public boolean isBatterySaverSupported() { + final long ident = Binder.clearCallingIdentity(); + try { + return mBatterySaverSupported; + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override // Binder call public BatterySaverPolicyConfig getFullPowerSavePolicy() { + // Return default BatterySaverPolicyConfig if battery saver is not supported. final long ident = Binder.clearCallingIdentity(); try { - return mBatterySaverStateMachine.getFullBatterySaverPolicy(); + return mBatterySaverSupported + ? mBatterySaverStateMachine.getFullBatterySaverPolicy() + : new BatterySaverPolicyConfig.Builder().build(); } finally { Binder.restoreCallingIdentity(ident); } @@ -6036,7 +6063,8 @@ public final class PowerManagerService extends SystemService } final long ident = Binder.clearCallingIdentity(); try { - return mBatterySaverStateMachine.setFullBatterySaverPolicy(config); + return mBatterySaverSupported + && mBatterySaverStateMachine.setFullBatterySaverPolicy(config); } finally { Binder.restoreCallingIdentity(ident); } @@ -6073,7 +6101,8 @@ public final class PowerManagerService extends SystemService } final long ident = Binder.clearCallingIdentity(); try { - return mBatterySaverStateMachine.setAdaptiveBatterySaverPolicy(config); + return mBatterySaverSupported + && mBatterySaverStateMachine.setAdaptiveBatterySaverPolicy(config); } finally { Binder.restoreCallingIdentity(ident); } @@ -6088,7 +6117,8 @@ public final class PowerManagerService extends SystemService } final long ident = Binder.clearCallingIdentity(); try { - return mBatterySaverStateMachine.setAdaptiveBatterySaverEnabled(enabled); + return mBatterySaverSupported + && mBatterySaverStateMachine.setAdaptiveBatterySaverEnabled(enabled); } finally { Binder.restoreCallingIdentity(ident); } @@ -6953,12 +6983,21 @@ public final class PowerManagerService extends SystemService @Override public PowerSaveState getLowPowerState(@ServiceType int serviceType) { - return mBatterySaverPolicy.getBatterySaverPolicy(serviceType); + // Return default PowerSaveState if battery saver is not supported. + return mBatterySaverSupported + ? + mBatterySaverStateMachine.getBatterySaverPolicy().getBatterySaverPolicy( + serviceType) : new PowerSaveState.Builder().build(); } @Override public void registerLowPowerModeObserver(LowPowerModeListener listener) { - mBatterySaverController.addListener(listener); + if (mBatterySaverSupported) { + mBatterySaverStateMachine.getBatterySaverController().addListener(listener); + } else { + Slog.w(TAG, + "Battery saver is not supported, no low power mode observer registered"); + } } @Override diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index 57d69c2c93d1..8c1e9a57663f 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -40,7 +40,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.EventLogTags; import com.android.server.LocalServices; -import com.android.server.power.PowerManagerService; import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener; import com.android.server.power.batterysaver.BatterySaverPolicy.Policy; import com.android.server.power.batterysaver.BatterySaverPolicy.PolicyLevel; @@ -223,7 +222,7 @@ public class BatterySaverController implements BatterySaverPolicyListener { } /** - * Called by {@link PowerManagerService} on system ready, *with no lock held*. + * Called by {@link BatterySaverStateMachine} on system ready, *with no lock held*. */ public void systemReady() { final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java index 07d1844081a9..e3f36385ef6c 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java @@ -41,7 +41,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ConcurrentUtils; -import com.android.server.power.PowerManagerService; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -283,7 +282,7 @@ public class BatterySaverPolicy extends ContentObserver implements } /** - * Called by {@link PowerManagerService#onBootPhase}, *with no lock held.* + * Called by {@link BatterySaverStateMachine#systemReady()}, *with no lock held.* */ public void systemReady() { ConcurrentUtils.wtfIfLockHeld(TAG, mLock); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index e0bbd369560b..b22e37bd579b 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -44,6 +44,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.server.EventLogTags; import com.android.server.power.BatterySaverStateMachineProto; +import com.android.server.power.PowerManagerService; import java.io.PrintWriter; import java.time.Duration; @@ -253,6 +254,24 @@ public class BatterySaverStateMachine { com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold); } + /** + * Called by {@link PowerManagerService} on system ready, *with no lock held*. + */ + public void systemReady() { + mBatterySaverController.systemReady(); + getBatterySaverPolicy().systemReady(); + } + + /** @return Battery saver controller. */ + public BatterySaverController getBatterySaverController() { + return mBatterySaverController; + } + + /** @return Battery saver policy. */ + public BatterySaverPolicy getBatterySaverPolicy() { + return mBatterySaverController.getBatterySaverPolicy(); + } + /** @return true if the automatic percentage based mode should be used */ private boolean isAutomaticModeActiveLocked() { return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE @@ -937,8 +956,7 @@ public class BatterySaverStateMachine { ipw.print(mBatterySaverController.isAdaptiveEnabled()); if (mBatterySaverController.isAdaptiveEnabled()) { ipw.print(" (advertise="); - ipw.print( - mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled()); + ipw.print(getBatterySaverPolicy().shouldAdvertiseIsEnabled()); ipw.print(")"); } ipw.decreaseIndent(); @@ -1005,7 +1023,7 @@ public class BatterySaverStateMachine { proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED, mBatterySaverController.isAdaptiveEnabled()); proto.write(BatterySaverStateMachineProto.SHOULD_ADVERTISE_IS_ENABLED, - mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled()); + getBatterySaverPolicy().shouldAdvertiseIsEnabled()); proto.write(BatterySaverStateMachineProto.BOOT_COMPLETED, mBootCompleted); proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded); diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java index d5fd017fc8ba..f4b2f52eef9c 100644 --- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java @@ -386,7 +386,11 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat } } - return mExecutorService.schedule(syncRunnable, delayMillis, TimeUnit.MILLISECONDS); + try { + return mExecutorService.schedule(syncRunnable, delayMillis, TimeUnit.MILLISECONDS); + } catch (RejectedExecutionException e) { + return CompletableFuture.failedFuture(e); + } } public synchronized Future<?> scheduleWrite() { diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java index 76126df14bfd..87809916bcaf 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java @@ -27,6 +27,7 @@ import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO; +import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -1125,6 +1126,8 @@ public final class SensorPrivacyService extends SystemService { case MICROPHONE: mAppOpsManagerInternal.setGlobalRestriction(OP_RECORD_AUDIO, enabled, mAppOpsRestrictionToken); + mAppOpsManagerInternal.setGlobalRestriction( + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, enabled, mAppOpsRestrictionToken); mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_MICROPHONE, enabled, mAppOpsRestrictionToken); // We don't show the dialog for RECEIVE_SOUNDTRIGGER_AUDIO, but still want to diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index cb09aef90420..5b36cd6b36c1 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -3055,10 +3055,10 @@ public final class TvInputManagerService extends SystemService { TvInputInfo tvInputInfo = tvInputState.info; int inputState = tvInputState.state; int inputType = tvInputInfo.getType(); - // For non-CEC input, the value of vendorId is 0. - int vendorId = 0; - // For non-HDMI input, the value of hdmiPort is 0. - int hdmiPort = 0; + // For non-CEC input, the value of vendorId is 0xFFFFFF (16777215 in decimal). + int vendorId = 16777215; + // For non-HDMI input, the value of hdmiPort is -1. + int hdmiPort = -1; String tifSessionId = sessionState.sessionId; if (tvInputInfo.getType() == TvInputInfo.TYPE_HDMI) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 3125518a24d5..cba215ad23fd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -224,6 +224,9 @@ import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW; import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO; +import static com.android.server.wm.StartingData.AFTER_TRANSACTION_COPY_TO_CLIENT; +import static com.android.server.wm.StartingData.AFTER_TRANSACTION_IDLE; +import static com.android.server.wm.StartingData.AFTER_TRANSACTION_REMOVE_DIRECTLY; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; @@ -2690,6 +2693,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (isTransferringSplashScreen()) { return true; } + // Only do transfer after transaction has done when starting window exist. + if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) { + mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT; + return true; + } requestCopySplashScreen(); return isTransferringSplashScreen(); } @@ -2850,11 +2858,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mStartingData == null) { return; } - mStartingData.mWaitForSyncTransactionCommit = false; - if (mStartingData.mRemoveAfterTransaction) { - mStartingData.mRemoveAfterTransaction = false; - removeStartingWindowAnimation(mStartingData.mPrepareRemoveAnimation); + final StartingData lastData = mStartingData; + lastData.mWaitForSyncTransactionCommit = false; + if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_REMOVE_DIRECTLY) { + removeStartingWindowAnimation(lastData.mPrepareRemoveAnimation); + } else if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_COPY_TO_CLIENT) { + removeStartingWindow(); } + lastData.mRemoveAfterTransaction = AFTER_TRANSACTION_IDLE; } void removeStartingWindowAnimation(boolean prepareAnimation) { @@ -2881,7 +2892,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mStartingData != null) { if (mStartingData.mWaitForSyncTransactionCommit || mTransitionController.inCollectingTransition(startingWindow)) { - mStartingData.mRemoveAfterTransaction = true; + mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY; mStartingData.mPrepareRemoveAnimation = prepareAnimation; return; } diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index 105b2bb09cf8..148bf9bfdcd9 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -16,16 +16,14 @@ package com.android.server.wm; -import static com.android.server.wm.SnapshotController.ACTIVITY_CLOSE; -import static com.android.server.wm.SnapshotController.ACTIVITY_OPEN; -import static com.android.server.wm.SnapshotController.TASK_CLOSE; -import static com.android.server.wm.SnapshotController.TASK_OPEN; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.os.Environment; import android.os.SystemProperties; +import android.os.Trace; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -35,7 +33,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; -import com.android.server.wm.SnapshotController.TransitionState; import java.io.File; import java.util.ArrayList; @@ -62,12 +59,6 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord static final String SNAPSHOTS_DIRNAME = "activity_snapshots"; /** - * The pending activities which should capture snapshot when process transition finish. - */ - @VisibleForTesting - final ArraySet<ActivityRecord> mPendingCaptureActivity = new ArraySet<>(); - - /** * The pending activities which should remove snapshot from memory when process transition * finish. */ @@ -86,6 +77,10 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord @VisibleForTesting final ArraySet<ActivityRecord> mPendingLoadActivity = new ArraySet<>(); + private final ArraySet<ActivityRecord> mOnBackPressedActivities = new ArraySet<>(); + + private final ArrayList<ActivityRecord> mTmpBelowActivities = new ArrayList<>(); + private final ArrayList<WindowContainer> mTmpTransitionParticipants = new ArrayList<>(); private final SnapshotPersistQueue mSnapshotPersistQueue; private final PersistInfoProvider mPersistInfoProvider; private final AppSnapshotLoader mSnapshotLoader; @@ -117,20 +112,6 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord setSnapshotEnabled(snapshotEnabled); } - void systemReady() { - if (shouldDisableSnapshots()) { - return; - } - mService.mSnapshotController.registerTransitionStateConsumer( - ACTIVITY_OPEN, this::handleOpenActivityTransition); - mService.mSnapshotController.registerTransitionStateConsumer( - ACTIVITY_CLOSE, this::handleCloseActivityTransition); - mService.mSnapshotController.registerTransitionStateConsumer( - TASK_OPEN, this::handleOpenTaskTransition); - mService.mSnapshotController.registerTransitionStateConsumer( - TASK_CLOSE, this::handleCloseTaskTransition); - } - @Override protected float initSnapshotScale() { final float config = mService.mContext.getResources().getFloat( @@ -173,6 +154,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord @Override void write() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cleanUpUserFiles"); final File file = mPersistInfoProvider.getDirectory(userId); if (file.exists()) { final File[] contents = file.listFiles(); @@ -182,15 +164,30 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } } } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }); } } + void addOnBackPressedActivity(ActivityRecord ar) { + if (shouldDisableSnapshots()) { + return; + } + mOnBackPressedActivities.add(ar); + } + + void clearOnBackPressedActivities() { + if (shouldDisableSnapshots()) { + return; + } + mOnBackPressedActivities.clear(); + } + /** - * Prepare to handle on transition start. Clear all temporary fields. + * Prepare to collect any change for snapshots processing. Clear all temporary fields. */ - void preTransitionStart() { + void beginSnapshotProcess() { if (shouldDisableSnapshots()) { return; } @@ -198,18 +195,22 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } /** - * on transition start has notified, start process data. + * End collect any change for snapshots processing, start process data. */ - void postTransitionStart() { + void endSnapshotProcess() { if (shouldDisableSnapshots()) { return; } - onCommitTransition(); + for (int i = mOnBackPressedActivities.size() - 1; i >= 0; --i) { + handleActivityTransition(mOnBackPressedActivities.valueAt(i)); + } + mOnBackPressedActivities.clear(); + mTmpTransitionParticipants.clear(); + postProcess(); } @VisibleForTesting void resetTmpFields() { - mPendingCaptureActivity.clear(); mPendingRemoveActivity.clear(); mPendingDeleteActivity.clear(); mPendingLoadActivity.clear(); @@ -218,31 +219,13 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord /** * Start process all pending activities for a transition. */ - private void onCommitTransition() { + private void postProcess() { if (DEBUG) { - Slog.d(TAG, "ActivitySnapshotController#onCommitTransition result:" - + " capture " + mPendingCaptureActivity + Slog.d(TAG, "ActivitySnapshotController#postProcess result:" + " remove " + mPendingRemoveActivity + " delete " + mPendingDeleteActivity + " load " + mPendingLoadActivity); } - // task snapshots - for (int i = mPendingCaptureActivity.size() - 1; i >= 0; i--) { - recordSnapshot(mPendingCaptureActivity.valueAt(i)); - } - // clear mTmpRemoveActivity from cache - for (int i = mPendingRemoveActivity.size() - 1; i >= 0; i--) { - final ActivityRecord ar = mPendingRemoveActivity.valueAt(i); - final int code = getSystemHashCode(ar); - mCache.onIdRemoved(code); - } - // clear snapshot on cache and delete files - for (int i = mPendingDeleteActivity.size() - 1; i >= 0; i--) { - final ActivityRecord ar = mPendingDeleteActivity.valueAt(i); - final int code = getSystemHashCode(ar); - mCache.onIdRemoved(code); - removeIfUserSavedFileExist(code, ar.mUserId); - } // load snapshot to cache for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) { final ActivityRecord ar = mPendingLoadActivity.valueAt(i); @@ -258,6 +241,8 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) { @Override void write() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "load_activity_snapshot"); final TaskSnapshot snapshot = mSnapshotLoader.loadTask(code, userId, false /* loadLowResolutionBitmap */); synchronized (mService.getWindowManagerLock()) { @@ -265,16 +250,36 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord mCache.putSnapshot(ar, snapshot); } } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }); } } } + // clear mTmpRemoveActivity from cache + for (int i = mPendingRemoveActivity.size() - 1; i >= 0; i--) { + final ActivityRecord ar = mPendingRemoveActivity.valueAt(i); + final int code = getSystemHashCode(ar); + mCache.onIdRemoved(code); + } + // clear snapshot on cache and delete files + for (int i = mPendingDeleteActivity.size() - 1; i >= 0; i--) { + final ActivityRecord ar = mPendingDeleteActivity.valueAt(i); + final int code = getSystemHashCode(ar); + mCache.onIdRemoved(code); + removeIfUserSavedFileExist(code, ar.mUserId); + } // don't keep any reference resetTmpFields(); } - private void recordSnapshot(ActivityRecord activity) { + void recordSnapshot(ActivityRecord activity) { + if (shouldDisableSnapshots()) { + return; + } + if (DEBUG) { + Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity); + } final TaskSnapshot snapshot = recordSnapshotInner(activity, false /* allowSnapshotHome */); if (snapshot != null) { final int code = getSystemHashCode(activity); @@ -285,15 +290,20 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord /** * Called when the visibility of an app changes outside the regular app transition flow. */ - void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) { + void notifyAppVisibilityChanged(ActivityRecord ar, boolean visible) { if (shouldDisableSnapshots()) { return; } + final Task task = ar.getTask(); + if (task == null) { + return; + } + // Doesn't need to capture activity snapshot when it converts from translucent. if (!visible) { resetTmpFields(); - addBelowTopActivityIfExist(appWindowToken.getTask(), mPendingRemoveActivity, + addBelowActivityIfExist(ar, mPendingRemoveActivity, false, "remove-snapshot"); - onCommitTransition(); + postProcess(); } } @@ -301,65 +311,146 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord return System.identityHashCode(activity); } - void handleOpenActivityTransition(TransitionState<ActivityRecord> transitionState) { - ArraySet<ActivityRecord> participant = transitionState.getParticipant(false /* open */); - for (ActivityRecord ar : participant) { - mPendingCaptureActivity.add(ar); - // remove the snapshot for the one below close - final ActivityRecord below = ar.getTask().getActivityBelow(ar); - if (below != null) { - mPendingRemoveActivity.add(below); + @VisibleForTesting + void handleTransitionFinish(@NonNull ArrayList<WindowContainer> windows) { + mTmpTransitionParticipants.clear(); + mTmpTransitionParticipants.addAll(windows); + for (int i = mTmpTransitionParticipants.size() - 1; i >= 0; --i) { + final WindowContainer next = mTmpTransitionParticipants.get(i); + if (next.asTask() != null) { + handleTaskTransition(next.asTask()); + } else if (next.asTaskFragment() != null) { + final TaskFragment tf = next.asTaskFragment(); + final ActivityRecord ar = tf.getTopMostActivity(); + if (ar != null) { + handleActivityTransition(ar); + } + } else if (next.asActivityRecord() != null) { + handleActivityTransition(next.asActivityRecord()); } } } - void handleCloseActivityTransition(TransitionState<ActivityRecord> transitionState) { - ArraySet<ActivityRecord> participant = transitionState.getParticipant(true /* open */); - for (ActivityRecord ar : participant) { + private void handleActivityTransition(@NonNull ActivityRecord ar) { + if (shouldDisableSnapshots()) { + return; + } + if (ar.isVisibleRequested()) { mPendingDeleteActivity.add(ar); // load next one if exists. - final ActivityRecord below = ar.getTask().getActivityBelow(ar); - if (below != null) { - mPendingLoadActivity.add(below); - } + addBelowActivityIfExist(ar, mPendingLoadActivity, true, "load-snapshot"); + } else { + // remove the snapshot for the one below close + addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot"); } } - void handleCloseTaskTransition(TransitionState<Task> closeTaskTransitionRecord) { - ArraySet<Task> participant = closeTaskTransitionRecord.getParticipant(false /* open */); - for (Task close : participant) { + private void handleTaskTransition(Task task) { + if (shouldDisableSnapshots()) { + return; + } + final ActivityRecord topActivity = task.getTopMostActivity(); + if (topActivity == null) { + return; + } + if (task.isVisibleRequested()) { + // this is open task transition + // load the N - 1 to cache + addBelowActivityIfExist(topActivity, mPendingLoadActivity, true, "load-snapshot"); + // Move the activities to top of mSavedFilesInOrder, so when purge happen, there + // will trim the persisted files from the most non-accessed. + adjustSavedFileOrder(task); + } else { // this is close task transition // remove the N - 1 from cache - addBelowTopActivityIfExist(close, mPendingRemoveActivity, "remove-snapshot"); + addBelowActivityIfExist(topActivity, mPendingRemoveActivity, true, "remove-snapshot"); } } - void handleOpenTaskTransition(TransitionState<Task> openTaskTransitionRecord) { - ArraySet<Task> participant = openTaskTransitionRecord.getParticipant(true /* open */); - for (Task open : participant) { - // this is close task transition - // remove the N - 1 from cache - addBelowTopActivityIfExist(open, mPendingLoadActivity, "load-snapshot"); - // Move the activities to top of mSavedFilesInOrder, so when purge happen, there - // will trim the persisted files from the most non-accessed. - adjustSavedFileOrder(open); + /** + * Add the top -1 activity to a set if it exists. + * @param inTransition true if the activity must participant in transition. + */ + private void addBelowActivityIfExist(ActivityRecord currentActivity, + ArraySet<ActivityRecord> set, boolean inTransition, String debugMessage) { + getActivityBelow(currentActivity, inTransition, mTmpBelowActivities); + for (int i = mTmpBelowActivities.size() - 1; i >= 0; --i) { + set.add(mTmpBelowActivities.get(i)); + if (DEBUG) { + Slog.d(TAG, "ActivitySnapshotController#addBelowTopActivityIfExist " + + mTmpBelowActivities.get(i) + " from " + debugMessage); + } } + mTmpBelowActivities.clear(); } - // Add the top -1 activity to a set if it exists. - private void addBelowTopActivityIfExist(Task task, ArraySet<ActivityRecord> set, - String debugMessage) { - final ActivityRecord topActivity = task.getTopMostActivity(); - if (topActivity != null) { - final ActivityRecord below = task.getActivityBelow(topActivity); - if (below != null) { - set.add(below); - if (DEBUG) { - Slog.d(TAG, "ActivitySnapshotController#addBelowTopActivityIfExist " - + below + " from " + debugMessage); + private void getActivityBelow(ActivityRecord currentActivity, boolean inTransition, + ArrayList<ActivityRecord> result) { + final Task currentTask = currentActivity.getTask(); + if (currentTask == null) { + return; + } + final ActivityRecord initPrev = currentTask.getActivityBelow(currentActivity); + if (initPrev == null) { + return; + } + final TaskFragment currTF = currentActivity.getTaskFragment(); + final TaskFragment prevTF = initPrev.getTaskFragment(); + final TaskFragment prevAdjacentTF = prevTF != null + ? prevTF.getAdjacentTaskFragment() : null; + if (currTF == prevTF && currTF != null || prevAdjacentTF == null) { + // Current activity and previous one is in the same task fragment, or + // previous activity is not in a task fragment, or + // previous activity's task fragment doesn't adjacent to any others. + if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) { + result.add(initPrev); + } + return; + } + + if (prevAdjacentTF == currTF) { + // previous activity A is adjacent to current activity B. + // Try to find anyone below previous activityA, which are C and D if exists. + // A | B + // C (| D) + getActivityBelow(initPrev, inTransition, result); + } else { + // previous activity C isn't adjacent to current activity A. + // A + // B | C + final Task prevAdjacentTask = prevAdjacentTF.getTask(); + if (prevAdjacentTask == currentTask) { + final int currentIndex = currTF != null + ? currentTask.mChildren.indexOf(currTF) + : currentTask.mChildren.indexOf(currentActivity); + final int prevAdjacentIndex = + prevAdjacentTask.mChildren.indexOf(prevAdjacentTF); + // prevAdjacentTF already above currentActivity + if (prevAdjacentIndex > currentIndex) { + return; } } + if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) { + result.add(initPrev); + } + // prevAdjacentTF is adjacent to another one + final ActivityRecord prevAdjacentActivity = prevAdjacentTF.getTopMostActivity(); + if (prevAdjacentActivity != null && (!inTransition + || isInParticipant(prevAdjacentActivity, mTmpTransitionParticipants))) { + result.add(prevAdjacentActivity); + } + } + } + + static boolean isInParticipant(ActivityRecord ar, + ArrayList<WindowContainer> transitionParticipants) { + for (int i = transitionParticipants.size() - 1; i >= 0; --i) { + final WindowContainer wc = transitionParticipants.get(i); + if (ar == wc || ar.isDescendantOf(wc)) { + return true; + } } + return false; } private void adjustSavedFileOrder(Task nextTopTask) { @@ -376,6 +467,9 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord @Override void onAppRemoved(ActivityRecord activity) { + if (shouldDisableSnapshots()) { + return; + } super.onAppRemoved(activity); final int code = getSystemHashCode(activity); removeIfUserSavedFileExist(code, activity.mUserId); @@ -386,6 +480,9 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord @Override void onAppDied(ActivityRecord activity) { + if (shouldDisableSnapshots()) { + return; + } super.onAppDied(activity); final int code = getSystemHashCode(activity); removeIfUserSavedFileExist(code, activity.mUserId); @@ -440,7 +537,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord private void removeIfUserSavedFileExist(int code, int userId) { final UserSavedFile usf = getUserFiles(userId).get(code); if (usf != null) { - mUserSavedFiles.remove(code); + mUserSavedFiles.get(userId).remove(code); mSavedFilesInOrder.remove(usf); mPersister.removeSnap(code, userId); } @@ -490,11 +587,13 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) { @Override void write() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activity_remove_files"); for (int i = files.size() - 1; i >= 0; --i) { final UserSavedFile usf = files.get(i); mSnapshotPersistQueue.deleteSnapshot( usf.mFileId, usf.mUserId, mPersistInfoProvider); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 6c848d18a007..fb6241200145 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2606,9 +2606,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final int callingUid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - // When a task is locked, dismiss the root pinned task if it exists - mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); - getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid); } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 976641b52a16..993c01672b44 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1177,6 +1177,8 @@ class BackNavigationController { if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) { return null; } + mCloseTarget.mTransitionController.mSnapshotController + .mActivitySnapshotController.clearOnBackPressedActivities(); applyPreviewStrategy(mOpenAdaptor, openActivity); final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(); @@ -1222,6 +1224,8 @@ class BackNavigationController { // Call it again to make sure the activity could be visible while handling the pending // animation. activity.commitVisibility(true, true); + activity.mTransitionController.mSnapshotController + .mActivitySnapshotController.addOnBackPressedActivity(activity); } activity.mLaunchTaskBehind = true; @@ -1248,6 +1252,9 @@ class BackNavigationController { // Restore the launch-behind state. activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); activity.mLaunchTaskBehind = false; + // Ignore all change + activity.mTransitionController.mSnapshotController + .mActivitySnapshotController.clearOnBackPressedActivities(); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to false. Activity=%s", activity); diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index ba242ecd0ec3..01786becda61 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -969,8 +969,10 @@ final class LetterboxUiController { final Rect innerFrame = hasInheritedLetterboxBehavior() ? mActivityRecord.getBounds() : w.getFrame(); mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint); - // We need to notify Shell that letterbox position has changed. - mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */); + if (mDoubleTapEvent) { + // We need to notify Shell that letterbox position has changed. + mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */); + } } else if (mLetterbox != null) { mLetterbox.hide(); } @@ -1242,6 +1244,7 @@ final class LetterboxUiController { ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER; logLetterboxPositionChange(changeToLog); + mDoubleTapEvent = true; } else if (mLetterbox.getInnerFrame().right < x) { // Moving to the next stop on the right side of the app window: left > center > right. mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextRightStop( @@ -1252,8 +1255,8 @@ final class LetterboxUiController { ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER; logLetterboxPositionChange(changeToLog); + mDoubleTapEvent = true; } - mDoubleTapEvent = true; // TODO(197549949): Add animation for transition. mActivityRecord.recomputeConfiguration(); } @@ -1281,6 +1284,7 @@ final class LetterboxUiController { ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER; logLetterboxPositionChange(changeToLog); + mDoubleTapEvent = true; } else if (mLetterbox.getInnerFrame().bottom < y) { // Moving to the next stop on the bottom side of the app window: top > center > bottom. mLetterboxConfiguration.movePositionForVerticalReachabilityToNextBottomStop( @@ -1291,8 +1295,8 @@ final class LetterboxUiController { ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER; logLetterboxPositionChange(changeToLog); + mDoubleTapEvent = true; } - mDoubleTapEvent = true; // TODO(197549949): Add animation for transition. mActivityRecord.recomputeConfiguration(); } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 0c98fb5000d5..0f9998cafc4e 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Context.DEVICE_POLICY_SERVICE; import static android.content.Context.STATUS_BAR_SERVICE; @@ -669,6 +670,9 @@ public class LockTaskController { } } + // When a task is locked, dismiss the root pinned task if it exists + mSupervisor.mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); + // System can only initiate screen pinning, not full lock task mode ProtoLog.w(WM_DEBUG_LOCKTASK, "%s", isSystemCaller ? "Locking pinned" : "Locking fully"); setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED, diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS index 0af9fe930d83..26abe51c8c34 100644 --- a/services/core/java/com/android/server/wm/OWNERS +++ b/services/core/java/com/android/server/wm/OWNERS @@ -18,4 +18,4 @@ rgl@google.com yunfanc@google.com per-file BackgroundActivityStartController.java = set noparent -per-file BackgroundActivityStartController.java = brufino@google.com, ogunwale@google.com, louischang@google.com, lus@google.com, rickywai@google.com +per-file BackgroundActivityStartController.java = brufino@google.com, ogunwale@google.com, louischang@google.com, lus@google.com diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index badcfa9d6964..37f9730d5443 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -16,185 +16,36 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; -import android.annotation.IntDef; -import android.util.ArraySet; -import android.util.Slog; -import android.util.SparseArray; +import android.os.Trace; import android.view.WindowManager; -import com.android.internal.annotations.VisibleForTesting; - import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.function.Consumer; /** * Integrates common functionality from TaskSnapshotController and ActivitySnapshotController. */ class SnapshotController { - private static final boolean DEBUG = false; - private static final String TAG = AbsAppSnapshotController.TAG; - - static final int ACTIVITY_OPEN = 1; - static final int ACTIVITY_CLOSE = 2; - static final int TASK_OPEN = 4; - static final int TASK_CLOSE = 8; - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = {ACTIVITY_OPEN, - ACTIVITY_CLOSE, - TASK_OPEN, - TASK_CLOSE}) - @interface TransitionStateType {} - private final SnapshotPersistQueue mSnapshotPersistQueue; final TaskSnapshotController mTaskSnapshotController; final ActivitySnapshotController mActivitySnapshotController; - private final ArraySet<Task> mTmpCloseTasks = new ArraySet<>(); - private final ArraySet<Task> mTmpOpenTasks = new ArraySet<>(); - - private final SparseArray<TransitionState> mTmpOpenCloseRecord = new SparseArray<>(); - private final ArraySet<Integer> mTmpAnalysisRecord = new ArraySet<>(); - private final SparseArray<ArrayList<Consumer<TransitionState>>> mTransitionStateConsumer = - new SparseArray<>(); - private int mActivatedType; - - private final ActivityOrderCheck mActivityOrderCheck = new ActivityOrderCheck(); - private final ActivityOrderCheck.AnalysisResult mResultHandler = (type, close, open) -> { - addTransitionRecord(type, true/* open */, open); - addTransitionRecord(type, false/* open */, close); - }; - - private static class ActivityOrderCheck { - private ActivityRecord mOpenActivity; - private ActivityRecord mCloseActivity; - private int mOpenIndex = -1; - private int mCloseIndex = -1; - - private void reset() { - mOpenActivity = null; - mCloseActivity = null; - mOpenIndex = -1; - mCloseIndex = -1; - } - - private void setTarget(boolean open, ActivityRecord ar, int index) { - if (open) { - mOpenActivity = ar; - mOpenIndex = index; - } else { - mCloseActivity = ar; - mCloseIndex = index; - } - } - - void analysisOrder(ArraySet<ActivityRecord> closeApps, - ArraySet<ActivityRecord> openApps, Task task, AnalysisResult result) { - for (int j = closeApps.size() - 1; j >= 0; j--) { - final ActivityRecord ar = closeApps.valueAt(j); - if (ar.getTask() == task) { - setTarget(false, ar, task.mChildren.indexOf(ar)); - break; - } - } - for (int j = openApps.size() - 1; j >= 0; j--) { - final ActivityRecord ar = openApps.valueAt(j); - if (ar.getTask() == task) { - setTarget(true, ar, task.mChildren.indexOf(ar)); - break; - } - } - if (mOpenIndex > mCloseIndex && mCloseIndex != -1) { - result.onCheckResult(ACTIVITY_OPEN, mCloseActivity, mOpenActivity); - } else if (mOpenIndex < mCloseIndex && mOpenIndex != -1) { - result.onCheckResult(ACTIVITY_CLOSE, mCloseActivity, mOpenActivity); - } - reset(); - } - private interface AnalysisResult { - void onCheckResult(@TransitionStateType int type, - ActivityRecord close, ActivityRecord open); - } - } - - private void addTransitionRecord(int type, boolean open, WindowContainer target) { - TransitionState record = mTmpOpenCloseRecord.get(type); - if (record == null) { - record = new TransitionState(); - mTmpOpenCloseRecord.set(type, record); - } - record.addParticipant(target, open); - mTmpAnalysisRecord.add(type); - } - - private void clearRecord() { - mTmpOpenCloseRecord.clear(); - mTmpAnalysisRecord.clear(); - } - - static class TransitionState<TYPE extends WindowContainer> { - private final ArraySet<TYPE> mOpenParticipant = new ArraySet<>(); - private final ArraySet<TYPE> mCloseParticipant = new ArraySet<>(); - - void addParticipant(TYPE target, boolean open) { - final ArraySet<TYPE> participant = open - ? mOpenParticipant : mCloseParticipant; - participant.add(target); - } - - ArraySet<TYPE> getParticipant(boolean open) { - return open ? mOpenParticipant : mCloseParticipant; - } - } - SnapshotController(WindowManagerService wms) { mSnapshotPersistQueue = new SnapshotPersistQueue(); mTaskSnapshotController = new TaskSnapshotController(wms, mSnapshotPersistQueue); mActivitySnapshotController = new ActivitySnapshotController(wms, mSnapshotPersistQueue); } - void registerTransitionStateConsumer(@TransitionStateType int type, - Consumer<TransitionState> consumer) { - ArrayList<Consumer<TransitionState>> consumers = mTransitionStateConsumer.get(type); - if (consumers == null) { - consumers = new ArrayList<>(); - mTransitionStateConsumer.set(type, consumers); - } - if (!consumers.contains(consumer)) { - consumers.add(consumer); - } - mActivatedType |= type; - } - - void unregisterTransitionStateConsumer(int type, Consumer<TransitionState> consumer) { - final ArrayList<Consumer<TransitionState>> consumers = mTransitionStateConsumer.get(type); - if (consumers == null) { - return; - } - consumers.remove(consumer); - if (consumers.size() == 0) { - mActivatedType &= ~type; - } - } - - private boolean hasTransitionStateConsumer(@TransitionStateType int type) { - return (mActivatedType & type) != 0; - } - void systemReady() { mSnapshotPersistQueue.systemReady(); - mTaskSnapshotController.systemReady(); - mActivitySnapshotController.systemReady(); } void setPause(boolean paused) { @@ -212,126 +63,76 @@ class SnapshotController { } void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) { - if (!visible && hasTransitionStateConsumer(TASK_CLOSE)) { - final Task task = appWindowToken.getTask(); - if (task == null || task.isVisibleRequested()) { - return; - } - // close task transition - addTransitionRecord(TASK_CLOSE, false /*open*/, task); - mActivitySnapshotController.preTransitionStart(); - notifyTransition(TASK_CLOSE); - mActivitySnapshotController.postTransitionStart(); - clearRecord(); - } + mActivitySnapshotController.notifyAppVisibilityChanged(appWindowToken, visible); } - // For legacy transition + // For legacy transition, which won't support activity snapshot void onTransitionStarting(DisplayContent displayContent) { - handleAppTransition(displayContent.mClosingApps, displayContent.mOpeningApps); + mTaskSnapshotController.handleClosingApps(displayContent.mClosingApps); } - // For shell transition, adapt to legacy transition. - void onTransitionReady(@WindowManager.TransitionType int type, - ArraySet<WindowContainer> participants) { + // For shell transition, record snapshots before transaction start. + void onTransactionReady(@WindowManager.TransitionType int type, + ArrayList<Transition.ChangeInfo> changeInfos) { final boolean isTransitionOpen = isTransitionOpen(type); final boolean isTransitionClose = isTransitionClose(type); - if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM - || (mActivatedType == 0)) { + if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM) { return; } - final ArraySet<ActivityRecord> openingApps = new ArraySet<>(); - final ArraySet<ActivityRecord> closingApps = new ArraySet<>(); - - for (int i = participants.size() - 1; i >= 0; --i) { - final ActivityRecord ar = participants.valueAt(i).asActivityRecord(); - if (ar == null || ar.getTask() == null) continue; - if (ar.isVisibleRequested()) { - openingApps.add(ar); - } else { - closingApps.add(ar); + for (int i = changeInfos.size() - 1; i >= 0; --i) { + Transition.ChangeInfo info = changeInfos.get(i); + // Intentionally skip record snapshot for changes originated from PiP. + if (info.mWindowingMode == WINDOWING_MODE_PINNED) continue; + if (info.mContainer.asTask() != null && !info.mContainer.isVisibleRequested()) { + mTaskSnapshotController.recordSnapshot(info.mContainer.asTask(), + false /* allowSnapshotHome */); + } + // Won't need to capture activity snapshot in close transition. + if (isTransitionClose) { + continue; + } + if (info.mContainer.asActivityRecord() != null + || info.mContainer.asTaskFragment() != null) { + final TaskFragment tf = info.mContainer.asTaskFragment(); + final ActivityRecord ar = tf != null ? tf.getTopMostActivity() + : info.mContainer.asActivityRecord(); + final boolean taskVis = ar != null && ar.getTask().isVisibleRequested(); + if (ar != null && !ar.isVisibleRequested() && taskVis) { + mActivitySnapshotController.recordSnapshot(ar); + } } } - handleAppTransition(closingApps, openingApps); } - private static boolean isTransitionOpen(int type) { - return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT; - } - private static boolean isTransitionClose(int type) { - return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK; - } - - @VisibleForTesting - void handleAppTransition(ArraySet<ActivityRecord> closingApps, - ArraySet<ActivityRecord> openApps) { - if (mActivatedType == 0) { + void onTransitionFinish(@WindowManager.TransitionType int type, + ArrayList<Transition.ChangeInfo> changeInfos) { + final boolean isTransitionOpen = isTransitionOpen(type); + final boolean isTransitionClose = isTransitionClose(type); + if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM + || (changeInfos.isEmpty())) { return; } - analysisTransition(closingApps, openApps); - mActivitySnapshotController.preTransitionStart(); - for (Integer transitionType : mTmpAnalysisRecord) { - notifyTransition(transitionType); - } - mActivitySnapshotController.postTransitionStart(); - clearRecord(); - } - - private void notifyTransition(int transitionType) { - final TransitionState record = mTmpOpenCloseRecord.get(transitionType); - final ArrayList<Consumer<TransitionState>> consumers = - mTransitionStateConsumer.get(transitionType); - for (Consumer<TransitionState> consumer : consumers) { - consumer.accept(record); - } - } - - private void analysisTransition(ArraySet<ActivityRecord> closingApps, - ArraySet<ActivityRecord> openingApps) { - getParticipantTasks(closingApps, mTmpCloseTasks, false /* isOpen */); - getParticipantTasks(openingApps, mTmpOpenTasks, true /* isOpen */); - if (DEBUG) { - Slog.d(TAG, "AppSnapshotController#analysisTransition participants" - + " mTmpCloseTasks " + mTmpCloseTasks - + " mTmpOpenTasks " + mTmpOpenTasks); - } - for (int i = mTmpCloseTasks.size() - 1; i >= 0; i--) { - final Task closeTask = mTmpCloseTasks.valueAt(i); - if (mTmpOpenTasks.contains(closeTask)) { - if (hasTransitionStateConsumer(ACTIVITY_OPEN) - || hasTransitionStateConsumer(ACTIVITY_CLOSE)) { - mActivityOrderCheck.analysisOrder(closingApps, openingApps, closeTask, - mResultHandler); - } - } else if (hasTransitionStateConsumer(TASK_CLOSE)) { - // close task transition - addTransitionRecord(TASK_CLOSE, false /*open*/, closeTask); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SnapshotController_analysis"); + mActivitySnapshotController.beginSnapshotProcess(); + final ArrayList<WindowContainer> windows = new ArrayList<>(); + for (int i = changeInfos.size() - 1; i >= 0; --i) { + final WindowContainer wc = changeInfos.get(i).mContainer; + if (wc.asTask() == null && wc.asTaskFragment() == null + && wc.asActivityRecord() == null) { + continue; } + windows.add(wc); } - if (hasTransitionStateConsumer(TASK_OPEN)) { - for (int i = mTmpOpenTasks.size() - 1; i >= 0; i--) { - final Task openTask = mTmpOpenTasks.valueAt(i); - if (!mTmpCloseTasks.contains(openTask)) { - // this is open task transition - addTransitionRecord(TASK_OPEN, true /*open*/, openTask); - } - } - } - mTmpCloseTasks.clear(); - mTmpOpenTasks.clear(); + mActivitySnapshotController.handleTransitionFinish(windows); + mActivitySnapshotController.endSnapshotProcess(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - private void getParticipantTasks(ArraySet<ActivityRecord> activityRecords, ArraySet<Task> tasks, - boolean isOpen) { - for (int i = activityRecords.size() - 1; i >= 0; i--) { - final ActivityRecord activity = activityRecords.valueAt(i); - final Task task = activity.getTask(); - if (task == null) continue; - - if (isOpen == activity.isVisibleRequested()) { - tasks.add(task); - } - } + private static boolean isTransitionOpen(int type) { + return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT; + } + private static boolean isTransitionClose(int type) { + return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK; } void dump(PrintWriter pw, String prefix) { diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java index 58e1c544202d..f4f641f35395 100644 --- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.graphics.Bitmap.CompressFormat.JPEG; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -26,6 +27,7 @@ import android.annotation.TestApi; import android.graphics.Bitmap; import android.os.Process; import android.os.SystemClock; +import android.os.Trace; import android.util.AtomicFile; import android.util.Slog; import android.window.TaskSnapshot; @@ -249,6 +251,7 @@ class SnapshotPersistQueue { @Override void write() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem"); if (!mPersistInfoProvider.createDirectory(mUserId)) { Slog.e(TAG, "Unable to create snapshot directory for user dir=" + mPersistInfoProvider.getDirectory(mUserId)); @@ -263,6 +266,7 @@ class SnapshotPersistQueue { if (failed) { deleteSnapshot(mId, mUserId, mPersistInfoProvider); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } boolean writeProto() { @@ -373,7 +377,9 @@ class SnapshotPersistQueue { @Override void write() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DeleteWriteQueueItem"); deleteSnapshot(mId, mUserId, mPersistInfoProvider); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } } diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java index 34806bd023a0..a23547ef1d5b 100644 --- a/services/core/java/com/android/server/wm/StartingData.java +++ b/services/core/java/com/android/server/wm/StartingData.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import android.annotation.IntDef; + import com.android.server.wm.StartingSurfaceController.StartingSurface; /** @@ -23,6 +25,20 @@ import com.android.server.wm.StartingSurfaceController.StartingSurface; */ public abstract class StartingData { + /** Nothing need to do after transaction */ + static final int AFTER_TRANSACTION_IDLE = 0; + /** Remove the starting window directly after transaction done. */ + static final int AFTER_TRANSACTION_REMOVE_DIRECTLY = 1; + /** Do copy splash screen to client after transaction done. */ + static final int AFTER_TRANSACTION_COPY_TO_CLIENT = 2; + + @IntDef(prefix = { "AFTER_TRANSACTION" }, value = { + AFTER_TRANSACTION_IDLE, + AFTER_TRANSACTION_REMOVE_DIRECTLY, + AFTER_TRANSACTION_COPY_TO_CLIENT, + }) + @interface AfterTransaction {} + protected final WindowManagerService mService; protected final int mTypeParams; @@ -60,7 +76,7 @@ public abstract class StartingData { * This starting window should be removed after applying the start transaction of transition, * which ensures the app window has shown. */ - boolean mRemoveAfterTransaction; + @AfterTransaction int mRemoveAfterTransaction; /** Whether to prepare the removal animation. */ boolean mPrepareRemoveAnimation; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c99291db1ea3..43430dd1eed0 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2878,8 +2878,8 @@ class Task extends TaskFragment { // No need to check if allowed if it's leaving dragResize if (dragResizing && !(getRootTask().getWindowingMode() == WINDOWING_MODE_FREEFORM)) { - throw new IllegalArgumentException("Drag resize not allow for root task id=" - + getRootTaskId()); + Slog.e(TAG, "Drag resize isn't allowed for root task id=" + getRootTaskId()); + return; } mDragResizing = dragResizing; resetDragResizingChangeReported(); @@ -3459,6 +3459,8 @@ class Task extends TaskFragment { info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET; info.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET; info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET; + info.isUserFullscreenOverrideEnabled = top != null + && top.mLetterboxUiController.shouldApplyUserFullscreenOverride(); info.isFromLetterboxDoubleTap = top != null && top.mLetterboxUiController.isFromDoubleTap(); if (info.isLetterboxDoubleTapEnabled) { info.topActivityLetterboxWidth = top.getBounds().width(); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index d8cc8d386424..6dc896a92e52 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1175,7 +1175,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, - boolean deferPause) { + boolean skipPause) { ActivityRecord next = topRunningActivity(true /* focusableOnly */); if (next == null || !next.canResumeByCompat()) { return false; @@ -1183,11 +1183,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { next.delayedResume = false; - // If we are currently pausing an activity, then don't do anything until that is done. - final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete(); - if (!allPausedComplete) { - ProtoLog.v(WM_DEBUG_STATES, - "resumeTopActivity: Skip resume: some activity pausing."); + if (!skipPause && !mRootWindowContainer.allPausedActivitiesComplete()) { + // If we aren't skipping pause, then we have to wait for currently pausing activities. + ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: some activity pausing."); return false; } @@ -1251,7 +1249,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { lastResumed = lastFocusedRootTask.getTopResumedActivity(); } - boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next); + boolean pausing = !skipPause && taskDisplayArea.pauseBackTasks(next); if (mResumedActivity != null) { ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity); pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */, @@ -1329,7 +1327,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { .notifyBeforePackageUnstopped(next.packageName); mAtmService.getPackageManagerInternalLocked().notifyComponentUsed( next.packageName, next.mUserId, - next.packageName); /* TODO: Verify if correct userid */ + next.packageName, next.toString()); /* TODO: Verify if correct userid */ } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " + next.packageName + ": " + e); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index c747c09a6872..4eb42908c6d4 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static com.android.server.wm.SnapshotController.TASK_CLOSE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -77,13 +76,6 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot setSnapshotEnabled(snapshotEnabled); } - void systemReady() { - if (!shouldDisableSnapshots()) { - mService.mSnapshotController.registerTransitionStateConsumer(TASK_CLOSE, - this::handleTaskClose); - } - } - static PersistInfoProvider createPersistInfoProvider(WindowManagerService service, BaseAppSnapshotPersister.DirectoryResolver resolver) { final float highResTaskSnapshotScale = service.mContext.getResources().getFloat( @@ -116,20 +108,23 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot enableLowResSnapshots, lowResScaleFactor, use16BitFormat); } - void handleTaskClose(SnapshotController.TransitionState<Task> closeTaskTransitionRecord) { + // Still needed for legacy transition.(AppTransitionControllerTest) + void handleClosingApps(ArraySet<ActivityRecord> closingApps) { if (shouldDisableSnapshots()) { return; } + // We need to take a snapshot of the task if and only if all activities of the task are + // either closing or hidden. mTmpTasks.clear(); - final ArraySet<Task> tasks = closeTaskTransitionRecord.getParticipant(false /* open */); - if (mService.mAtmService.getTransitionController().isShellTransitionsEnabled()) { - mTmpTasks.addAll(tasks); - } else { - for (Task task : tasks) { - getClosingTasksInner(task, mTmpTasks); - } + for (int i = closingApps.size() - 1; i >= 0; i--) { + final ActivityRecord activity = closingApps.valueAt(i); + final Task task = activity.getTask(); + if (task == null) continue; + + getClosingTasksInner(task, mTmpTasks); } snapshotTasks(mTmpTasks); + mTmpTasks.clear(); mSkipClosingAppSnapshotTasks.clear(); } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index cd1511914d34..3e8c0177b3b3 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; + +import android.os.Trace; import android.util.ArraySet; import android.window.TaskSnapshot; @@ -102,6 +105,7 @@ class TaskSnapshotPersister extends BaseAppSnapshotPersister { @Override void write() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RemoveObsoleteFilesQueueItem"); final ArraySet<Integer> newPersistedTaskIds; synchronized (mLock) { newPersistedTaskIds = new ArraySet<>(mPersistedTaskIdsSinceLastRemoveObsolete); @@ -120,6 +124,7 @@ class TaskSnapshotPersister extends BaseAppSnapshotPersister { } } } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index aad17aa16c7f..1566bb2c8610 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1191,8 +1191,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { " Skipping post-transition snapshot for task %d", task.mTaskId); } - snapController.mActivitySnapshotController - .notifyAppVisibilityChanged(ar, false /* visible */); } ar.commitVisibility(false /* visible */, false /* performLayout */, true /* fromTransition */); @@ -1389,6 +1387,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Handle back animation if it's already started. mController.mAtm.mBackNavigationController.onTransitionFinish(mTargets, this); mController.mFinishingTransition = null; + mController.mSnapshotController.onTransitionFinish(mType, mTargets); } void abort() { @@ -1593,16 +1592,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // transferred. If transition is transient, IME won't be moved during the transition and // the tasks are still live, so we take the snapshot at the end of the transition instead. if (mTransientLaunches == null) { - for (int i = mParticipants.size() - 1; i >= 0; --i) { - final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); - if (ar == null || ar.getTask() == null - || ar.getTask().isVisibleRequested()) continue; - final ChangeInfo change = mChanges.get(ar); - // Intentionally skip record snapshot for changes originated from PiP. - if (change != null && change.mWindowingMode == WINDOWING_MODE_PINNED) continue; - mController.mSnapshotController.mTaskSnapshotController.recordSnapshot( - ar.getTask(), false /* allowSnapshotHome */); - } + mController.mSnapshotController.onTransactionReady(mType, mTargets); } // This is non-null only if display has changes. It handles the visible windows that don't diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 6432ff081c73..6ede3456d74c 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -1124,14 +1124,15 @@ class TransitionController { + "track #%d", transition.getSyncId(), track); } } - if (sync) { + transition.mAnimationTrack = track; + info.setTrack(track); + mTrackCount = Math.max(mTrackCount, track + 1); + if (sync && mTrackCount > 1) { + // If there are >1 tracks, mark as sync so that all tracks finish. info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Marking #%d animation as SYNC.", transition.getSyncId()); } - transition.mAnimationTrack = track; - info.setTrack(track); - mTrackCount = Math.max(mTrackCount, track + 1); } void updateAnimatingState(SurfaceControl.Transaction t) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 261d6bc489b6..03efb1bf705e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4648,7 +4648,14 @@ public class WindowManagerService extends IWindowManager.Stub void reportSystemGestureExclusionChanged(Session session, IWindow window, List<Rect> exclusionRects) { synchronized (mGlobalLock) { - final WindowState win = windowForClientLocked(session, window, true); + final WindowState win = windowForClientLocked(session, window, + false /* throwOnError */); + if (win == null) { + Slog.i(TAG_WM, + "reportSystemGestureExclusionChanged(): No window state for package:" + + session.mPackageName); + return; + } if (win.setSystemGestureExclusion(exclusionRects)) { win.getDisplayContent().updateSystemGestureExclusion(); } @@ -4658,7 +4665,14 @@ public class WindowManagerService extends IWindowManager.Stub void reportKeepClearAreasChanged(Session session, IWindow window, List<Rect> restricted, List<Rect> unrestricted) { synchronized (mGlobalLock) { - final WindowState win = windowForClientLocked(session, window, true); + final WindowState win = windowForClientLocked(session, window, + false /* throwOnError */); + if (win == null) { + Slog.i(TAG_WM, + "reportKeepClearAreasChanged(): No window state for package:" + + session.mPackageName); + return; + } if (win.setKeepClearAreas(restricted, unrestricted)) { win.getDisplayContent().updateKeepClearAreas(); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 427ab7efed40..6d7e2970e2b1 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -1657,9 +1657,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { - final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment(); - final TaskFragment root2 = - WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment(); + final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer()); + if (wc1 == null || !wc1.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1); + return TRANSACT_EFFECTS_NONE; + } + final TaskFragment root1 = wc1.asTaskFragment(); + final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot()); + if (wc2 == null || !wc2.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2); + return TRANSACT_EFFECTS_NONE; + } + final TaskFragment root2 = wc2.asTaskFragment(); if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) { throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + " organizer root1=" + root1 + " root2=" + root2); @@ -1672,7 +1681,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int clearAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { - final TaskFragment root = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment(); + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc); + return TRANSACT_EFFECTS_NONE; + } + final TaskFragment root = wc.asTaskFragment(); if (!root.mCreatedByOrganizer) { throw new IllegalArgumentException("clearAdjacentRootsHierarchyOp: Not created by" + " organizer root=" + root); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 5a45fe11797e..a172d9912cbd 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -30,7 +30,6 @@ import static android.os.PowerManager.DRAW_WAKE_LOCK; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.SurfaceControl.Transaction; import static android.view.SurfaceControl.getGlobalTransaction; -import static android.view.ViewRootImpl.LOCAL_LAYOUT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; @@ -1446,9 +1445,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final boolean dragResizingChanged = !mDragResizingChangeReported && isDragResizeChanged(); - - final boolean attachedFrameChanged = LOCAL_LAYOUT - && mLayoutAttached && getParentWindow().frameChanged(); + final boolean attachedFrameChanged = mLayoutAttached && getParentWindow().frameChanged(); if (DEBUG) { Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp index ad098b757eae..4cd018b0269e 100644 --- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp +++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp @@ -144,12 +144,20 @@ static unique_fd openUinput(const char* readableName, jint vendorId, jint produc } uinput_abs_setup slotAbsSetup; slotAbsSetup.code = ABS_MT_SLOT; - slotAbsSetup.absinfo.maximum = MAX_POINTERS; + slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1; slotAbsSetup.absinfo.minimum = 0; if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) { ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno)); return invalidFd(); } + uinput_abs_setup trackingIdAbsSetup; + trackingIdAbsSetup.code = ABS_MT_TRACKING_ID; + trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1; + trackingIdAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno)); + return invalidFd(); + } } if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) { ALOGE("Error creating uinput device: %s", strerror(errno)); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java index 522ee341010b..e7855bc85061 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java @@ -79,14 +79,7 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { @Override public boolean isScreenCaptureAllowed(int userHandle) { - if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) { - return isScreenCaptureAllowedInPolicyEngine(userHandle); - } else { - synchronized (mLock) { - return mScreenCaptureDisallowedUser != UserHandle.USER_ALL - && mScreenCaptureDisallowedUser != userHandle; - } - } + return isScreenCaptureAllowedInPolicyEngine(userHandle); } private boolean isScreenCaptureAllowedInPolicyEngine(int userHandle) { @@ -182,11 +175,7 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { synchronized (mLock) { pw.println("Device policy cache:"); pw.increaseIndent(); - if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) { - pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers); - } else { - pw.println("Screen capture disallowed user: " + mScreenCaptureDisallowedUser); - } + pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers); pw.println("Password quality: " + mPasswordQuality); pw.println("Permission policy: " + mPermissionPolicy); pw.println("Admin can grant sensors permission: " + mCanGrantSensorsPermissions.get()); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c323a7f4430d..af1bac890ff1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -241,6 +241,7 @@ import static android.provider.Telephony.Carriers.ENFORCE_KEY; import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI; import static android.provider.Telephony.Carriers.INVALID_APN_ID; import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION; + import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -1190,9 +1191,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { maybeResumeDeviceWideLoggingLocked(); } } - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - mDevicePolicyEngine.handleUserRemoved(userHandle); - } + mDevicePolicyEngine.handleUserRemoved(userHandle); } else if (Intent.ACTION_USER_STARTED.equals(action)) { sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle); synchronized (getLockObject()) { @@ -1442,10 +1441,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { && (owner.getPackageName().equals(packageName))) { startOwnerService(userHandle, "package-broadcast"); } - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - mDevicePolicyEngine.handlePackageChanged( - packageName, userHandle, removedAdminPackage); - } + mDevicePolicyEngine.handlePackageChanged(packageName, userHandle, removedAdminPackage); // Persist updates if the removed package was an admin or delegate. if (removedAdmin || removedDelegate) { saveSettingsLocked(policy.mUserId); @@ -1453,7 +1449,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (removedAdmin) { // The removed admin might have disabled camera, so update user restrictions. - pushUserRestrictions(userHandle); pushMeteredDisabledPackages(userHandle); } } @@ -2144,9 +2139,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener()); mDeviceManagementResourcesProvider.load(); - if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) { - mDevicePolicyEngine.load(); - } + mDevicePolicyEngine.load(); mContactSystemRoleHolders = fetchOemSystemHolders(/* roleResIds...= */ com.android.internal.R.string.config_defaultSms, @@ -2278,11 +2271,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (parentAdmin != null) { parentAdmin.userRestrictions = null; } - pushUserRestrictions(userHandle); } mOwners.removeProfileOwner(userHandle); mOwners.writeProfileOwner(userHandle); - pushScreenCapturePolicy(userHandle); DevicePolicyData policy = mUserData.get(userHandle); if (policy != null) { @@ -2640,20 +2631,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ActiveAdmin profileOwner, boolean newOwner) { if (newOwner || mInjector.settingsSecureGetIntForUser( Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) != 0) { - if (isPolicyEngineForFinanceFlagEnabled()) { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.getPolicyDefinitionForUserRestriction( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES), - EnforcingAdmin.createEnterpriseEnforcingAdmin( - profileOwner.info.getComponent(), - profileOwner.getUserHandle().getIdentifier()), - new BooleanPolicyValue(true), - userId); - } else { - profileOwner.ensureUserRestrictions().putBoolean( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true); - saveUserRestrictionsLocked(userId); - } + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.getPolicyDefinitionForUserRestriction( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES), + EnforcingAdmin.createEnterpriseEnforcingAdmin( + profileOwner.info.getComponent(), + profileOwner.getUserHandle().getIdentifier()), + new BooleanPolicyValue(true), + userId); mInjector.settingsSecurePutIntForUser( Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId); } @@ -2668,41 +2653,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) { return; // The same set of default restrictions has been already applied. } - if (isPolicyEngineForFinanceFlagEnabled()) { - for (String restriction : defaultRestrictions) { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction), - EnforcingAdmin.createEnterpriseEnforcingAdmin( - admin.info.getComponent(), - admin.getUserHandle().getIdentifier()), - new BooleanPolicyValue(true), - userId); - } - admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions); - Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + - defaultRestrictions); - return; - } - - Slogf.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId); - - if (VERBOSE_LOG) { - Slogf.d(LOG_TAG, "Default enabled restrictions: " - + defaultRestrictions - + ". Restrictions already enabled: " - + admin.defaultEnabledRestrictionsAlreadySet); - } - - final Set<String> restrictionsToSet = new ArraySet<>(defaultRestrictions); - restrictionsToSet.removeAll(admin.defaultEnabledRestrictionsAlreadySet); - if (!restrictionsToSet.isEmpty()) { - for (final String restriction : restrictionsToSet) { - admin.ensureUserRestrictions().putBoolean(restriction, true); - } - admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet); - Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet); - saveUserRestrictionsLocked(userId); + for (String restriction : defaultRestrictions) { + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction), + EnforcingAdmin.createEnterpriseEnforcingAdmin( + admin.info.getComponent(), + admin.getUserHandle().getIdentifier()), + new BooleanPolicyValue(true), + userId); } + admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions); + Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + + defaultRestrictions); } private void setDeviceOwnershipSystemPropertyLocked() { @@ -2765,7 +2727,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Apply user restriction to parent active admin instead parent.ensureUserRestrictions().putBoolean( UserManager.DISALLOW_CONFIG_DATE_TIME, true); - pushUserRestrictions(userId); } } } @@ -3297,10 +3258,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.validatePasswordOwner(); updateMaximumTimeToLockLocked(userHandle); - if (!isPolicyEngineForFinanceFlagEnabled()) { - updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userHandle); - updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle); - } if (policy.mStatusBarDisabled) { setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle); } @@ -3470,9 +3427,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } revertTransferOwnershipIfNecessaryLocked(); - if (!isPolicyEngineForFinanceFlagEnabled()) { - updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked()); - } } // In case flag value has changed, we apply it during boot to avoid doing it concurrently @@ -3545,9 +3499,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { deleteTransferOwnershipBundleLocked(metadata.userId); } updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true); - if (!isPolicyEngineForFinanceFlagEnabled()) { - pushUserControlDisabledPackagesLocked(metadata.userId); - } } private void maybeLogStart() { @@ -3584,13 +3535,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } void handleStartUser(int userId) { - synchronized (getLockObject()) { - pushScreenCapturePolicy(userId); - if (!isPolicyEngineForFinanceFlagEnabled()) { - pushUserControlDisabledPackagesLocked(userId); - } - } - pushUserRestrictions(userId); // When system user is started (device boot), load cache for all users. // This is to mitigate the potential race between loading the cache and keyguard // reading the value during user switch, due to onStartUser() being asynchronous. @@ -3615,9 +3559,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } startOwnerService(userId, "start-user"); - if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) { - mDevicePolicyEngine.handleStartUser(userId); - } + mDevicePolicyEngine.handleStartUser(userId); } void pushUserControlDisabledPackagesLocked(int userId) { @@ -3642,9 +3584,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void handleUnlockUser(int userId) { startOwnerService(userId, "unlock-user"); - if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) { - mDevicePolicyEngine.handleUnlockUser(userId); - } + mDevicePolicyEngine.handleUnlockUser(userId); } void handleOnUserUnlocked(int userId) { @@ -3654,9 +3594,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void handleStopUser(int userId) { updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT)); mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user"); - if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) { - mDevicePolicyEngine.handleStopUser(userId); - } + mDevicePolicyEngine.handleStopUser(userId); } private void startOwnerService(int userId, String actionForLog) { @@ -3690,9 +3628,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } for (Integer userId : deletedUsers) { removeUserData(userId); - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - mDevicePolicyEngine.handleUserRemoved(userId); - } + mDevicePolicyEngine.handleUserRemoved(userId); } } @@ -3879,16 +3815,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver); final int oldAdminUid = adminToTransfer.getUid(); - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - EnforcingAdmin oldAdmin = - EnforcingAdmin.createEnterpriseEnforcingAdmin( - outgoingReceiver, userHandle, adminToTransfer); - EnforcingAdmin newAdmin = - EnforcingAdmin.createEnterpriseEnforcingAdmin( - incomingReceiver, userHandle, adminToTransfer); + EnforcingAdmin oldAdmin = + EnforcingAdmin.createEnterpriseEnforcingAdmin( + outgoingReceiver, userHandle, adminToTransfer); + EnforcingAdmin newAdmin = + EnforcingAdmin.createEnterpriseEnforcingAdmin( + incomingReceiver, userHandle, adminToTransfer); - mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin); - } + mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin); adminToTransfer.transfer(incomingDeviceInfo); policy.mAdminMap.remove(outgoingReceiver); @@ -4194,11 +4128,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mInjector.binderWithCleanCallingIdentity(() -> removeActiveAdminLocked(adminReceiver, userHandle)); } - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - mDevicePolicyEngine.removePoliciesForAdmin( - EnforcingAdmin.createEnterpriseEnforcingAdmin( - adminReceiver, userHandle, admin)); - } + mDevicePolicyEngine.removePoliciesForAdmin( + EnforcingAdmin.createEnterpriseEnforcingAdmin( + adminReceiver, userHandle, admin)); } private boolean canSetPasswordQualityOnParent(String packageName, final CallerIdentity caller) { @@ -7558,47 +7490,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) { return; } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(callerPackageName); - } else { - caller = getCallerIdentity(); - } - ActiveAdmin admin; + CallerIdentity caller = getCallerIdentity(callerPackageName); boolean calledByProfileOwnerOnOrgOwnedDevice = isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()); - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( - /*admin=*/ null, - /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, MASTER_CLEAR}, - USES_POLICY_WIPE_DATA, - caller.getPackageName(), - factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance)); - admin = enforcingAdmin.getActiveAdmin(); - } else { - if (calledOnParentInstance) { - Preconditions.checkCallAuthorization(calledByProfileOwnerOnOrgOwnedDevice, - "Wiping the entire device can only be done by a profile owner on " - + "organization-owned device."); - } - if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { - Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) - || calledByProfileOwnerOnOrgOwnedDevice - || isFinancedDeviceOwner(caller), - "Only device owners or profile owners of organization-owned device can set " - + "WIPE_RESET_PROTECTION_DATA"); - } - synchronized (getLockObject()) { - admin = getActiveAdminWithPolicyForUidLocked(/* who= */ null, - DeviceAdminInfo.USES_POLICY_WIPE_DATA, caller.getUid()); - } - Preconditions.checkCallAuthorization( - (admin != null) || hasCallingOrSelfPermission(permission.MASTER_CLEAR), - "No active admin for user %d and caller %d does not hold MASTER_CLEAR " - + "permission", - caller.getUserId(), caller.getUid()); - } + EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( + /*admin=*/ null, + /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, MASTER_CLEAR}, + USES_POLICY_WIPE_DATA, + caller.getPackageName(), + factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance)); + ActiveAdmin admin = enforcingAdmin.getActiveAdmin(); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA); @@ -8639,62 +8541,36 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return; } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackage); - } else { - Objects.requireNonNull(who, "ComponentName is null"); - caller = getCallerIdentity(who); - if (parent) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(caller)); - } else { - Preconditions.checkCallAuthorization(isProfileOwner(caller) - || isDefaultDeviceOwner(caller)); - } - } - - if (isPolicyEngineForFinanceFlagEnabled()) { - int callerUserId = Binder.getCallingUserHandle().getIdentifier(); - int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId; - EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( - who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(), - targetUserId); - if ((parent && isProfileOwnerOfOrganizationOwnedDevice(caller)) - || isDefaultDeviceOwner(caller)) { - if (disabled) { - mDevicePolicyEngine.setGlobalPolicy( - PolicyDefinition.SCREEN_CAPTURE_DISABLED, - admin, - new BooleanPolicyValue(disabled)); - } else { - mDevicePolicyEngine.removeGlobalPolicy( - PolicyDefinition.SCREEN_CAPTURE_DISABLED, - admin); - } + CallerIdentity caller = getCallerIdentity(who, callerPackage); + int callerUserId = Binder.getCallingUserHandle().getIdentifier(); + int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId; + EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( + who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(), + targetUserId); + if ((parent && isProfileOwnerOfOrganizationOwnedDevice(caller)) + || isDefaultDeviceOwner(caller)) { + if (disabled) { + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin, + new BooleanPolicyValue(disabled)); } else { - if (disabled) { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.SCREEN_CAPTURE_DISABLED, - admin, - new BooleanPolicyValue(disabled), - callerUserId); - } else { - mDevicePolicyEngine.removeLocalPolicy( - PolicyDefinition.SCREEN_CAPTURE_DISABLED, - admin, - callerUserId); - } + mDevicePolicyEngine.removeGlobalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin); } } else { - synchronized (getLockObject()) { - ActiveAdmin ap = getParentOfAdminIfRequired( - getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent); - if (ap.disableScreenCapture != disabled) { - ap.disableScreenCapture = disabled; - saveSettingsLocked(caller.getUserId()); - pushScreenCapturePolicy(caller.getUserId()); - } + if (disabled) { + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin, + new BooleanPolicyValue(disabled), + callerUserId); + } else { + mDevicePolicyEngine.removeLocalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin, + callerUserId); } } DevicePolicyEventLogger @@ -8704,42 +8580,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { .write(); } - // Push the screen capture policy for a given userId. If screen capture is disabled by the - // DO or COPE PO on the parent profile, then this takes precedence as screen capture will - // be disabled device-wide. - private void pushScreenCapturePolicy(int adminUserId) { - if (isPolicyEngineForFinanceFlagEnabled()) { - return; - } - // Update screen capture device-wide if disabled by the DO or COPE PO on the parent profile. - // TODO(b/261999445): remove - ActiveAdmin admin; - if (isHeadlessFlagEnabled()) { - admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked( - mUserManagerInternal.getProfileParentId(adminUserId)); - } else { - admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked( - UserHandle.USER_SYSTEM); - } - if (admin != null && admin.disableScreenCapture) { - setScreenCaptureDisabled(UserHandle.USER_ALL); - return; - } - // Otherwise, update screen capture only for the calling user. - admin = getProfileOwnerAdminLocked(adminUserId); - if (admin != null && admin.disableScreenCapture) { - setScreenCaptureDisabled(adminUserId); - return; - } - // If the admin is permission based, update only for the calling user. - admin = getUserData(adminUserId).createOrGetPermissionBasedAdmin(adminUserId); - if (admin != null && admin.disableScreenCapture) { - setScreenCaptureDisabled(adminUserId); - return; - } - setScreenCaptureDisabled(UserHandle.USER_NULL); - } - // Set the latest screen capture policy, overriding any existing ones. // userHandle can be one of USER_ALL, USER_NULL or a concrete userId. private void setScreenCaptureDisabled(int userHandle) { @@ -8766,14 +8606,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId())); } - if (isPolicyEngineForFinanceFlagEnabled()) { - Boolean disallowed = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.SCREEN_CAPTURE_DISABLED, - userHandle); - return disallowed != null && disallowed; - } else { - return !mPolicyCache.isScreenCaptureAllowed(userHandle); - } + Boolean disallowed = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + userHandle); + return disallowed != null && disallowed; } private void updateScreenCaptureDisabled() { @@ -8882,23 +8718,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()), "Managed profile cannot set auto time required"); - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin admin = getEnforcingAdminForCaller(who, who.getPackageName()); - setGlobalUserRestrictionInternal( - admin, UserManager.DISALLOW_CONFIG_DATE_TIME, required); - } else { - ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); - if (admin.requireAutoTime != required) { - admin.requireAutoTime = required; - saveSettingsLocked(caller.getUserId()); - requireAutoTimeChanged = true; - } - // requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so - // propagate updated restrictions to the framework. - if (requireAutoTimeChanged) { - pushUserRestrictions(caller.getUserId()); - } - } + EnforcingAdmin admin = getEnforcingAdminForCaller(who, who.getPackageName()); + setGlobalUserRestrictionInternal( + admin, UserManager.DISALLOW_CONFIG_DATE_TIME, required); } // Turn AUTO_TIME on in settings if it is required if (required) { @@ -8921,31 +8743,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - if (isPolicyEngineForFinanceFlagEnabled()) { - Boolean required = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.getPolicyDefinitionForUserRestriction( - UserManager.DISALLOW_CONFIG_DATE_TIME), - mInjector.binderGetCallingUserHandle().getIdentifier()); - return required != null && required; - } else { - synchronized (getLockObject()) { - ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - if (deviceOwner != null && deviceOwner.requireAutoTime) { - // If the device owner enforces auto time, we don't need to check the PO's - return true; - } - - // Now check to see if any profile owner on any user enforces auto time - for (Integer userId : mOwners.getProfileOwnerKeys()) { - ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); - if (profileOwner != null && profileOwner.requireAutoTime) { - return true; - } - } - - return false; - } - } + Boolean required = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.getPolicyDefinitionForUserRestriction( + UserManager.DISALLOW_CONFIG_DATE_TIME), + mInjector.binderGetCallingUserHandle().getIdentifier()); + return required != null && required; } /** @@ -9240,47 +9042,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return; } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); final int userId = caller.getUserId(); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_CAMERA_DISABLED); - - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( - who, - MANAGE_DEVICE_POLICY_CAMERA, - caller.getPackageName(), - getProfileParentUserIfRequested(userId, parent)); - try { - setBackwardCompatibleUserRestriction( - caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent); - } catch (IllegalStateException e) { - throw new IllegalStateException( - "Please use addUserRestriction or addUserRestrictionGlobally using the key" - + " UserManager.DISALLOW_CAMERA to disable the camera locally or" - + " globally, respectively"); - } - } else { - Objects.requireNonNull(who, "ComponentName is null"); - if (parent) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(caller)); - } - synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent); - if (admin.disableCamera != disabled) { - admin.disableCamera = disabled; - saveSettingsLocked(userId); - } - } - // Tell the user manager that the restrictions have changed. - pushUserRestrictions(userId); + EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + who, + MANAGE_DEVICE_POLICY_CAMERA, + caller.getPackageName(), + getProfileParentUserIfRequested(userId, parent)); + try { + setBackwardCompatibleUserRestriction( + caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent); + } catch (IllegalStateException e) { + throw new IllegalStateException( + "Please use addUserRestriction or addUserRestrictionGlobally using the key" + + " UserManager.DISALLOW_CAMERA to disable the camera locally or" + + " globally, respectively"); } final int affectedUserId = parent ? getProfileParentId(userId) : userId; @@ -9306,66 +9084,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } - if (isPolicyEngineForFinanceFlagEnabled()) { - Preconditions.checkCallAuthorization( - hasFullCrossUsersPermission(caller, userHandle) - || isCameraServerUid(caller) - || hasPermission(MANAGE_DEVICE_POLICY_CAMERA, - caller.getPackageName(), userHandle) - || hasPermission(QUERY_ADMIN_POLICY, caller.getPackageName())); - } else { - Preconditions.checkCallAuthorization( - hasFullCrossUsersPermission(caller, userHandle) || isCameraServerUid(caller)); - if (parent) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())); - } - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); + Preconditions.checkCallAuthorization( + hasFullCrossUsersPermission(caller, userHandle) + || isCameraServerUid(caller) + || hasPermission(MANAGE_DEVICE_POLICY_CAMERA, + caller.getPackageName(), userHandle) + || hasPermission(QUERY_ADMIN_POLICY, caller.getPackageName())); int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle; - - if (isPolicyEngineForFinanceFlagEnabled()) { - PolicyDefinition<Boolean> policy = - PolicyDefinition.getPolicyDefinitionForUserRestriction( - UserManager.DISALLOW_CAMERA); - if (who != null) { - EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName); - return Boolean.TRUE.equals( - mDevicePolicyEngine.getLocalPolicySetByAdmin( - policy, admin, affectedUserId)); - } else { - return Boolean.TRUE.equals( - mDevicePolicyEngine.getResolvedPolicy(policy, affectedUserId)); - } + PolicyDefinition<Boolean> policy = + PolicyDefinition.getPolicyDefinitionForUserRestriction( + UserManager.DISALLOW_CAMERA); + if (who != null) { + EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName); + return Boolean.TRUE.equals( + mDevicePolicyEngine.getLocalPolicySetByAdmin( + policy, admin, affectedUserId)); } else { - synchronized (getLockObject()) { - if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); - return (admin != null) && admin.disableCamera; - } - // First, see if DO has set it. If so, it's device-wide. - final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - if (deviceOwner != null && deviceOwner.disableCamera) { - return true; - } - - // Return the strictest policy across all participating admins. - List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(affectedUserId); - - // Determine whether or not the device camera is disabled for any active admins. - for (ActiveAdmin activeAdmin : admins) { - if (activeAdmin.disableCamera) { - return true; - } - } - return false; - } + return Boolean.TRUE.equals( + mDevicePolicyEngine.getResolvedPolicy(policy, affectedUserId)); } } @@ -10117,9 +9855,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { clearUserPoliciesLocked(userId); clearOverrideApnUnchecked(); clearApplicationRestrictions(userId); - if (!isPolicyEngineForFinanceFlagEnabled()) { - mInjector.getPackageManagerInternal().clearBlockUninstallForUser(userId); - } mOwners.clearDeviceOwner(); mOwners.writeDeviceOwner(); @@ -10131,16 +9866,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { setNetworkLoggingActiveInternal(false); deleteTransferOwnershipBundleLocked(userId); toggleBackupServiceActive(UserHandle.USER_SYSTEM, true); - if (!isPolicyEngineForFinanceFlagEnabled()) { - pushUserControlDisabledPackagesLocked(userId); - } setGlobalSettingDeviceOwnerType(DEVICE_OWNER_TYPE_DEFAULT); - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - mDevicePolicyEngine.removePoliciesForAdmin( - EnforcingAdmin.createEnterpriseEnforcingAdmin( - admin.info.getComponent(), userId, admin)); - } + mDevicePolicyEngine.removePoliciesForAdmin( + EnforcingAdmin.createEnterpriseEnforcingAdmin( + admin.info.getComponent(), userId, admin)); } private void clearApplicationRestrictions(int userId) { @@ -10289,11 +10019,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { applyProfileRestrictionsIfDeviceOwnerLocked(); setNetworkLoggingActiveInternal(false); - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - mDevicePolicyEngine.removePoliciesForAdmin( - EnforcingAdmin.createEnterpriseEnforcingAdmin( - admin.info.getComponent(), userId, admin)); - } + mDevicePolicyEngine.removePoliciesForAdmin( + EnforcingAdmin.createEnterpriseEnforcingAdmin( + admin.info.getComponent(), userId, admin)); } @Override @@ -10337,9 +10065,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mAffiliationIds.clear(); resetAffiliationCacheLocked(); policy.mLockTaskPackages.clear(); - if (!isPolicyEngineForFinanceFlagEnabled()) { - updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userId); - } policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE; saveSettingsLocked(userId); @@ -10347,7 +10072,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mIPermissionManager.updatePermissionFlagsForAllApps( PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0 /* flagValues */, userId); - pushUserRestrictions(userId); } catch (RemoteException re) { // Shouldn't happen. Slogf.wtf(LOG_TAG, "Failing in updatePermissionFlagsForAllApps", re); @@ -11280,25 +11004,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void dumpPerUserData(IndentingPrintWriter pw) { + private void dumpPersonalAppInfoForSystemUserNoLock(IndentingPrintWriter pw) { + wtfIfInLock(); + PersonalAppsSuspensionHelper.forUser(mContext, UserHandle.USER_SYSTEM).dump(pw); + } + + private void dumpPerUserPolicyData(IndentingPrintWriter pw) { int userCount = mUserData.size(); for (int i = 0; i < userCount; i++) { int userId = mUserData.keyAt(i); DevicePolicyData policy = getUserData(userId); policy.dump(pw); pw.println(); - - if (userId == UserHandle.USER_SYSTEM) { - pw.increaseIndent(); - PersonalAppsSuspensionHelper.forUser(mContext, userId).dump(pw); - pw.decreaseIndent(); - pw.println(); - } else { - // pm.getUnsuspendablePackages() will fail if it's called for a different user; - // as this dump is mostly useful for system user anyways, we can just ignore the - // others (rather than changing the permission check in the PM method) - Slogf.d(LOG_TAG, "skipping PersonalAppsSuspensionHelper.dump() for user " + userId); - } } } @@ -11316,7 +11033,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(); mDeviceAdminServiceController.dump(pw); pw.println(); - dumpPerUserData(pw); + dumpPerUserPolicyData(pw); pw.println(); mConstants.dump(pw); pw.println(); @@ -11343,6 +11060,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mStateCache.dump(pw); pw.println(); } + dumpPersonalAppInfoForSystemUserNoLock(pw); synchronized (mSubscriptionsChangedListenerLock) { pw.println("Subscription changed listener : " + mSubscriptionsChangedListener); @@ -11434,53 +11152,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void addPersistentPreferredActivity(ComponentName who, String callerPackageName, IntentFilter filter, ComponentName activity) { - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); final int userId = caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin; - if (who == null) { - enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( - who, - MANAGE_DEVICE_POLICY_LOCK_TASK, - caller.getPackageName(), - userId); - } else { - Preconditions.checkCallAuthorization(isProfileOwner(caller) - || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); - enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName); - } - if (!isPackageInstalledForUser(activity.getPackageName(), userId)) { - // Fail early as packageManager doesn't persist the activity if its not installed. - return; - } - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter), - enforcingAdmin, - new ComponentNamePolicyValue(activity), + EnforcingAdmin enforcingAdmin; + if (who == null) { + enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + who, + MANAGE_DEVICE_POLICY_LOCK_TASK, + caller.getPackageName(), userId); } else { - Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); - synchronized (getLockObject()) { - long id = mInjector.binderClearCallingIdentity(); - try { - mIPackageManager.addPersistentPreferredActivity(filter, activity, userId); - mIPackageManager.flushPackageRestrictionsAsUser(userId); - } catch (RemoteException re) { - // Shouldn't happen - Slog.wtf(LOG_TAG, "Error adding persistent preferred activity", re); - } finally { - mInjector.binderRestoreCallingIdentity(id); - } - } + enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName); + } + if (!isPackageInstalledForUser(activity.getPackageName(), userId)) { + // Fail early as packageManager doesn't persist the activity if its not installed. + return; } + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter), + enforcingAdmin, + new ComponentNamePolicyValue(activity), + userId); final String activityPackage = (activity != null ? activity.getPackageName() : null); DevicePolicyEventLogger @@ -11493,51 +11188,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void clearPackagePersistentPreferredActivities(ComponentName who, String callerPackageName, String packageName) { - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); final int userId = caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin; - if (who == null) { - enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( - who, - MANAGE_DEVICE_POLICY_LOCK_TASK, - caller.getPackageName(), - userId); - } else { - Preconditions.checkCallAuthorization(isProfileOwner(caller) - || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); - enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName); - } - clearPackagePersistentPreferredActivitiesFromPolicyEngine( - enforcingAdmin, - packageName, + EnforcingAdmin enforcingAdmin; + if (who == null) { + enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + who, + MANAGE_DEVICE_POLICY_LOCK_TASK, + caller.getPackageName(), userId); } else { - Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); - synchronized (getLockObject()) { - long id = mInjector.binderClearCallingIdentity(); - try { - mIPackageManager.clearPackagePersistentPreferredActivities(packageName, - userId); - mIPackageManager.flushPackageRestrictionsAsUser(userId); - } catch (RemoteException re) { - // Shouldn't happen - Slogf.wtf( - LOG_TAG, "Error when clearing package persistent preferred activities", - re); - } finally { - mInjector.binderRestoreCallingIdentity(id); - } - } + enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName); } + clearPackagePersistentPreferredActivitiesFromPolicyEngine( + enforcingAdmin, + packageName, + userId); } /** @@ -12274,28 +11943,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - Objects.requireNonNull(who, "ComponentName is null"); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); int userId = getProfileParentUserIfRequested( caller.getUserId(), calledOnParentInstance); if (calledOnParentInstance) { - if (!isPolicyEngineForFinanceFlagEnabled()) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(caller)); - } Preconditions.checkArgument(packageList == null || packageList.isEmpty(), "Permitted input methods must allow all input methods or only " + "system input methods when called on the parent instance of an " + "organization-owned device"); - } else if (!isPolicyEngineForFinanceFlagEnabled()) { - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwner(caller)); } if (packageList != null) { @@ -12320,28 +11976,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } synchronized (getLockObject()) { - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( - who, MANAGE_DEVICE_POLICY_INPUT_METHODS, - caller.getPackageName(), userId); - if (packageList == null) { - mDevicePolicyEngine.removeLocalPolicy( - PolicyDefinition.PERMITTED_INPUT_METHODS, - admin, - userId); - } else { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.PERMITTED_INPUT_METHODS, - admin, - new StringSetPolicyValue(new HashSet<>(packageList)), - userId); - } + EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( + who, MANAGE_DEVICE_POLICY_INPUT_METHODS, + caller.getPackageName(), userId); + if (packageList == null) { + mDevicePolicyEngine.removeLocalPolicy( + PolicyDefinition.PERMITTED_INPUT_METHODS, + admin, + userId); } else { - ActiveAdmin admin = getParentOfAdminIfRequired( - getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), - calledOnParentInstance); - admin.permittedInputMethods = packageList; - saveSettingsLocked(caller.getUserId()); + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.PERMITTED_INPUT_METHODS, + admin, + new StringSetPolicyValue(new HashSet<>(packageList)), + userId); } } @@ -12371,37 +12019,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - Objects.requireNonNull(who, "ComponentName is null"); - } - - if (!isPolicyEngineForFinanceFlagEnabled()) { - if (calledOnParentInstance) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(caller)); - } else { - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwner(caller)); - } - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); synchronized (getLockObject()) { - if (isPolicyEngineForFinanceFlagEnabled()) { - int affectedUser = calledOnParentInstance ? getProfileParentId( - caller.getUserId()) : caller.getUserId(); - Set<String> policy = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser); - return policy == null ? null : new ArrayList<>(policy); - } else { - ActiveAdmin admin = getParentOfAdminIfRequired( - getProfileOwnerOrDeviceOwnerLocked( - caller.getUserId()), calledOnParentInstance); - return admin.permittedInputMethods; - } + int affectedUser = calledOnParentInstance ? getProfileParentId( + caller.getUserId()) : caller.getUserId(); + Set<String> policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser); + return policy == null ? null : new ArrayList<>(policy); } } @@ -12419,29 +12044,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private @Nullable List<String> getPermittedInputMethodsUnchecked(@UserIdInt int userId) { - List<String> result = null; - if (isPolicyEngineForFinanceFlagEnabled()) { - Set<String> policy = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.PERMITTED_INPUT_METHODS, userId); - result = policy == null ? null : new ArrayList<>(policy); - } else { - synchronized (getLockObject()) { - // Only device or profile owners can have permitted lists set. - List<ActiveAdmin> admins = - getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked( - userId); - for (ActiveAdmin admin : admins) { - List<String> fromAdmin = admin.permittedInputMethods; - if (fromAdmin != null) { - if (result == null) { - result = new ArrayList<String>(fromAdmin); - } else { - result.retainAll(fromAdmin); - } - } - } - } - } + Set<String> policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.PERMITTED_INPUT_METHODS, userId); + List<String> result = policy == null ? null : new ArrayList<>(policy); // If we have a permitted list add all system input methods. if (result != null) { @@ -12472,39 +12077,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String.format(NOT_SYSTEM_CALLER_MSG, "query if an input method is disabled by admin")); - if (isPolicyEngineForFinanceFlagEnabled()) { - int affectedUser = calledOnParentInstance ? getProfileParentId(userHandle) : userHandle; - Map<EnforcingAdmin, PolicyValue<Set<String>>> policies = - mDevicePolicyEngine.getLocalPoliciesSetByAdmins( - PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser); - EnforcingAdmin admin = null; - for (EnforcingAdmin a : policies.keySet()) { - if (a.getPackageName().equals(who.getPackageName())) { - if (policies.get(a).getValue() == null) { - return true; - } else { - return checkPackagesInPermittedListOrSystem( - Collections.singletonList(packageName), - new ArrayList<>(policies.get(a).getValue()), affectedUser); - } - } - } - // Admin didn't set a policy - return false; - } else { - synchronized (getLockObject()) { - ActiveAdmin admin = getParentOfAdminIfRequired( - getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance); - if (admin == null) { - return false; - } - if (admin.permittedInputMethods == null) { + int affectedUser = calledOnParentInstance ? getProfileParentId(userHandle) : userHandle; + Map<EnforcingAdmin, PolicyValue<Set<String>>> policies = + mDevicePolicyEngine.getLocalPoliciesSetByAdmins( + PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser); + for (EnforcingAdmin a : policies.keySet()) { + if (a.getPackageName().equals(who.getPackageName())) { + if (policies.get(a).getValue() == null) { return true; + } else { + return checkPackagesInPermittedListOrSystem( + Collections.singletonList(packageName), + new ArrayList<>(policies.get(a).getValue()), affectedUser); } - return checkPackagesInPermittedListOrSystem(Collections.singletonList(packageName), - admin.permittedInputMethods, userHandle); } } + // Admin didn't set a policy + return false; } @Override @@ -12775,12 +12364,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + ", token=" + token); } - final int userId = user.id; - - if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { - mDevicePolicyEngine.handleUserCreated(user); - } + mDevicePolicyEngine.handleUserCreated(user); + final int userId = user.id; if (token != null) { synchronized (getLockObject()) { if (mPendingUserCreatedCallbackTokens.contains(token)) { @@ -13374,79 +12960,54 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ComponentName who, String callerPackage, String key, boolean enabledFromThisOwner, boolean parent) { - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackage); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackage); int userId = caller.getUserId(); int affectedUserId = parent ? getProfileParentId(userId) : userId; checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION); - if (isPolicyEngineForFinanceFlagEnabled()) { - if (!isDeviceOwner(caller) && !isProfileOwner(caller)) { - EnforcingAdmin admin = enforcePermissionForUserRestriction( - who, - key, - caller.getPackageName(), - affectedUserId); - if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) { - throw new IllegalStateException("Calling package is not targeting Android U."); - } - if (!UserRestrictionsUtils.isValidRestriction(key)) { - throw new IllegalArgumentException("Invalid restriction key: " + key); + if (!isDeviceOwner(caller) && !isProfileOwner(caller)) { + EnforcingAdmin admin = enforcePermissionForUserRestriction( + who, + key, + caller.getPackageName(), + affectedUserId); + if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) { + throw new IllegalStateException("Calling package is not targeting Android U."); + } + if (!UserRestrictionsUtils.isValidRestriction(key)) { + throw new IllegalArgumentException("Invalid restriction key: " + key); + } + PolicyDefinition<Boolean> policyDefinition = + PolicyDefinition.getPolicyDefinitionForUserRestriction(key); + if (enabledFromThisOwner) { + setLocalUserRestrictionInternal( + admin, key, /* enabled= */ true, affectedUserId); + } else { + // Remove any local and global policy that was set by the admin + if (!policyDefinition.isLocalOnlyPolicy()) { + setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false); } - PolicyDefinition<Boolean> policyDefinition = - PolicyDefinition.getPolicyDefinitionForUserRestriction(key); - if (enabledFromThisOwner) { - setLocalUserRestrictionInternal( - admin, key, /* enabled= */ true, affectedUserId); - } else { - // Remove any local and global policy that was set by the admin - if (!policyDefinition.isLocalOnlyPolicy()) { - setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false); - } - if (!policyDefinition.isGlobalOnlyPolicy()) { - setLocalUserRestrictionInternal(admin, key, /* enabled= */ false, - userId); + if (!policyDefinition.isGlobalOnlyPolicy()) { + setLocalUserRestrictionInternal(admin, key, /* enabled= */ false, + userId); - int parentUserId = getProfileParentId(userId); - if (parentUserId != userId) { - setLocalUserRestrictionInternal( - admin, key, /* enabled= */ false, parentUserId); - } + int parentUserId = getProfileParentId(userId); + if (parentUserId != userId) { + setLocalUserRestrictionInternal( + admin, key, /* enabled= */ false, parentUserId); } } - } else { - if (!UserRestrictionsUtils.isValidRestriction(key)) { - return; - } - Objects.requireNonNull(who, "ComponentName is null"); - EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); - checkAdminCanSetRestriction(caller, parent, key); - setBackwardCompatibleUserRestriction( - caller, admin, key, enabledFromThisOwner, parent); } } else { if (!UserRestrictionsUtils.isValidRestriction(key)) { return; } Objects.requireNonNull(who, "ComponentName is null"); + EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); checkAdminCanSetRestriction(caller, parent, key); - synchronized (getLockObject()) { - final ActiveAdmin activeAdmin = getParentOfAdminIfRequired( - getProfileOwnerOrDeviceOwnerLocked(userId), parent); - // Save the restriction to ActiveAdmin. - final Bundle restrictions = activeAdmin.ensureUserRestrictions(); - if (enabledFromThisOwner) { - restrictions.putBoolean(key, true); - } else { - restrictions.remove(key); - } - saveUserRestrictionsLocked(userId); - } + setBackwardCompatibleUserRestriction( + caller, admin, key, enabledFromThisOwner, parent); } logUserRestrictionCall(key, enabledFromThisOwner, parent, caller); } @@ -13532,9 +13093,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION); - if (!isPolicyEngineForFinanceFlagEnabled()) { - throw new IllegalStateException("Feature flag is not enabled."); - } if (isDeviceOwner(caller) || isProfileOwner(caller)) { throw new SecurityException("Admins are not allowed to call this API."); } @@ -13604,121 +13162,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { key, enabled, caller.toString()); } - private void saveUserRestrictionsLocked(int userId) { - if (isPolicyEngineForFinanceFlagEnabled()) { - // User restrictions are handled in the policy engine - return; - } - saveSettingsLocked(userId); - pushUserRestrictions(userId); - sendChangedNotification(userId); - } - - /** - * Pushes the user restrictions originating from a specific user. - * - * If called by the profile owner of an organization-owned device, the global and local - * user restrictions will be an accumulation of the global user restrictions from the profile - * owner active admin and its parent active admin. The key of the local user restrictions set - * will be the target user id. - */ - private void pushUserRestrictions(int originatingUserId) { - if (isPolicyEngineForFinanceFlagEnabled()) { - // User restrictions are handled in the policy engine - return; - } - final Bundle global; - final RestrictionsSet local = new RestrictionsSet(); - final boolean isDeviceOwner; - synchronized (getLockObject()) { - isDeviceOwner = mOwners.isDeviceOwnerUserId(originatingUserId); - if (isDeviceOwner) { - final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - if (deviceOwner == null) { - return; // Shouldn't happen. - } - global = deviceOwner.getGlobalUserRestrictions(OWNER_TYPE_DEVICE_OWNER); - local.updateRestrictions(originatingUserId, deviceOwner.getLocalUserRestrictions( - OWNER_TYPE_DEVICE_OWNER)); - } else { - final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(originatingUserId); - if (profileOwner == null) { - return; - } - global = profileOwner.getGlobalUserRestrictions(OWNER_TYPE_PROFILE_OWNER); - local.updateRestrictions(originatingUserId, profileOwner.getLocalUserRestrictions( - OWNER_TYPE_PROFILE_OWNER)); - // Global (device-wide) and local user restrictions set by the profile owner of an - // organization-owned device are stored in the parent ActiveAdmin instance. - if (isProfileOwnerOfOrganizationOwnedDevice( - profileOwner.getUserHandle().getIdentifier())) { - // The global restrictions set on the parent ActiveAdmin instance need to be - // merged with the global restrictions set on the profile owner ActiveAdmin - // instance, since both are to be applied device-wide. - UserRestrictionsUtils.merge(global, - profileOwner.getParentActiveAdmin().getGlobalUserRestrictions( - OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)); - // The local restrictions set on the parent ActiveAdmin instance are only to be - // applied to the primary user. They therefore need to be added the local - // restriction set with the primary user id as the key, in this case the - // primary user id is the target user. - local.updateRestrictions( - getProfileParentId(profileOwner.getUserHandle().getIdentifier()), - profileOwner.getParentActiveAdmin().getLocalUserRestrictions( - OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)); - } - } - } - mUserManagerInternal.setDevicePolicyUserRestrictions(originatingUserId, global, local, - isDeviceOwner); - } - @Override public Bundle getUserRestrictions(ComponentName who, String callerPackage, boolean parent) { if (!mHasFeature) { return null; } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackage); - } else { - caller = getCallerIdentity(who); - } - - if (isPolicyEngineForFinanceFlagEnabled()) { - int targetUserId = parent - ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); - if (isDeviceOwner(caller) || isProfileOwner(caller)) { - Objects.requireNonNull(who, "ComponentName is null"); - Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) - || isFinancedDeviceOwner(caller) - || isProfileOwner(caller) - || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); - - Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId); - // Add global restrictions set by the admin as well. - restrictions.putAll( - getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL)); - return restrictions; - } else { - if (!mInjector.isChangeEnabled( - ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) { - throw new IllegalStateException("Calling package is not targeting Android U."); - } - return getUserRestrictionsFromPolicyEngine(admin, targetUserId); - } - } else { + CallerIdentity caller = getCallerIdentity(who, callerPackage); + int targetUserId = parent + ? getProfileParentId(caller.getUserId()) : caller.getUserId(); + EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); + if (isDeviceOwner(caller) || isProfileOwner(caller)) { Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller) || isProfileOwner(caller) || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); - synchronized (getLockObject()) { - final ActiveAdmin activeAdmin = getParentOfAdminIfRequired( - getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent); - return activeAdmin.userRestrictions; + + Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId); + // Add global restrictions set by the admin as well. + restrictions.putAll( + getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL)); + return restrictions; + } else { + if (!mInjector.isChangeEnabled( + ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) { + throw new IllegalStateException("Calling package is not targeting Android U."); } + return getUserRestrictionsFromPolicyEngine(admin, targetUserId); } } @@ -13889,10 +13359,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } final CallerIdentity caller = getCallerIdentity(callerPackage); - if (!isPolicyEngineForFinanceFlagEnabled()) { - throw new IllegalStateException("Feature flag is not enabled."); - } - EnforcingAdmin admin = getEnforcingAdminForCaller(/*who=*/ null, caller.getPackageName()); return getUserRestrictionsFromPolicyEngine(admin, @@ -13922,13 +13388,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean hidden, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { - enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); - } else { - Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) - || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); - } + enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); List<String> exemptApps = listPolicyExemptAppsUnchecked(mContext); if (exemptApps.contains(packageName)) { @@ -13940,11 +13400,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean result; synchronized (getLockObject()) { if (parent) { - if (!isPolicyEngineForFinanceFlagEnabled()) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice( - caller.getUserId()) && isManagedProfile(caller.getUserId())); - } // Ensure the package provided is a system package, this is to ensure that this // API cannot be used to leak if certain non-system package exists in the person // profile. @@ -13957,29 +13412,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)", packageName, hidden, userId); } - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.APPLICATION_HIDDEN(packageName), - admin, - new BooleanPolicyValue(hidden), - userId); - result = mInjector.binderWithCleanCallingIdentity(() -> { - try { - // This is a best effort to continue returning the same value that was - // returned before the policy engine migration. - return mInjector.getIPackageManager().getPackageInfo( - packageName, MATCH_UNINSTALLED_PACKAGES, userId) != null - && (mIPackageManager.getApplicationHiddenSettingAsUser( - packageName, userId) == hidden); - } catch (RemoteException e) { - return false; - } - }); - } else { - result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager - .setApplicationHiddenSettingAsUser(packageName, hidden, userId)); - } + EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.APPLICATION_HIDDEN(packageName), + admin, + new BooleanPolicyValue(hidden), + userId); + result = mInjector.binderWithCleanCallingIdentity(() -> { + try { + // This is a best effort to continue returning the same value that was + // returned before the policy engine migration. + return mInjector.getIPackageManager().getPackageInfo( + packageName, MATCH_UNINSTALLED_PACKAGES, userId) != null + && (mIPackageManager.getApplicationHiddenSettingAsUser( + packageName, userId) == hidden); + } catch (RemoteException e) { + return false; + } + }); } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN) @@ -13996,23 +13446,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String packageName, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { - // TODO: Also support DELEGATION_PACKAGE_ACCESS - enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); - } else { - Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) - || (caller.hasPackage() && isCallerDelegate( - caller, DELEGATION_PACKAGE_ACCESS))); - } + // TODO: Also support DELEGATION_PACKAGE_ACCESS + enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); synchronized (getLockObject()) { if (parent) { - if (!isPolicyEngineForFinanceFlagEnabled()) { - Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()) - && isManagedProfile(caller.getUserId())); - } // Ensure the package provided is a system package. mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(packageName, userId)); @@ -14199,57 +13637,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforceMaxStringLength(accountType, "account type"); - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); synchronized (getLockObject()) { - if (isPolicyEngineForFinanceFlagEnabled()) { - int affectedUser = getAffectedUser(parent); - EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( - who, - MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, - caller.getPackageName(), - affectedUser - ); - if (disabled) { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType), - enforcingAdmin, - new BooleanPolicyValue(disabled), - affectedUser); - } else { - mDevicePolicyEngine.removeLocalPolicy( - PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType), - enforcingAdmin, - affectedUser); - } + int affectedUser = getAffectedUser(parent); + EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + who, + MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, + caller.getPackageName(), + affectedUser + ); + if (disabled) { + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType), + enforcingAdmin, + new BooleanPolicyValue(disabled), + affectedUser); } else { - final ActiveAdmin ap; - Objects.requireNonNull(who, "ComponentName is null"); - /* - * When called on the parent DPM instance (parent == true), affects active admin - * selection in two ways: - * * The ActiveAdmin must be of an org-owned profile owner. - * * The parent ActiveAdmin instance should be used for managing the restriction. - */ - if (parent) { - ap = getParentOfAdminIfRequired(getOrganizationOwnedProfileOwnerLocked(caller), - parent); - } else { - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwner(caller)); - ap = getParentOfAdminIfRequired( - getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent); - } - if (disabled) { - ap.accountTypesWithManagementDisabled.add(accountType); - } else { - ap.accountTypesWithManagementDisabled.remove(accountType); - } - saveSettingsLocked(UserHandle.getCallingUserId()); + mDevicePolicyEngine.removeLocalPolicy( + PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType), + enforcingAdmin, + affectedUser); } } } @@ -14266,62 +13673,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return null; } - CallerIdentity caller; + CallerIdentity caller = getCallerIdentity(callerPackageName); Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); final ArraySet<String> resultSet = new ArraySet<>(); - if (isPolicyEngineForFinanceFlagEnabled()) { - int affectedUser = parent ? getProfileParentId(userId) : userId; - caller = getCallerIdentity(callerPackageName); - if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, - callerPackageName, affectedUser) - && !hasFullCrossUsersPermission(caller, userId)) { - throw new SecurityException("Caller does not have permission to call this on user: " - + affectedUser); - } - Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins( - PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED, - affectedUser); - - for (PolicyKey key : keys) { - if (!(key instanceof AccountTypePolicyKey)) { - throw new IllegalStateException("PolicyKey for " - + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type " - + "AccountTypePolicyKey"); - } - AccountTypePolicyKey parsedKey = - (AccountTypePolicyKey) key; - String accountType = Objects.requireNonNull(parsedKey.getAccountType()); + int affectedUser = parent ? getProfileParentId(userId) : userId; + if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, + callerPackageName, affectedUser) + && !hasFullCrossUsersPermission(caller, userId)) { + throw new SecurityException("Caller does not have permission to call this on user: " + + affectedUser); + } + Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins( + PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED, + affectedUser); - Boolean disabled = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType), - affectedUser); - if (disabled != null && disabled) { - resultSet.add(accountType); - } + for (PolicyKey key : keys) { + if (!(key instanceof AccountTypePolicyKey)) { + throw new IllegalStateException("PolicyKey for " + + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type " + + "AccountTypePolicyKey"); } - } else { - caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); - - synchronized (getLockObject()) { - if (!parent) { - final DevicePolicyData policy = getUserData(userId); - for (ActiveAdmin admin : policy.mAdminList) { - resultSet.addAll(admin.accountTypesWithManagementDisabled); - } - } + AccountTypePolicyKey parsedKey = + (AccountTypePolicyKey) key; + String accountType = Objects.requireNonNull(parsedKey.getAccountType()); - // Check if there's a profile owner of an org-owned device and the method is called - // for the parent user of this profile owner. - final ActiveAdmin orgOwnedAdmin = - getProfileOwnerOfOrganizationOwnedDeviceLocked(userId); - final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent - || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId); - if (shouldGetParentAccounts) { - resultSet.addAll( - orgOwnedAdmin.getParentActiveAdmin() - .accountTypesWithManagementDisabled); - } + Boolean disabled = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType), + affectedUser); + if (disabled != null && disabled) { + resultSet.add(accountType); } } return resultSet.toArray(new String[resultSet.size()]); @@ -14332,46 +13712,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean uninstallBlocked) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( - who, - new String[]{ - MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL - }, - caller.getPackageName(), - caller.getUserId()); - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.PACKAGE_UNINSTALL_BLOCKED(packageName), - enforcingAdmin, - new BooleanPolicyValue(uninstallBlocked), - caller.getUserId()); - } else { - Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDefaultDeviceOwner(caller) - || isFinancedDeviceOwner(caller))) - || (caller.hasPackage() - && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL))); - final int userId = caller.getUserId(); - synchronized (getLockObject()) { - long id = mInjector.binderClearCallingIdentity(); - try { - mIPackageManager.setBlockUninstallForUser( - packageName, uninstallBlocked, userId); - } catch (RemoteException re) { - // Shouldn't happen. - Slogf.e(LOG_TAG, "Failed to setBlockUninstallForUser", re); - } finally { - mInjector.binderRestoreCallingIdentity(id); - } - } - if (uninstallBlocked) { - final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); - pmi.removeNonSystemPackageSuspensions(packageName, userId); - pmi.removeDistractingPackageRestrictions(packageName, userId); - pmi.flushPackageRestrictions(userId); - } - } + EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( + who, + new String[]{ + MANAGE_DEVICE_POLICY_APPS_CONTROL, + MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL + }, + caller.getPackageName(), + caller.getUserId()); + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.PACKAGE_UNINSTALL_BLOCKED(packageName), + enforcingAdmin, + new BooleanPolicyValue(uninstallBlocked), + caller.getUserId()); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_UNINSTALL_BLOCKED) @@ -14898,49 +14251,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforceMaxPackageNameLength(pkg); } - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES); - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin; - synchronized (getLockObject()) { - enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName()); - } - LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin( + EnforcingAdmin enforcingAdmin; + synchronized (getLockObject()) { + enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName()); + } + LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin( + PolicyDefinition.LOCK_TASK, + enforcingAdmin, + caller.getUserId()); + LockTaskPolicy policy; + if (currentPolicy == null) { + policy = new LockTaskPolicy(Set.of(packages)); + } else { + policy = new LockTaskPolicy(currentPolicy); + policy.setPackages(Set.of(packages)); + } + if (policy.getPackages().isEmpty()) { + mDevicePolicyEngine.removeLocalPolicy( PolicyDefinition.LOCK_TASK, enforcingAdmin, caller.getUserId()); - LockTaskPolicy policy; - if (currentPolicy == null) { - policy = new LockTaskPolicy(Set.of(packages)); - } else { - policy = new LockTaskPolicy(currentPolicy); - policy.setPackages(Set.of(packages)); - } - if (policy.getPackages().isEmpty()) { - mDevicePolicyEngine.removeLocalPolicy( - PolicyDefinition.LOCK_TASK, - enforcingAdmin, - caller.getUserId()); - } else { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.LOCK_TASK, - enforcingAdmin, - policy, - caller.getUserId()); - } } else { - Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(caller); - final int userHandle = caller.getUserId(); - setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages))); - } + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.LOCK_TASK, + enforcingAdmin, + policy, + caller.getUserId()); } } @@ -14955,32 +14294,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public String[] getLockTaskPackages(ComponentName who, String callerPackageName) { - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); final int userHandle = caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { - synchronized (getLockObject()) { - enforceCanQueryLockTaskLocked(who, caller.getPackageName()); - } - LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.LOCK_TASK, userHandle); - if (policy == null) { - return new String[0]; - } else { - return policy.getPackages().toArray(new String[policy.getPackages().size()]); - } + synchronized (getLockObject()) { + enforceCanQueryLockTaskLocked(who, caller.getPackageName()); + } + LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.LOCK_TASK, userHandle); + if (policy == null) { + return new String[0]; } else { - Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(caller); - final List<String> packages = getUserData(userHandle).mLockTaskPackages; - return packages.toArray(new String[packages.size()]); - } + return policy.getPackages().toArray(new String[policy.getPackages().size()]); } } @@ -14996,18 +14321,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } final int userId = mInjector.userHandleGetCallingUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { - LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.LOCK_TASK, userId); - if (policy == null) { - return false; - } - return policy.getPackages().contains(pkg); - } else { - synchronized (getLockObject()) { - return getUserData(userId).mLockTaskPackages.contains(pkg); - } + LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.LOCK_TASK, userId); + if (policy == null) { + return false; } + return policy.getPackages().contains(pkg); } @Override @@ -15021,54 +14340,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Preconditions.checkArgument(hasHome || !hasNotification, "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME"); - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); final int userHandle = caller.getUserId(); synchronized (getLockObject()) { checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES); } - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin; - synchronized (getLockObject()) { - enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName()); - enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags); - } - LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin( + EnforcingAdmin enforcingAdmin; + synchronized (getLockObject()) { + enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName()); + enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags); + } + LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin( + PolicyDefinition.LOCK_TASK, + enforcingAdmin, + caller.getUserId()); + LockTaskPolicy policy; + if (currentPolicy == null) { + policy = new LockTaskPolicy(flags); + } else { + policy = new LockTaskPolicy(currentPolicy); + policy.setFlags(flags); + } + if (policy.getPackages().isEmpty() + && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) { + mDevicePolicyEngine.removeLocalPolicy( PolicyDefinition.LOCK_TASK, enforcingAdmin, caller.getUserId()); - LockTaskPolicy policy; - if (currentPolicy == null) { - policy = new LockTaskPolicy(flags); - } else { - policy = new LockTaskPolicy(currentPolicy); - policy.setFlags(flags); - } - if (policy.getPackages().isEmpty() - && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) { - mDevicePolicyEngine.removeLocalPolicy( - PolicyDefinition.LOCK_TASK, - enforcingAdmin, - caller.getUserId()); - } else { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.LOCK_TASK, - enforcingAdmin, - policy, - caller.getUserId()); - } } else { - Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(caller); - enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags); - setLockTaskFeaturesLocked(userHandle, flags); - } + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.LOCK_TASK, + enforcingAdmin, + policy, + caller.getUserId()); } } @@ -15081,33 +14386,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public int getLockTaskFeatures(ComponentName who, String callerPackageName) { - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); final int userHandle = caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { - synchronized (getLockObject()) { - enforceCanQueryLockTaskLocked(who, caller.getPackageName()); - } - LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.LOCK_TASK, userHandle); - if (policy == null) { - // We default on the power button menu, in order to be consistent with pre-P - // behaviour. - return DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS; - } - return policy.getFlags(); - } else { - Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(caller); - return getUserData(userHandle).mLockTaskFeatures; - } + synchronized (getLockObject()) { + enforceCanQueryLockTaskLocked(who, caller.getPackageName()); } + LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.LOCK_TASK, userHandle); + if (policy == null) { + // We default on the power button menu, in order to be consistent with pre-P + // behaviour. + return DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS; + } + return policy.getFlags(); } private void maybeClearLockTaskPolicyLocked() { @@ -15118,34 +14410,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (canDPCManagedUserUseLockTaskLocked(userId)) { continue; } - - if (isPolicyEngineForFinanceFlagEnabled()) { - Map<EnforcingAdmin, PolicyValue<LockTaskPolicy>> policies = - mDevicePolicyEngine.getLocalPoliciesSetByAdmins( - PolicyDefinition.LOCK_TASK, userId); - Set<EnforcingAdmin> admins = new HashSet<>(policies.keySet()); - for (EnforcingAdmin admin : admins) { - if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) { - mDevicePolicyEngine.removeLocalPolicy( - PolicyDefinition.LOCK_TASK, admin, userId); - } - } - } else { - final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages; - // TODO(b/278438525): handle in the policy engine - if (!lockTaskPackages.isEmpty()) { - Slogf.d(LOG_TAG, - "User id " + userId - + " not affiliated. Clearing lock task packages"); - setLockTaskPackagesLocked(userId, Collections.<String>emptyList()); - } - final int lockTaskFeatures = getUserData(userId).mLockTaskFeatures; - if (lockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) { - Slogf.d(LOG_TAG, - "User id " + userId - + " not affiliated. Clearing lock task features"); - setLockTaskFeaturesLocked(userId, - DevicePolicyManager.LOCK_TASK_FEATURE_NONE); + Map<EnforcingAdmin, PolicyValue<LockTaskPolicy>> policies = + mDevicePolicyEngine.getLocalPoliciesSetByAdmins( + PolicyDefinition.LOCK_TASK, userId); + Set<EnforcingAdmin> admins = new HashSet<>(policies.keySet()); + for (EnforcingAdmin admin : admins) { + if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) { + mDevicePolicyEngine.removeLocalPolicy( + PolicyDefinition.LOCK_TASK, admin, userId); } } } @@ -16442,69 +15714,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return result; } } else if (DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) { - if (isPolicyEngineForFinanceFlagEnabled()) { - Boolean value = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.SCREEN_CAPTURE_DISABLED, userId); - if (value != null && value) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, userId); - return result; - } - } else { - synchronized (getLockObject()) { - final DevicePolicyData policy = getUserData(userId); - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - final ActiveAdmin admin = policy.mAdminList.get(i); - if (admin.disableScreenCapture) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, userId); - result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - admin.info.getComponent()); - return result; - } - } - } + Boolean value = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, userId); + if (value != null && value) { + result = new Bundle(); + result.putInt(Intent.EXTRA_USER_ID, userId); + return result; } } else if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) { - if (isPolicyEngineForFinanceFlagEnabled()) { - PolicyDefinition<Boolean> policyDefinition = - PolicyDefinition.getPolicyDefinitionForUserRestriction( - UserManager.DISALLOW_CAMERA); - Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId); - if (value != null && value) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, userId); - return result; - } - } else { - synchronized (getLockObject()) { - final DevicePolicyData policy = getUserData(userId); - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - final ActiveAdmin admin = policy.mAdminList.get(i); - if (admin.disableCamera) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, userId); - result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - admin.info.getComponent()); - return result; - } - } - // For the camera, a device owner on a different user can disable it globally, - // so we need an additional check. - if (result == null - && DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) { - final ActiveAdmin admin = getDeviceOwnerAdminLocked(); - if (admin != null && admin.disableCamera) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, mOwners.getDeviceOwnerUserId()); - result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - admin.info.getComponent()); - return result; - } - } - } + PolicyDefinition<Boolean> policyDefinition = + PolicyDefinition.getPolicyDefinitionForUserRestriction( + UserManager.DISALLOW_CAMERA); + Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId); + if (value != null && value) { + result = new Bundle(); + result.putInt(Intent.EXTRA_USER_ID, userId); + return result; } } else { long ident = mInjector.binderClearCallingIdentity(); @@ -18564,14 +17789,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.d(LOG_TAG, "Current state of DevicePolicyData#mRemovingAdmins for user " + userHandle + ": " + policy.mRemovingAdmins); - pushScreenCapturePolicy(userHandle); - Slogf.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle); } pushMeteredDisabledPackages(userHandle); - // The removed admin might have disabled camera, so update user - // restrictions. - pushUserRestrictions(userHandle); } @Override @@ -20483,20 +19703,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private boolean isLockTaskFeatureEnabled(int lockTaskFeature) throws RemoteException { - int lockTaskFeatures = 0; - if (isPolicyEngineForFinanceFlagEnabled()) { - LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.LOCK_TASK, getCurrentForegroundUserId()); - lockTaskFeatures = policy == null - // We default on the power button menu, in order to be consistent with pre-P - // behaviour. - ? DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS - : policy.getFlags(); - } else { - //TODO(b/175285301): Explicitly get the user's identity to check. - lockTaskFeatures = - getUserData(getCurrentForegroundUserId()).mLockTaskFeatures; - } + LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.LOCK_TASK, getCurrentForegroundUserId()); + int lockTaskFeatures = policy == null + // We default on the power button menu, in order to be consistent with pre-P + // behaviour. + ? DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS + : policy.getFlags(); return (lockTaskFeatures & lockTaskFeature) == lockTaskFeature; } @@ -20680,41 +19893,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void setUserControlDisabledPackages(ComponentName who, String callerPackageName, List<String> packages) { Objects.requireNonNull(packages, "packages is null"); - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); checkCanExecuteOrThrowUnsafe( DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES); - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( - who, - MANAGE_DEVICE_POLICY_APPS_CONTROL, - caller.getPackageName(), - caller.getUserId()); - Binder.withCleanCallingIdentity(() -> { - if (packages.isEmpty()) { - removeUserControlDisabledPackages(caller, enforcingAdmin); - } else { - addUserControlDisabledPackages(caller, enforcingAdmin, new HashSet<>(packages)); - } - }); - } else { - Objects.requireNonNull(who, "ComponentName is null"); - Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) - || isProfileOwner(caller) || isFinancedDeviceOwner(caller)); - synchronized (getLockObject()) { - ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); - if (!Objects.equals(admin.protectedPackages, packages)) { - admin.protectedPackages = packages.isEmpty() ? null : packages; - saveSettingsLocked(caller.getUserId()); - pushUserControlDisabledPackagesLocked(caller.getUserId()); - } + EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + who, + MANAGE_DEVICE_POLICY_APPS_CONTROL, + caller.getPackageName(), + caller.getUserId()); + Binder.withCleanCallingIdentity(() -> { + if (packages.isEmpty()) { + removeUserControlDisabledPackages(caller, enforcingAdmin); + } else { + addUserControlDisabledPackages(caller, enforcingAdmin, new HashSet<>(packages)); } - } + }); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES) @@ -20756,34 +19950,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public List<String> getUserControlDisabledPackages(ComponentName who, String callerPackageName) { - CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } - - if (isPolicyEngineForFinanceFlagEnabled()) { - enforceCanQuery( - MANAGE_DEVICE_POLICY_APPS_CONTROL, - caller.getPackageName(), - caller.getUserId()); - // This retrieves the policy for the calling user only, DOs for example can't know - // what's enforced globally or on another user. - Set<String> packages = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES, - caller.getUserId()); - return packages == null ? Collections.emptyList() : packages.stream().toList(); - } else { - Objects.requireNonNull(who, "ComponentName is null"); - Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) - || isProfileOwner(caller) || isFinancedDeviceOwner(caller)); - synchronized (getLockObject()) { - ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); - return admin.protectedPackages != null - ? admin.protectedPackages : Collections.emptyList(); - } - } + CallerIdentity caller = getCallerIdentity(who, callerPackageName); + enforceCanQuery( + MANAGE_DEVICE_POLICY_APPS_CONTROL, + caller.getPackageName(), + caller.getUserId()); + // This retrieves the policy for the calling user only, DOs for example can't know + // what's enforced globally or on another user. + Set<String> packages = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES, + caller.getUserId()); + return packages == null ? Collections.emptyList() : packages.stream().toList(); } @Override @@ -21066,27 +20243,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending", parentUserId); - if (isPolicyEngineForFinanceFlagEnabled()) { - // TODO(b/280602237): migrate properly - ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId); - if (profileOwner != null) { - EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin( - profileOwner.info.getComponent(), - profileUserId, - profileOwner); - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.PERSONAL_APPS_SUSPENDED, - admin, - new BooleanPolicyValue(suspended), - parentUserId); - } - } else { - if (suspended) { - suspendPersonalAppsInPackageManager(parentUserId); - } else { - mInjector.getPackageManagerInternal().unsuspendForSuspendingPackage( - PLATFORM_PACKAGE_NAME, parentUserId); - } + // TODO(b/280602237): migrate properly + ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId); + if (profileOwner != null) { + EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin( + profileOwner.info.getComponent(), + profileUserId, + profileOwner); + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.PERSONAL_APPS_SUSPENDED, + admin, + new BooleanPolicyValue(suspended), + parentUserId); } synchronized (getLockObject()) { @@ -22373,35 +21541,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void setUsbDataSignalingEnabled(String packageName, boolean enabled) { Objects.requireNonNull(packageName, "Admin package name must be provided"); final CallerIdentity caller = getCallerIdentity(packageName); - if (!isPolicyEngineForFinanceFlagEnabled()) { - Preconditions.checkCallAuthorization( - isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), - "USB data signaling can only be controlled by a device owner or " - + "a profile owner on an organization-owned device."); - Preconditions.checkState(canUsbDataSignalingBeDisabled(), - "USB data signaling cannot be disabled."); - } synchronized (getLockObject()) { - if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( - /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, - caller.getPackageName(), - caller.getUserId()); - Preconditions.checkState(canUsbDataSignalingBeDisabled(), - "USB data signaling cannot be disabled."); - mDevicePolicyEngine.setGlobalPolicy( - PolicyDefinition.USB_DATA_SIGNALING, - enforcingAdmin, - new BooleanPolicyValue(enabled)); - } else { - ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); - if (admin.mUsbDataSignalingEnabled != enabled) { - admin.mUsbDataSignalingEnabled = enabled; - saveSettingsLocked(caller.getUserId()); - updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked()); - } - } + EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, + caller.getPackageName(), + caller.getUserId()); + Preconditions.checkState(canUsbDataSignalingBeDisabled(), + "USB data signaling cannot be disabled."); + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.USB_DATA_SIGNALING, + enforcingAdmin, + new BooleanPolicyValue(enabled)); } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING) @@ -22423,24 +21574,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean isUsbDataSignalingEnabled(String packageName) { final CallerIdentity caller = getCallerIdentity(packageName); - if (isPolicyEngineForFinanceFlagEnabled()) { - Boolean enabled = mDevicePolicyEngine.getResolvedPolicy( - PolicyDefinition.USB_DATA_SIGNALING, - caller.getUserId()); - return enabled == null || enabled; - } else { - synchronized (getLockObject()) { - // If the caller is an admin, return the policy set by itself. Otherwise - // return the device-wide policy. - if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice( - caller)) { - return getProfileOwnerOrDeviceOwnerLocked( - caller.getUserId()).mUsbDataSignalingEnabled; - } else { - return isUsbDataSignalingEnabledInternalLocked(); - } - } - } + Boolean enabled = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.USB_DATA_SIGNALING, + caller.getUserId()); + return enabled == null || enabled; } private boolean isUsbDataSignalingEnabledInternalLocked() { @@ -22849,9 +21986,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private void handleFinancedDeviceKioskRoleChange() { - if (!isPolicyEngineForFinanceFlagEnabled()) { - return; - } Slog.i(LOG_TAG, "Handling action " + ACTION_DEVICE_FINANCING_STATE_CHANGED); Intent intent = new Intent(ACTION_DEVICE_FINANCING_STATE_CHANGED); mInjector.binderWithCleanCallingIdentity(() -> { @@ -23842,13 +22976,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG); } - static boolean isPolicyEngineForFinanceFlagEnabled() { - return DeviceConfig.getBoolean( - NAMESPACE_DEVICE_POLICY_MANAGER, - ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG, - DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG); - } - private static boolean isKeepProfilesRunningFlagEnabled() { return DeviceConfig.getBoolean( NAMESPACE_DEVICE_POLICY_MANAGER, @@ -24200,9 +23327,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private boolean shouldMigrateToDevicePolicyEngine() { - return mInjector.binderWithCleanCallingIdentity(() -> - (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) - && !mOwners.isMigratedToPolicyEngine()); + return mInjector.binderWithCleanCallingIdentity(() -> !mOwners.isMigratedToPolicyEngine()); } /** diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt index ed7f0af9299f..9aa0a4182eb7 100644 --- a/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt +++ b/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt @@ -77,7 +77,7 @@ inline fun <T> MutableIntMap<T>.getOrPut(key: Int, defaultValue: () -> T): T { } operator fun <T> MutableIntMap<T>.minusAssign(key: Int) { - array.remove(key) + array.remove(key).also { array.gc() } } fun <T> MutableIntMap<T>.putWithDefault(key: Int, value: T, defaultValue: T): T { diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt index b4de5d164e3b..1ed4f8a777d2 100644 --- a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt +++ b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt @@ -77,7 +77,7 @@ inline fun <I : Immutable<M>, M : I> MutableIntReferenceMap<I, M>.mutateOrPut( } operator fun <I : Immutable<M>, M : I> MutableIntReferenceMap<I, M>.minusAssign(key: Int) { - array.remove(key) + array.remove(key).also { array.gc() } } operator fun <I : Immutable<M>, M : I> MutableIntReferenceMap<I, M>.set(key: Int, value: M) { diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java index 74dc8532f163..7552800d8b0e 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java @@ -56,7 +56,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.StatFs; import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.Postsubmit; import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -93,7 +93,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; -@Presubmit +@Postsubmit public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java new file mode 100644 index 000000000000..4fd8f26d91a8 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static com.google.common.truth.Truth.assertThat; + +import android.graphics.Point; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for the {@link DisplayDevice} class. + * + * Build/Install/Run: + * atest DisplayServicesTests:DisplayDeviceTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class DisplayDeviceTest { + private final DisplayDeviceInfo mDisplayDeviceInfo = new DisplayDeviceInfo(); + private static final int WIDTH = 500; + private static final int HEIGHT = 900; + private static final Point PORTRAIT_SIZE = new Point(WIDTH, HEIGHT); + private static final Point LANDSCAPE_SIZE = new Point(HEIGHT, WIDTH); + + @Mock + private SurfaceControl.Transaction mMockTransaction; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mDisplayDeviceInfo.width = WIDTH; + mDisplayDeviceInfo.height = HEIGHT; + mDisplayDeviceInfo.rotation = ROTATION_0; + } + + @Test + public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() { + DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo); + assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE); + } + + @Test + public void testGetDisplaySurfaceDefaultSizeLocked_rotation0() { + DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo); + displayDevice.setProjectionLocked(mMockTransaction, ROTATION_0, new Rect(), new Rect()); + assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE); + } + + @Test + public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() { + DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo); + displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect()); + assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE); + } + + @Test + public void testGetDisplaySurfaceDefaultSizeLocked_rotation180() { + DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo); + displayDevice.setProjectionLocked(mMockTransaction, ROTATION_180, new Rect(), new Rect()); + assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE); + } + + @Test + public void testGetDisplaySurfaceDefaultSizeLocked_rotation270() { + DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo); + displayDevice.setProjectionLocked(mMockTransaction, ROTATION_270, new Rect(), new Rect()); + assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE); + } + + private static class FakeDisplayDevice extends DisplayDevice { + private final DisplayDeviceInfo mDisplayDeviceInfo; + + FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo) { + super(null, null, "", InstrumentationRegistry.getInstrumentation().getContext()); + mDisplayDeviceInfo = displayDeviceInfo; + } + + @Override + public boolean hasStableUniqueId() { + return false; + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + return mDisplayDeviceInfo; + } + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java index f975b6fd1d6f..183a84de0eb0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java @@ -64,7 +64,9 @@ public final class AmbientLuxTest { private static final int AMBIENT_COLOR_TYPE = 20705; private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc"; private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f; + private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG = 5555.5f; private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE = 3456.7f; + private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG = 3333.3f; private Handler mHandler = new Handler(Looper.getMainLooper()); private Sensor mLightSensor; @@ -78,6 +80,10 @@ public final class AmbientLuxTest { @Mock private TypedArray mBiases; @Mock private TypedArray mHighLightBrightnesses; @Mock private TypedArray mHighLightBiases; + @Mock private TypedArray mBrightnessesStrong; + @Mock private TypedArray mBiasesStrong; + @Mock private TypedArray mHighLightBrightnessesStrong; + @Mock private TypedArray mHighLightBiasesStrong; @Mock private TypedArray mAmbientColorTemperatures; @Mock private TypedArray mDisplayColorTemperatures; @Mock private TypedArray mStrongAmbientColorTemperatures; @@ -108,6 +114,10 @@ public final class AmbientLuxTest { LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE); mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperature, HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE); + mockResourcesFloat(R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong, + LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG); + mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong, + HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG); when(mResourcesSpy.obtainTypedArray( R.array.config_displayWhiteBalanceAmbientColorTemperatures)) .thenReturn(mAmbientColorTemperatures); @@ -133,6 +143,18 @@ public final class AmbientLuxTest { when(mResourcesSpy.obtainTypedArray( R.array.config_displayWhiteBalanceHighLightAmbientBiases)) .thenReturn(mHighLightBiases); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceLowLightAmbientBrightnessesStrong)) + .thenReturn(mBrightnessesStrong); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceLowLightAmbientBiasesStrong)) + .thenReturn(mBiasesStrong); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceHighLightAmbientBrightnessesStrong)) + .thenReturn(mHighLightBrightnessesStrong); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceHighLightAmbientBiasesStrong)) + .thenReturn(mHighLightBiasesStrong); mockThrottler(); LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class, @@ -388,8 +410,8 @@ public final class AmbientLuxTest { public void testStrongMode() { final float lowerBrightness = 10.0f; final float upperBrightness = 50.0f; - setBrightnesses(lowerBrightness, upperBrightness); - setBiases(0.0f, 1.0f); + setBrightnessesStrong(lowerBrightness, upperBrightness); + setBiasesStrong(0.0f, 1.0f); final int ambientColorTempLow = 6000; final int ambientColorTempHigh = 8000; final int displayColorTempLow = 6400; @@ -413,7 +435,7 @@ public final class AmbientLuxTest { setEstimatedBrightnessAndUpdate(controller, mix(lowerBrightness, upperBrightness, brightnessFraction)); assertEquals(controller.mPendingAmbientColorTemperature, - mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, + mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG, mix(displayColorTempLow, displayColorTempHigh, ambientTempFraction), brightnessFraction), ALLOWED_ERROR_DELTA); @@ -458,7 +480,7 @@ public final class AmbientLuxTest { assertEquals(-1.0f, controller.mPendingAmbientColorTemperature, 0); } - void mockThrottler() { + private void mockThrottler() { when(mResourcesSpy.getInteger( R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0); when(mResourcesSpy.getInteger( @@ -513,10 +535,18 @@ public final class AmbientLuxTest { setFloatArrayResource(mBrightnesses, vals); } + private void setBrightnessesStrong(float... vals) { + setFloatArrayResource(mBrightnessesStrong, vals); + } + private void setBiases(float... vals) { setFloatArrayResource(mBiases, vals); } + private void setBiasesStrong(float... vals) { + setFloatArrayResource(mBiasesStrong, vals); + } + private void setHighLightBrightnesses(float... vals) { setFloatArrayResource(mHighLightBrightnesses, vals); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java index de27d773504e..e672928c5403 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java @@ -102,7 +102,7 @@ public class SettingsToPropertiesMapperTest { ).when(() -> Settings.Global.getString(any(), anyString())); mTestMapper = new SettingsToPropertiesMapper( - mMockContentResolver, TEST_MAPPING, new String[] {}); + mMockContentResolver, TEST_MAPPING, new String[] {}, new String[] {}); } @After diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index 5cca5fa8ea0b..679757629e32 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -40,7 +40,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) testHandler.flush() verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) @@ -64,14 +64,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), null /* packageNames */, true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) assertThat(failedNames).isNull() failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOfNulls(0), true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) assertThat(failedNames).isEmpty() } @@ -81,7 +81,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, - Binder.getCallingUid(), false /* forQuietMode */) + Binder.getCallingUid(), false /* forQuietMode */, false /* quarantined */) assertThat(failedNames).asList().hasSize(1) assertThat(failedNames).asList().contains(TEST_PACKAGE_2) @@ -92,7 +92,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) assertThat(failedNames).asList().hasSize(1) assertThat(failedNames).asList().contains(DEVICE_OWNER_PACKAGE) @@ -103,7 +103,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) assertThat(failedNames).asList().hasSize(1) assertThat(failedNames).asList().contains(NONEXISTENT_PACKAGE) @@ -116,7 +116,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), knownPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */)!! + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)!! assertThat(failedNames.size).isEqualTo(knownPackages.size) for (pkg in knownPackages) { @@ -132,7 +132,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), knownPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */)!! + TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */, false /* quarantined */)!! assertThat(failedNames.size).isEqualTo(1) assertThat(failedNames[0]).isEqualTo(MGMT_ROLE_HOLDER_PACKAGE) @@ -144,13 +144,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, false /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) testHandler.flush() verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) @@ -202,7 +202,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, - false /* forQuietMode */) + false /* forQuietMode */, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -220,7 +220,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, appExtras, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, - false /* forQuietMode */) + false /* forQuietMode */, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), @@ -265,7 +265,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, - false /* forQuietMode */) + false /* forQuietMode */, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -280,7 +280,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, - TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */) + TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -295,7 +295,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, - false /* forQuietMode */) + false /* forQuietMode */, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -310,7 +310,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */, null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID, - deviceOwnerUid, false /* forQuietMode */) + deviceOwnerUid, false /* forQuietMode */, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -324,7 +324,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { @Throws(Exception::class) fun sendPackagesSuspendedForUser() { suspendPackageHelper.sendPackagesSuspendedForUser( - Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, TEST_USER_ID) + Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, false, TEST_USER_ID) testHandler.flush() verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(), @@ -341,7 +341,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { @Throws(Exception::class) fun sendPackagesSuspendModifiedForUser() { suspendPackageHelper.sendPackagesSuspendedForUser( - Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, TEST_USER_ID) + Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, false, TEST_USER_ID) testHandler.flush() verify(broadcastHelper).sendPackageBroadcast( eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(), diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java index d32b6be4a198..bdbf4ecbd346 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -63,6 +63,8 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; +import java.io.File; + /** * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest} */ @@ -107,6 +109,8 @@ public final class UserManagerServiceTest { .getTargetContext(); private final SparseArray<UserData> mUsers = new SparseArray<>(); + private File mTestDir; + private Context mSpiedContext; private @Mock PackageManagerService mMockPms; @@ -144,17 +148,23 @@ public final class UserManagerServiceTest { doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any()); // Must construct UserManagerService in the UiThread + mTestDir = new File(mRealContext.getDataDir(), "umstest"); + mTestDir.mkdirs(); mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer, - mPackagesLock, mRealContext.getDataDir(), mUsers); + mPackagesLock, mTestDir, mUsers); mUmi = LocalServices.getService(UserManagerInternal.class); assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi) .isNotNull(); } @After - public void resetUserManagerInternal() { + public void tearDown() { // LocalServices follows the "Highlander rule" - There can be only one! LocalServices.removeServiceForTest(UserManagerInternal.class); + + // Clean up test dir to remove persisted user files. + assertThat(deleteRecursive(mTestDir)).isTrue(); + mUsers.clear(); } @Test @@ -331,6 +341,7 @@ public final class UserManagerServiceTest { @Test public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() throws Exception { setSystemUserHeadless(true); + removeNonSystemUsers(); assertThrows(UserManager.CheckedUserOperationException.class, () -> mUmi.getBootUser(/* waitUntilSet= */ false)); @@ -495,6 +506,14 @@ public final class UserManagerServiceTest { @Test public void testMainUser_hasNoCallsOrSMSRestrictionsByDefault() { + // Remove the main user so we can add another one + for (int i = 0; i < mUsers.size(); i++) { + UserData userData = mUsers.valueAt(i); + if (userData.info.isMain()) { + mUsers.delete(i); + break; + } + } UserInfo mainUser = mUms.createUserWithThrow("main user", USER_TYPE_FULL_SECONDARY, UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN); @@ -504,6 +523,14 @@ public final class UserManagerServiceTest { .isFalse(); } + private void removeNonSystemUsers() { + for (UserInfo user : mUms.getUsers(true)) { + if (!user.getUserHandle().isSystem()) { + mUms.removeUserInfo(user.id); + } + } + } + private void resetUserSwitcherEnabled() { mUms.putUserInfo(new UserInfo(USER_ID, "Test User", 0)); mUms.setUserRestriction(DISALLOW_USER_SWITCH, false, USER_ID); @@ -612,6 +639,18 @@ public final class UserManagerServiceTest { userData.mLastEnteredForegroundTimeMillis = timeMillis; } + public boolean deleteRecursive(File file) { + if (file.isDirectory()) { + for (File item : file.listFiles()) { + boolean success = deleteRecursive(item); + if (!success) { + return false; + } + } + } + return file.delete(); + } + private static final class TestUserData extends UserData { @SuppressWarnings("deprecation") diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java index 58cdb1b79d83..91d8ceb28ec9 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java @@ -50,9 +50,7 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.app.IBatteryStats; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; -import com.android.server.power.batterysaver.BatterySaverController; -import com.android.server.power.batterysaver.BatterySaverPolicy; -import com.android.server.power.batterysaver.BatterySavingStats; +import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.Before; @@ -69,8 +67,7 @@ public class NotifierTest { private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; private static final int USER_ID = 0; - @Mock private BatterySaverController mBatterySaverControllerMock; - @Mock private BatterySaverPolicy mBatterySaverPolicyMock; + @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock; @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; @Mock private Notifier mNotifierMock; @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; @@ -263,16 +260,8 @@ public class NotifierTest { } @Override - BatterySaverPolicy createBatterySaverPolicy( - Object lock, Context context, BatterySavingStats batterySavingStats) { - return mBatterySaverPolicyMock; - } - - @Override - BatterySaverController createBatterySaverController( - Object lock, Context context, BatterySaverPolicy batterySaverPolicy, - BatterySavingStats batterySavingStats) { - return mBatterySaverControllerMock; + BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) { + return mBatterySaverStateMachineMock; } @Override diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java index d6d5264257e6..8e1d8ab439dd 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java @@ -108,7 +108,6 @@ import com.android.server.power.PowerManagerService.WakeLock; import com.android.server.power.batterysaver.BatterySaverController; import com.android.server.power.batterysaver.BatterySaverPolicy; import com.android.server.power.batterysaver.BatterySaverStateMachine; -import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.testutils.OffsettableClock; import com.google.testing.junit.testparameterinjector.TestParameter; @@ -184,6 +183,7 @@ public class PowerManagerServiceTest { private OffsettableClock mClock; private long mLastElapsedRealtime; private TestLooper mTestLooper; + private boolean mIsBatterySaverSupported = true; private static class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> { private final IntentFilter mFilter; @@ -215,6 +215,10 @@ public class PowerManagerServiceTest { .setBatterySaverEnabled(BATTERY_SAVER_ENABLED) .setBrightnessFactor(BRIGHTNESS_FACTOR) .build(); + when(mBatterySaverStateMachineMock.getBatterySaverController()).thenReturn( + mBatterySaverControllerMock); + when(mBatterySaverStateMachineMock.getBatterySaverPolicy()).thenReturn( + mBatterySaverPolicyMock); when(mBatterySaverPolicyMock.getBatterySaverPolicy( eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS))) .thenReturn(powerSaveState); @@ -235,6 +239,7 @@ public class PowerManagerServiceTest { mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); mResourcesSpy = spy(mContextSpy.getResources()); when(mContextSpy.getResources()).thenReturn(mResourcesSpy); + setBatterySaverSupported(); MockContentResolver cr = new MockContentResolver(mContextSpy); cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); @@ -269,21 +274,7 @@ public class PowerManagerServiceTest { } @Override - BatterySaverPolicy createBatterySaverPolicy( - Object lock, Context context, BatterySavingStats batterySavingStats) { - return mBatterySaverPolicyMock; - } - - @Override - BatterySaverController createBatterySaverController( - Object lock, Context context, BatterySaverPolicy batterySaverPolicy, - BatterySavingStats batterySavingStats) { - return mBatterySaverControllerMock; - } - - @Override - BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context, - BatterySaverController batterySaverController) { + BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) { return mBatterySaverStateMachineMock; } @@ -480,6 +471,12 @@ public class PowerManagerServiceTest { mTestLooper.dispatchAll(); } + private void setBatterySaverSupported() { + when(mResourcesSpy.getBoolean( + com.android.internal.R.bool.config_batterySaverSupported)).thenReturn( + mIsBatterySaverSupported); + } + @Test public void testCreateService_initializesNativeServiceAndSetsPowerModes() { PowerManagerService service = createService(); @@ -2543,6 +2540,19 @@ public class PowerManagerServiceTest { } @Test + public void testGetFullPowerSavePolicy_whenNoBatterySaverSupported() { + mIsBatterySaverSupported = false; + setBatterySaverSupported(); + createService(); + BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build(); + assertFalse(mService.getBinderServiceInstance().setPowerSaveModeEnabled(true)); + BatterySaverPolicyConfig policyConfig = + mService.getBinderServiceInstance().getFullPowerSavePolicy(); + assertThat(mockReturnConfig.toString()).isEqualTo(policyConfig.toString()); + verify(mBatterySaverStateMachineMock, never()).getFullBatterySaverPolicy(); + } + + @Test public void testSetFullPowerSavePolicy_callsStateMachine() { createService(); startSystem(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index 48ba765c3968..f2cbef69c8e5 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -38,7 +38,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BatteryStatsHistory; import com.android.internal.os.BatteryStatsHistoryIterator; -import com.android.internal.os.Clock; import org.junit.Before; import org.junit.Test; @@ -65,7 +64,7 @@ public class BatteryStatsHistoryTest { private final Parcel mHistoryBuffer = Parcel.obtain(); private File mSystemDir; private File mHistoryDir; - private final Clock mClock = new MockClock(); + private final MockClock mClock = new MockClock(); private BatteryStatsHistory mHistory; private BatteryStats.HistoryPrinter mHistoryPrinter; @Mock @@ -486,10 +485,94 @@ public class BatteryStatsHistoryTest { NetworkRegistrationInfo.NR_STATE_NONE); } + @Test + public void largeTagPool() { + // Keep the preserved part of history short - we only need to capture the very tail of + // history. + mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 1, 6000, + mStepDetailsCalculator, mClock, mTracer); + + mHistory.forceRecordAllHistory(); + + mClock.realtime = 2_000_000; + mClock.uptime = 1_000_000; + // More than 32k strings + final int tagCount = 0x7FFF + 20; + for (int tag = 0; tag < tagCount;) { + mClock.realtime += 10; + mClock.uptime += 10; + mHistory.recordEvent(mClock.realtime, mClock.uptime, HistoryItem.EVENT_ALARM_START, + "a" + (tag++), 42); + + mHistory.setBatteryState(true, BatteryManager.BATTERY_STATUS_CHARGING, tag % 50, 0); + mClock.realtime += 10; + mClock.uptime += 10; + mHistory.recordWakelockStartEvent(mClock.realtime, mClock.uptime, "w" + tag, 42); + mClock.realtime += 10; + mClock.uptime += 10; + mHistory.recordWakelockStopEvent(mClock.realtime, mClock.uptime, "w" + tag, 42); + tag++; + + mHistory.recordWakeupEvent(mClock.realtime, mClock.uptime, "wr" + (tag++)); + } + + int eventTagsPooled = 0; + int eventTagsUnpooled = 0; + int wakelockTagsPooled = 0; + int wakelockTagsUnpooled = 0; + int wakeReasonTagsPooled = 0; + int wakeReasonTagsUnpooled = 0; + for (BatteryStatsHistoryIterator iterator = mHistory.iterate(); iterator.hasNext(); ) { + HistoryItem item = iterator.next(); + if (item.cmd != HistoryItem.CMD_UPDATE) { + continue; + } + String checkinDump = toString(item, true); + if (item.eventCode == HistoryItem.EVENT_ALARM_START) { + if (item.eventTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + eventTagsPooled++; + assertThat(checkinDump).contains("+Eal=" + item.eventTag.poolIdx); + } else { + eventTagsUnpooled++; + assertThat(checkinDump).contains("+Eal=42:\"" + item.eventTag.string + "\""); + } + } + + if (item.wakelockTag != null) { + if (item.wakelockTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + wakelockTagsPooled++; + assertThat(checkinDump).contains("w=" + item.wakelockTag.poolIdx); + } else { + wakelockTagsUnpooled++; + assertThat(checkinDump).contains("w=42:\"" + item.wakelockTag.string + "\""); + } + } + + if (item.wakeReasonTag != null) { + if (item.wakeReasonTag.poolIdx + != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) { + wakeReasonTagsPooled++; + assertThat(checkinDump).contains("wr=" + item.wakeReasonTag.poolIdx); + } else { + wakeReasonTagsUnpooled++; + assertThat(checkinDump).contains("wr=0:\"" + item.wakeReasonTag.string + "\""); + } + } + } + + // Self-check - ensure that we have all cases represented in the test + assertThat(eventTagsPooled).isGreaterThan(0); + assertThat(eventTagsUnpooled).isGreaterThan(0); + assertThat(wakelockTagsPooled).isGreaterThan(0); + assertThat(wakelockTagsUnpooled).isGreaterThan(0); + assertThat(wakeReasonTagsPooled).isGreaterThan(0); + assertThat(wakeReasonTagsUnpooled).isGreaterThan(0); + } + private String toString(BatteryStats.HistoryItem item, boolean checkin) { StringWriter writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); - mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ true); + mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ false); pw.flush(); return writer.toString(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java index 99d66c5bda19..746fb53c7254 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java @@ -16,20 +16,42 @@ package com.android.server.biometrics; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static java.util.Collections.emptySet; + import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.content.res.Resources; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; import com.android.internal.R; +import com.android.server.biometrics.sensors.BiometricNotification; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.File; + +@Presubmit +@SmallTest public class AuthenticationStatsCollectorTest { private AuthenticationStatsCollector mAuthenticationStatsCollector; @@ -40,47 +62,220 @@ public class AuthenticationStatsCollectorTest { private Context mContext; @Mock private Resources mResources; + @Mock + private PackageManager mPackageManager; + @Mock + private FingerprintManager mFingerprintManager; + @Mock + private FaceManager mFaceManager; + @Mock + private SharedPreferences mSharedPreferences; + @Mock + private BiometricNotification mBiometricNotification; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getResources()).thenReturn(mResources); - when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1)) - .thenReturn(FRR_THRESHOLD); + when(mResources.getFraction(eq(R.fraction.config_biometricNotificationFrrThreshold), + anyInt(), anyInt())).thenReturn(FRR_THRESHOLD); + + when(mContext.getPackageManager()).thenReturn(mPackageManager); + + when(mContext.getSystemServiceName(FingerprintManager.class)) + .thenReturn(Context.FINGERPRINT_SERVICE); + when(mContext.getSystemService(Context.FINGERPRINT_SERVICE)) + .thenReturn(mFingerprintManager); + when(mContext.getSystemServiceName(FaceManager.class)).thenReturn(Context.FACE_SERVICE); + when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager); + + when(mContext.getSharedPreferences(any(File.class), anyInt())) + .thenReturn(mSharedPreferences); + when(mSharedPreferences.getStringSet(anyString(), anySet())).thenReturn(emptySet()); mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext, - 0 /* modality */); + 0 /* modality */, mBiometricNotification); } @Test public void authenticate_authenticationSucceeded_mapShouldBeUpdated() { // Assert that the user doesn't exist in the map initially. - assertNull(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1)); + assertThat(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1)).isNull(); - mAuthenticationStatsCollector.authenticate(USER_ID_1, true /* authenticated*/); + mAuthenticationStatsCollector.authenticate(USER_ID_1, true /* authenticated */); AuthenticationStats authenticationStats = mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1); - assertEquals(USER_ID_1, authenticationStats.getUserId()); - assertEquals(1, authenticationStats.getTotalAttempts()); - assertEquals(0, authenticationStats.getRejectedAttempts()); - assertEquals(0, authenticationStats.getEnrollmentNotifications()); + assertThat(authenticationStats.getUserId()).isEqualTo(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(1); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0); + assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0); } @Test public void authenticate_authenticationFailed_mapShouldBeUpdated() { // Assert that the user doesn't exist in the map initially. - assertNull(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1)); + assertThat(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1)).isNull(); - mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated*/); + mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); AuthenticationStats authenticationStats = mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1); - assertEquals(USER_ID_1, authenticationStats.getUserId()); - assertEquals(1, authenticationStats.getTotalAttempts()); - assertEquals(1, authenticationStats.getRejectedAttempts()); - assertEquals(0, authenticationStats.getEnrollmentNotifications()); + + assertThat(authenticationStats.getUserId()).isEqualTo(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(1); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(1); + assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0); + } + + @Test + public void authenticate_frrNotExceeded_notificationNotExceeded_shouldNotSendNotification() { + + mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1, + new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */, + 40 /* rejectedAttempts */, 0 /* enrollmentNotifications */, + 0 /* modality */)); + + mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); + + // Assert that no notification should be sent. + verify(mBiometricNotification, never()).sendFaceEnrollNotification(any()); + verify(mBiometricNotification, never()).sendFpEnrollNotification(any()); + // Assert that data has been reset. + AuthenticationStats authenticationStats = mAuthenticationStatsCollector + .getAuthenticationStatsForUser(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0); + assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f); + } + + @Test + public void authenticate_frrExceeded_notificationExceeded_shouldNotSendNotification() { + + mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1, + new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */, + 400 /* rejectedAttempts */, 2 /* enrollmentNotifications */, + 0 /* modality */)); + + mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); + + // Assert that no notification should be sent. + verify(mBiometricNotification, never()).sendFaceEnrollNotification(any()); + verify(mBiometricNotification, never()).sendFpEnrollNotification(any()); + // Assert that data has been reset. + AuthenticationStats authenticationStats = mAuthenticationStatsCollector + .getAuthenticationStatsForUser(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0); + assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f); + } + + @Test + public void authenticate_frrExceeded_bothBiometricsEnrolled_shouldNotSendNotification() { + + mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1, + new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */, + 400 /* rejectedAttempts */, 0 /* enrollmentNotifications */, + 0 /* modality */)); + + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) + .thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); + when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + + mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); + + // Assert that no notification should be sent. + verify(mBiometricNotification, never()).sendFaceEnrollNotification(any()); + verify(mBiometricNotification, never()).sendFpEnrollNotification(any()); + // Assert that data has been reset. + AuthenticationStats authenticationStats = mAuthenticationStatsCollector + .getAuthenticationStatsForUser(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0); + assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f); + } + + @Test + public void authenticate_frrExceeded_singleModality_shouldNotSendNotification() { + + mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1, + new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */, + 400 /* rejectedAttempts */, 0 /* enrollmentNotifications */, + 0 /* modality */)); + + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) + .thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false); + when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + + mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); + + // Assert that no notification should be sent. + verify(mBiometricNotification, never()).sendFaceEnrollNotification(any()); + verify(mBiometricNotification, never()).sendFpEnrollNotification(any()); + // Assert that data has been reset. + AuthenticationStats authenticationStats = mAuthenticationStatsCollector + .getAuthenticationStatsForUser(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0); + assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f); + } + + @Test + public void authenticate_frrExceeded_faceEnrolled_shouldSendFpNotification() { + mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1, + new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */, + 400 /* rejectedAttempts */, 0 /* enrollmentNotifications */, + 0 /* modality */)); + + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) + .thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); + when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + + mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); + + // Assert that fingerprint enrollment notification should be sent. + verify(mBiometricNotification, times(1)) + .sendFpEnrollNotification(mContext); + verify(mBiometricNotification, never()).sendFaceEnrollNotification(any()); + // Assert that data has been reset. + AuthenticationStats authenticationStats = mAuthenticationStatsCollector + .getAuthenticationStatsForUser(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0); + assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f); + } + + @Test + public void authenticate_frrExceeded_fpEnrolled_shouldSendFaceNotification() { + mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1, + new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */, + 400 /* rejectedAttempts */, 0 /* enrollmentNotifications */, + 0 /* modality */)); + + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) + .thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); + when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + + mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); + + // Assert that fingerprint enrollment notification should be sent. + verify(mBiometricNotification, times(1)) + .sendFaceEnrollNotification(mContext); + verify(mBiometricNotification, never()).sendFpEnrollNotification(any()); + // Assert that data has been reset. + AuthenticationStats authenticationStats = mAuthenticationStatsCollector + .getAuthenticationStatsForUser(USER_ID_1); + assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0); + assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0); + assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java new file mode 100644 index 000000000000..dde2a3c2cfd7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.SharedPreferences; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.io.File; +import java.util.List; +import java.util.Set; + +@Presubmit +@SmallTest +public class AuthenticationStatsPersisterTest { + + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + private static final int USER_ID_1 = 1; + private static final int USER_ID_2 = 2; + private static final String USER_ID = "user_id"; + private static final String FACE_ATTEMPTS = "face_attempts"; + private static final String FACE_REJECTIONS = "face_rejections"; + private static final String FINGERPRINT_ATTEMPTS = "fingerprint_attempts"; + private static final String FINGERPRINT_REJECTIONS = "fingerprint_rejections"; + private static final String ENROLLMENT_NOTIFICATIONS = "enrollment_notifications"; + private static final String KEY = "frr_stats"; + + @Mock + private Context mContext; + @Mock + private SharedPreferences mSharedPreferences; + @Mock + private SharedPreferences.Editor mEditor; + private AuthenticationStatsPersister mAuthenticationStatsPersister; + + @Captor + private ArgumentCaptor<Set<String>> mStringSetArgumentCaptor; + + @Before + public void setUp() { + when(mContext.getSharedPreferences(any(File.class), anyInt())) + .thenReturn(mSharedPreferences); + when(mSharedPreferences.edit()).thenReturn(mEditor); + when(mEditor.putStringSet(anyString(), anySet())).thenReturn(mEditor); + + mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext); + } + + @Test + public void getAllFrrStats_face_shouldListAllFrrStats() throws JSONException { + AuthenticationStats stats1 = new AuthenticationStats(USER_ID_1, + 300 /* totalAttempts */, 10 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + AuthenticationStats stats2 = new AuthenticationStats(USER_ID_2, + 200 /* totalAttempts */, 20 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FINGERPRINT); + when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn( + Set.of(buildFrrStats(stats1), buildFrrStats(stats2))); + + List<AuthenticationStats> authenticationStatsList = + mAuthenticationStatsPersister.getAllFrrStats(BiometricsProtoEnums.MODALITY_FACE); + + assertThat(authenticationStatsList.size()).isEqualTo(2); + AuthenticationStats expectedStats2 = new AuthenticationStats(USER_ID_2, + 0 /* totalAttempts */, 0 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + assertThat(authenticationStatsList).contains(stats1); + assertThat(authenticationStatsList).contains(expectedStats2); + } + + @Test + public void getAllFrrStats_fingerprint_shouldListAllFrrStats() throws JSONException { + // User 1 with fingerprint authentication stats. + AuthenticationStats stats1 = new AuthenticationStats(USER_ID_1, + 200 /* totalAttempts */, 20 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FINGERPRINT); + // User 2 without fingerprint authentication stats. + AuthenticationStats stats2 = new AuthenticationStats(USER_ID_2, + 300 /* totalAttempts */, 10 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn( + Set.of(buildFrrStats(stats1), buildFrrStats(stats2))); + + List<AuthenticationStats> authenticationStatsList = + mAuthenticationStatsPersister + .getAllFrrStats(BiometricsProtoEnums.MODALITY_FINGERPRINT); + + assertThat(authenticationStatsList.size()).isEqualTo(2); + AuthenticationStats expectedStats2 = new AuthenticationStats(USER_ID_2, + 0 /* totalAttempts */, 0 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FINGERPRINT); + assertThat(authenticationStatsList).contains(stats1); + assertThat(authenticationStatsList).contains(expectedStats2); + } + + @Test + public void persistFrrStats_newUser_face_shouldSuccess() throws JSONException { + AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1, + 300 /* totalAttempts */, 10 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + + mAuthenticationStatsPersister.persistFrrStats(authenticationStats.getUserId(), + authenticationStats.getTotalAttempts(), + authenticationStats.getRejectedAttempts(), + authenticationStats.getEnrollmentNotifications(), + authenticationStats.getModality()); + + verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture()); + assertThat(mStringSetArgumentCaptor.getValue()) + .contains(buildFrrStats(authenticationStats)); + } + + @Test + public void persistFrrStats_newUser_fingerprint_shouldSuccess() throws JSONException { + AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1, + 300 /* totalAttempts */, 10 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FINGERPRINT); + + mAuthenticationStatsPersister.persistFrrStats(authenticationStats.getUserId(), + authenticationStats.getTotalAttempts(), + authenticationStats.getRejectedAttempts(), + authenticationStats.getEnrollmentNotifications(), + authenticationStats.getModality()); + + verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture()); + assertThat(mStringSetArgumentCaptor.getValue()) + .contains(buildFrrStats(authenticationStats)); + } + + @Test + public void persistFrrStats_existingUser_shouldUpdateRecord() throws JSONException { + AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1, + 300 /* totalAttempts */, 10 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + AuthenticationStats newAuthenticationStats = new AuthenticationStats(USER_ID_1, + 500 /* totalAttempts */, 30 /* rejectedAttempts */, + 1 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn( + Set.of(buildFrrStats(authenticationStats))); + + mAuthenticationStatsPersister.persistFrrStats(newAuthenticationStats.getUserId(), + newAuthenticationStats.getTotalAttempts(), + newAuthenticationStats.getRejectedAttempts(), + newAuthenticationStats.getEnrollmentNotifications(), + newAuthenticationStats.getModality()); + + verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture()); + assertThat(mStringSetArgumentCaptor.getValue()) + .contains(buildFrrStats(newAuthenticationStats)); + } + + @Test + public void persistFrrStats_existingUserWithFingerprint_faceAuthenticate_shouldUpdateRecord() + throws JSONException { + // User with fingerprint authentication stats. + AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1, + 200 /* totalAttempts */, 20 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FINGERPRINT); + // The same user with face authentication stats. + AuthenticationStats newAuthenticationStats = new AuthenticationStats(USER_ID_1, + 500 /* totalAttempts */, 30 /* rejectedAttempts */, + 1 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn( + Set.of(buildFrrStats(authenticationStats))); + + mAuthenticationStatsPersister.persistFrrStats(newAuthenticationStats.getUserId(), + newAuthenticationStats.getTotalAttempts(), + newAuthenticationStats.getRejectedAttempts(), + newAuthenticationStats.getEnrollmentNotifications(), + newAuthenticationStats.getModality()); + + String expectedFrrStats = new JSONObject(buildFrrStats(authenticationStats)) + .put(ENROLLMENT_NOTIFICATIONS, newAuthenticationStats.getEnrollmentNotifications()) + .put(FACE_ATTEMPTS, newAuthenticationStats.getTotalAttempts()) + .put(FACE_REJECTIONS, newAuthenticationStats.getRejectedAttempts()).toString(); + verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture()); + assertThat(mStringSetArgumentCaptor.getValue()).contains(expectedFrrStats); + } + + @Test + public void removeFrrStats_existingUser_shouldUpdateRecord() throws JSONException { + AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1, + 300 /* totalAttempts */, 10 /* rejectedAttempts */, + 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE); + when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn( + Set.of(buildFrrStats(authenticationStats))); + + mAuthenticationStatsPersister.removeFrrStats(USER_ID_1); + + verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture()); + assertThat(mStringSetArgumentCaptor.getValue()).doesNotContain(authenticationStats); + } + + private String buildFrrStats(AuthenticationStats authenticationStats) + throws JSONException { + if (authenticationStats.getModality() == BiometricsProtoEnums.MODALITY_FACE) { + return new JSONObject() + .put(USER_ID, authenticationStats.getUserId()) + .put(FACE_ATTEMPTS, authenticationStats.getTotalAttempts()) + .put(FACE_REJECTIONS, authenticationStats.getRejectedAttempts()) + .put(ENROLLMENT_NOTIFICATIONS, authenticationStats.getEnrollmentNotifications()) + .toString(); + } else if (authenticationStats.getModality() == BiometricsProtoEnums.MODALITY_FINGERPRINT) { + return new JSONObject() + .put(USER_ID, authenticationStats.getUserId()) + .put(FINGERPRINT_ATTEMPTS, authenticationStats.getTotalAttempts()) + .put(FINGERPRINT_REJECTIONS, authenticationStats.getRejectedAttempts()) + .put(ENROLLMENT_NOTIFICATIONS, authenticationStats.getEnrollmentNotifications()) + .toString(); + } + return ""; + } +} diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index bcbbcd4456ee..908afc861f8b 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -61,6 +61,7 @@ import android.companion.virtual.sensor.IVirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensorConfig; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; @@ -1729,7 +1730,9 @@ public class VirtualDeviceManagerServiceTest { private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid, VirtualDeviceParams params) { VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext, - mAssociationInfo, mVdms, new Binder(), ownerUid, virtualDeviceId, + mAssociationInfo, mVdms, new Binder(), + new AttributionSource(ownerUid, "com.android.virtualdevice.test", "virtualdevice"), + virtualDeviceId, mInputController, mCameraAccessController, mPendingTrampolineCallback, mActivityListener, mSoundEffectListener, mRunningAppsChangedCallback, params, new DisplayManagerGlobal(mIDisplayManager)); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index b539a76b521c..943a9c4759c4 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -236,7 +236,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { verify(getServices().packageManagerInternal, never()) .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM); verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser( - any(), anyBoolean(), any(), any(), any(), any(), anyInt()); + any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt()); final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext); poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 7478778182bd..f408ef0394e2 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -7527,7 +7527,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); // Verify that the apps are NOT unsuspeded. verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser( - any(), eq(false), any(), any(), any(), any(), anyInt()); + any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt()); // Verify that DPC is invoked to check policy compliance. verify(mContext.spiedContext).startActivityAsUser( MockUtils.checkIntentAction(ACTION_CHECK_POLICY_COMPLIANCE), diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index e9a7d85ae755..037637630b7a 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -67,6 +67,8 @@ import java.util.stream.Collectors; @RunWith(AndroidJUnit4.class) public final class UpdatableFontDirTest { + private static final String LEGACY_FONTS_XML = "/system/etc/fonts.xml"; + /** * A {@link UpdatableFontDir.FontFileParser} for testing. Instead of using real font files, * this test uses fake font files. A fake font file has its PostScript naem and revision as the @@ -140,7 +142,7 @@ public final class UpdatableFontDirTest { private List<File> mPreinstalledFontDirs; private final Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME; private final Function<Map<String, File>, FontConfig> mConfigSupplier = - (map) -> SystemFonts.getSystemFontConfig(map, 0, 0); + (map) -> SystemFonts.getSystemFontConfigForTesting(LEGACY_FONTS_XML, map, 0, 0); private FakeFontFileParser mParser; private FakeFsverityUtil mFakeFsverityUtil; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java index 399655ffac55..e6d326a33415 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java @@ -92,11 +92,11 @@ public abstract class BaseAbsoluteVolumeBehaviorTest { // Default Audio Status given by the System Audio device in its initial <Report Audio Status> // that triggers AVB being enabled - private static final AudioStatus INITIAL_SYSTEM_AUDIO_DEVICE_STATUS = + protected static final AudioStatus INITIAL_SYSTEM_AUDIO_DEVICE_STATUS = new AudioStatus(50, false); // VolumeInfo passed to AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior to enable AVB - private static final VolumeInfo ENABLE_AVB_VOLUME_INFO = + protected static final VolumeInfo ENABLE_AVB_VOLUME_INFO = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) .setMuted(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute()) .setVolumeIndex(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume()) @@ -106,6 +106,8 @@ public abstract class BaseAbsoluteVolumeBehaviorTest { private static final int EMPTY_FLAGS = 0; + protected static final int STREAM_MUSIC_MAX_VOLUME = 25; + protected abstract HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService); protected abstract int getPhysicalAddress(); @@ -201,7 +203,7 @@ public abstract class BaseAbsoluteVolumeBehaviorTest { Collections.singletonList(getAudioOutputDevice())); // Max volume of STREAM_MUSIC - mAudioFramework.setStreamMaxVolume(AudioManager.STREAM_MUSIC, 25); + mAudioFramework.setStreamMaxVolume(AudioManager.STREAM_MUSIC, STREAM_MUSIC_MAX_VOLUME); // Receive messages from devices to make sure they're registered in HdmiCecNetwork mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveDevicePowerStatus( diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 40c762c28194..dec89d90cea5 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -383,6 +383,47 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleRoutingChange_toSwitchInActivePath_noStandby() { + int newPlaybackPhysicalAddress = 0x2100; + int switchPhysicalAddress = 0x2000; + mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + newPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mTestLooper.dispatchAll(); + + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, newPlaybackPhysicalAddress, + switchPhysicalAddress); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)) + .isEqualTo(Constants.HANDLED); + assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue(); + assertThat(mPowerManager.isInteractive()).isTrue(); + } + + @Test + public void handleRoutingChange_toTv_StandbyNow() { + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mTestLooper.dispatchAll(); + + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, mPlaybackPhysicalAddress, + Constants.TV_PHYSICAL_ADDRESS); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)) + .isEqualTo(Constants.HANDLED); + assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse(); + assertThat(mPowerManager.isInteractive()).isFalse(); + } + + @Test public void handleRoutingChange_otherDevice_StandbyNow_InactiveSource() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, diff --git a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java index 024e36d62273..86647fc16826 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java @@ -321,4 +321,98 @@ public class TvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehaviorTest { getLogicalAddress(), getSystemAudioDeviceLogicalAddress(), Constants.AUDIO_VOLUME_STATUS_UNKNOWN)); } + + /** + * Tests that a volume adjustment command with direction ADJUST_SAME causes HdmiControlService + * to request the System Audio device's audio status, and notify AudioService of the + * audio status. + */ + @Test + public void avbEnabled_audioDeviceVolumeAdjusted_adjustSame_updatesAudioService() { + enableAbsoluteVolumeBehavior(); + mNativeWrapper.clearResultMessages(); + + // HdmiControlService receives a volume adjustment with direction ADJUST_SAME + mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted( + getAudioOutputDevice(), + ENABLE_AVB_VOLUME_INFO, + AudioManager.ADJUST_SAME, + AudioDeviceVolumeManager.ADJUST_MODE_NORMAL + ); + mTestLooper.dispatchAll(); + + // Device sends <Give Audio Status> + assertThat(mNativeWrapper.getResultMessages()).contains( + HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(), + getSystemAudioDeviceLogicalAddress())); + + clearInvocations(mAudioManager); + + // Device receives <Report Audio Status> with a new volume and mute state + mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportAudioStatus( + getSystemAudioDeviceLogicalAddress(), + getLogicalAddress(), + 80, + true)); + mTestLooper.dispatchAll(); + + // HdmiControlService calls setStreamVolume and adjustStreamVolume to trigger volume UI + verify(mAudioManager).setStreamVolume( + eq(AudioManager.STREAM_MUSIC), + // Volume level is rescaled to the max volume of STREAM_MUSIC + eq(80 * STREAM_MUSIC_MAX_VOLUME / AudioStatus.MAX_VOLUME), + eq(AudioManager.FLAG_ABSOLUTE_VOLUME | AudioManager.FLAG_SHOW_UI)); + verify(mAudioManager).adjustStreamVolume( + eq(AudioManager.STREAM_MUSIC), + eq(AudioManager.ADJUST_MUTE), + eq(AudioManager.FLAG_ABSOLUTE_VOLUME | AudioManager.FLAG_SHOW_UI)); + } + + /** + * Tests that a volume adjustment command with direction ADJUST_SAME causes HdmiControlService + * to request the System Audio device's audio status, and notify AudioService of the + * audio status, even if it's unchanged from the previous one. + */ + @Test + public void avbEnabled_audioDeviceVolumeAdjusted_adjustSame_noChange_updatesAudioService() { + enableAbsoluteVolumeBehavior(); + mNativeWrapper.clearResultMessages(); + + // HdmiControlService receives a volume adjustment with direction ADJUST_SAME + mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted( + getAudioOutputDevice(), + ENABLE_AVB_VOLUME_INFO, + AudioManager.ADJUST_SAME, + AudioDeviceVolumeManager.ADJUST_MODE_NORMAL + ); + mTestLooper.dispatchAll(); + + // Device sends <Give Audio Status> + assertThat(mNativeWrapper.getResultMessages()).contains( + HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(), + getSystemAudioDeviceLogicalAddress())); + + clearInvocations(mAudioManager); + + // Device receives <Report Audio Status> with the same volume level and mute state that + // as when AVB was enabled + mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportAudioStatus( + getSystemAudioDeviceLogicalAddress(), + getLogicalAddress(), + INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(), + INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute())); + mTestLooper.dispatchAll(); + + // HdmiControlService calls setStreamVolume and adjustStreamVolume to trigger volume UI + verify(mAudioManager).setStreamVolume( + eq(AudioManager.STREAM_MUSIC), + // Volume level is rescaled to the max volume of STREAM_MUSIC + eq(INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume() + * STREAM_MUSIC_MAX_VOLUME / AudioStatus.MAX_VOLUME), + eq(AudioManager.FLAG_ABSOLUTE_VOLUME | AudioManager.FLAG_SHOW_UI)); + verify(mAudioManager).adjustStreamVolume( + eq(AudioManager.STREAM_MUSIC), + eq(AudioManager.ADJUST_UNMUTE), + eq(AudioManager.FLAG_ABSOLUTE_VOLUME | AudioManager.FLAG_SHOW_UI)); + } } diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java index 24029b1113ea..fc27edcb7bda 100644 --- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java @@ -35,6 +35,7 @@ import android.util.Pair; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -68,6 +69,27 @@ public class BugreportManagerServiceImplTest { mBugreportFileManager = new BugreportManagerServiceImpl.BugreportFileManager(); } + @After + public void tearDown() throws Exception { + // Changes to RoleManager persist between tests, so we need to clear out any funny + // business we did in previous tests. + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + CallbackFuture future = new CallbackFuture(); + runWithShellPermissionIdentity( + () -> { + roleManager.setBypassingRoleQualification(false); + roleManager.removeRoleHolderAsUser( + "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION", + mContext.getPackageName(), + /* flags= */ 0, + Process.myUserHandle(), + mContext.getMainExecutor(), + future); + }); + + assertThat(future.get()).isEqualTo(true); + } + @Test public void testBugreportFileManagerFileExists() { Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage); @@ -131,14 +153,17 @@ public class BugreportManagerServiceImplTest { new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>())); RoleManager roleManager = mContext.getSystemService(RoleManager.class); CallbackFuture future = new CallbackFuture(); - runWithShellPermissionIdentity(() -> roleManager.setBypassingRoleQualification(true)); - runWithShellPermissionIdentity(() -> roleManager.addRoleHolderAsUser( - "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION", - mContext.getPackageName(), - /* flags= */ 0, - Process.myUserHandle(), - mContext.getMainExecutor(), - future)); + runWithShellPermissionIdentity( + () -> { + roleManager.setBypassingRoleQualification(true); + roleManager.addRoleHolderAsUser( + "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION", + mContext.getPackageName(), + /* flags= */ 0, + Process.myUserHandle(), + mContext.getMainExecutor(), + future); + }); assertThat(future.get()).isEqualTo(true); mService.cancelBugreport(Binder.getCallingUid(), mContext.getPackageName()); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index dd681aa85c3f..ecd35a55e291 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -130,7 +130,8 @@ public final class UserManagerTest { // Keep system and current user if (user.id != UserHandle.USER_SYSTEM && user.id != currentUser && - user.id != communalProfileId) { + user.id != communalProfileId && + !user.isMain()) { removeUser(user.id); } } @@ -325,6 +326,24 @@ public final class UserManagerTest { assertThat(hasUser(user2.id)).isTrue(); } + + @MediumTest + @Test + public void testGetFullUserCount() throws Exception { + assertThat(mUserManager.getFullUserCount()).isEqualTo(1); + UserInfo user1 = createUser("User 1", UserInfo.FLAG_FULL); + UserInfo user2 = createUser("User 2", UserInfo.FLAG_ADMIN); + + assertThat(user1).isNotNull(); + assertThat(user2).isNotNull(); + + assertThat(mUserManager.getFullUserCount()).isEqualTo(3); + removeUser(user1.id); + assertThat(mUserManager.getFullUserCount()).isEqualTo(2); + removeUser(user2.id); + assertThat(mUserManager.getFullUserCount()).isEqualTo(1); + } + /** * Tests that UserManager knows how many users can be created. * @@ -1105,16 +1124,16 @@ public final class UserManagerTest { public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception { assumeManagedUsersSupported(); final int mainUserId = mUserManager.getMainUser().getIdentifier(); - final UserHandle mainUserHandle = asHandle(mainUserId); + final UserHandle currentUserHandle = asHandle(ActivityManager.getCurrentUser()); mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true, - mainUserHandle); + currentUserHandle); try { UserInfo userInfo = createProfileForUser("Managed", UserManager.USER_TYPE_PROFILE_MANAGED, mainUserId); assertThat(userInfo).isNull(); } finally { mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false, - mainUserHandle); + currentUserHandle); } } @@ -1190,6 +1209,7 @@ public final class UserManagerTest { @Test public void testGetManagedProfileCreationTime() throws Exception { assumeManagedUsersSupported(); + assumeTrue("User does not have access to creation time", mUserManager.isMainUser()); final int mainUserId = mUserManager.getMainUser().getIdentifier(); final long startTime = System.currentTimeMillis(); UserInfo profile = createProfileForUser("Managed 1", @@ -1545,6 +1565,25 @@ public final class UserManagerTest { assertThat(userInfo.name).isEqualTo(newName); } + @Test + public void testCannotCreateAdditionalMainUser() { + UserHandle mainUser = mUserManager.getMainUser(); + assumeTrue("There is no main user", mainUser != null); + + // Users with FLAG_MAIN can't be removed, so no point using the local createUser method. + UserInfo newMainUser = mUserManager.createUser("test", UserInfo.FLAG_MAIN); + assertThat(newMainUser).isNull(); + + List<UserInfo> users = mUserManager.getUsers(); + int mainUserCount = 0; + for (UserInfo user : users) { + if (user.isMain()) { + mainUserCount++; + } + } + assertThat(mainUserCount).isEqualTo(1); + } + private boolean isPackageInstalledForUser(String packageName, int userId) { try { return mPackageManager.getPackageInfoAsUser(packageName, 0, userId) != null; 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 e1f3c2b9e62c..57aa0b96a56a 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -7753,7 +7753,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testOnNotificationActionClick() { final int actionIndex = 2; final Notification.Action action = - new Notification.Action.Builder(null, "text", null).build(); + new Notification.Action.Builder(null, "text", PendingIntent.getActivity( + mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build(); final boolean generatedByAssistant = false; NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); @@ -7777,7 +7778,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testOnAssistantNotificationActionClick() { final int actionIndex = 1; final Notification.Action action = - new Notification.Action.Builder(null, "text", null).build(); + new Notification.Action.Builder(null, "text", PendingIntent.getActivity( + mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build(); final boolean generatedByAssistant = true; NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index fae92d9ac738..f83a1df358bd 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -36,7 +36,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -44,7 +44,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.ActivityManager; -import android.app.IActivityManager; import android.app.Notification; import android.app.Notification.Builder; import android.app.NotificationChannel; @@ -76,6 +75,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.server.LocalServices; import com.android.server.UiServiceTestCase; import com.android.server.uri.UriGrantsManagerInternal; @@ -850,84 +850,78 @@ public class NotificationRecordTest extends UiServiceTestCase { @Test public void testCalculateGrantableUris_PappProvided() { - IActivityManager am = mock(IActivityManager.class); UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class); when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), anyInt(), anyInt())).thenThrow(new SecurityException()); + LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); + LocalServices.addService(UriGrantsManagerInternal.class, ugm); + channel.setSound(null, null); Notification n = new Notification.Builder(mContext, channel.getId()) .setSmallIcon(Icon.createWithContentUri(Uri.parse("content://something"))) .build(); StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); - record.mAm = am; - record.mUgmInternal = ugm; - - try { - record.calculateGrantableUris(); - fail("App provided uri for p targeting app should throw exception"); - } catch (SecurityException e) { - // expected - } + + assertThrows("App provided uri for p targeting app should throw exception", + SecurityException.class, + () -> new NotificationRecord(mMockContext, sbn, channel)); } @Test public void testCalculateGrantableUris_PappProvided_invalidSound() { - IActivityManager am = mock(IActivityManager.class); UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class); when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), anyInt(), anyInt())).thenThrow(new SecurityException()); + LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); + LocalServices.addService(UriGrantsManagerInternal.class, ugm); + channel.setSound(Uri.parse("content://something"), mock(AudioAttributes.class)); Notification n = mock(Notification.class); when(n.getChannelId()).thenReturn(channel.getId()); StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); - record.mAm = am; - record.mUgmInternal = ugm; - record.calculateGrantableUris(); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound()); } @Test public void testCalculateGrantableUris_PuserOverridden() { - IActivityManager am = mock(IActivityManager.class); UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class); when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), anyInt(), anyInt())).thenThrow(new SecurityException()); + LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); + LocalServices.addService(UriGrantsManagerInternal.class, ugm); + channel.lockFields(NotificationChannel.USER_LOCKED_SOUND); Notification n = mock(Notification.class); when(n.getChannelId()).thenReturn(channel.getId()); StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); - record.mAm = am; - record.calculateGrantableUris(); + new NotificationRecord(mMockContext, sbn, channel); // should not throw } @Test public void testCalculateGrantableUris_prePappProvided() { - IActivityManager am = mock(IActivityManager.class); UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class); when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), anyInt(), anyInt())).thenThrow(new SecurityException()); + LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); + LocalServices.addService(UriGrantsManagerInternal.class, ugm); + Notification n = mock(Notification.class); when(n.getChannelId()).thenReturn(channel.getId()); StatusBarNotification sbn = new StatusBarNotification(PKG_O, PKG_O, id1, tag1, uid, uid, n, mUser, null, uid); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); - record.mAm = am; - record.calculateGrantableUris(); - // should not throw + new NotificationRecord(mMockContext, sbn, channel); // should not throw } @Test diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java index 3bb86a7bfecb..b9492e9b1b77 100644 --- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java @@ -170,10 +170,15 @@ public class SingleKeyGestureTests { } private void pressKey(int keyCode, long pressTime, boolean interactive) { + pressKey(keyCode, pressTime, interactive, false /* defaultDisplayOn */); + } + + private void pressKey( + int keyCode, long pressTime, boolean interactive, boolean defaultDisplayOn) { long eventTime = SystemClock.uptimeMillis(); final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN, keyCode, 0 /* repeat */, 0 /* metaState */); - mDetector.interceptKey(keyDown, interactive); + mDetector.interceptKey(keyDown, interactive, defaultDisplayOn); // keep press down. try { @@ -186,7 +191,7 @@ public class SingleKeyGestureTests { final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP, keyCode, 0 /* repeat */, 0 /* metaState */); - mDetector.interceptKey(keyUp, interactive); + mDetector.interceptKey(keyUp, interactive, defaultDisplayOn); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 3eed0b72e0bb..302ad7f33b7c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2719,6 +2719,9 @@ public class ActivityRecordTests extends WindowTestsBase { assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation); assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation); + // Unblock the rotation animation, so the further orientation updates won't be ignored. + unblockDisplayRotation(activity.mDisplayContent); + final ActivityRecord topActivity = createActivityRecord(activity.getTask()); topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java index 0eca8c988575..98f18433e53d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java @@ -18,6 +18,9 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; import android.platform.test.annotations.Presubmit; @@ -28,6 +31,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; + /** * Test class for {@link ActivitySnapshotController}. * @@ -42,13 +47,13 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { private ActivitySnapshotController mActivitySnapshotController; @Before public void setUp() throws Exception { + spyOn(mWm.mSnapshotController.mActivitySnapshotController); mActivitySnapshotController = mWm.mSnapshotController.mActivitySnapshotController; + doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots(); mActivitySnapshotController.resetTmpFields(); } @Test public void testOpenActivityTransition() { - final SnapshotController.TransitionState transitionState = - new SnapshotController.TransitionState(); final Task task = createTask(mDisplayContent); // note for createAppWindow: the new child is added at index 0 final WindowState openingWindow = createAppWindow(task, @@ -59,14 +64,12 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { "closingWindow"); closingWindow.mActivityRecord.commitVisibility( false /* visible */, true /* performLayout */); - transitionState.addParticipant(closingWindow.mActivityRecord, false); - transitionState.addParticipant(openingWindow.mActivityRecord, true); - mActivitySnapshotController.handleOpenActivityTransition(transitionState); + final ArrayList<WindowContainer> windows = new ArrayList<>(); + windows.add(openingWindow.mActivityRecord); + windows.add(closingWindow.mActivityRecord); + mActivitySnapshotController.handleTransitionFinish(windows); - assertEquals(1, mActivitySnapshotController.mPendingCaptureActivity.size()); assertEquals(0, mActivitySnapshotController.mPendingRemoveActivity.size()); - assertEquals(closingWindow.mActivityRecord, - mActivitySnapshotController.mPendingCaptureActivity.valueAt(0)); mActivitySnapshotController.resetTmpFields(); // simulate three activity @@ -74,19 +77,15 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { "belowClose"); belowClose.mActivityRecord.commitVisibility( false /* visible */, true /* performLayout */); - mActivitySnapshotController.handleOpenActivityTransition(transitionState); - assertEquals(1, mActivitySnapshotController.mPendingCaptureActivity.size()); + windows.add(belowClose.mActivityRecord); + mActivitySnapshotController.handleTransitionFinish(windows); assertEquals(1, mActivitySnapshotController.mPendingRemoveActivity.size()); - assertEquals(closingWindow.mActivityRecord, - mActivitySnapshotController.mPendingCaptureActivity.valueAt(0)); assertEquals(belowClose.mActivityRecord, mActivitySnapshotController.mPendingRemoveActivity.valueAt(0)); } @Test public void testCloseActivityTransition() { - final SnapshotController.TransitionState transitionState = - new SnapshotController.TransitionState(); final Task task = createTask(mDisplayContent); // note for createAppWindow: the new child is added at index 0 final WindowState closingWindow = createAppWindow(task, ACTIVITY_TYPE_STANDARD, @@ -97,10 +96,10 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { ACTIVITY_TYPE_STANDARD, "openingWindow"); openingWindow.mActivityRecord.commitVisibility( true /* visible */, true /* performLayout */); - transitionState.addParticipant(closingWindow.mActivityRecord, false); - transitionState.addParticipant(openingWindow.mActivityRecord, true); - mActivitySnapshotController.handleCloseActivityTransition(transitionState); - assertEquals(0, mActivitySnapshotController.mPendingCaptureActivity.size()); + final ArrayList<WindowContainer> windows = new ArrayList<>(); + windows.add(openingWindow.mActivityRecord); + windows.add(closingWindow.mActivityRecord); + mActivitySnapshotController.handleTransitionFinish(windows); assertEquals(1, mActivitySnapshotController.mPendingDeleteActivity.size()); assertEquals(openingWindow.mActivityRecord, mActivitySnapshotController.mPendingDeleteActivity.valueAt(0)); @@ -111,8 +110,8 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { "belowOpen"); belowOpen.mActivityRecord.commitVisibility( false /* visible */, true /* performLayout */); - mActivitySnapshotController.handleCloseActivityTransition(transitionState); - assertEquals(0, mActivitySnapshotController.mPendingCaptureActivity.size()); + windows.add(belowOpen.mActivityRecord); + mActivitySnapshotController.handleTransitionFinish(windows); assertEquals(1, mActivitySnapshotController.mPendingDeleteActivity.size()); assertEquals(1, mActivitySnapshotController.mPendingLoadActivity.size()); assertEquals(openingWindow.mActivityRecord, @@ -123,10 +122,6 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { @Test public void testTaskTransition() { - final SnapshotController.TransitionState taskCloseTransition = - new SnapshotController.TransitionState(); - final SnapshotController.TransitionState taskOpenTransition = - new SnapshotController.TransitionState(); final Task closeTask = createTask(mDisplayContent); // note for createAppWindow: the new child is added at index 0 final WindowState closingWindow = createAppWindow(closeTask, ACTIVITY_TYPE_STANDARD, @@ -147,10 +142,10 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { "openingWindowBelow"); openingWindowBelow.mActivityRecord.commitVisibility( false /* visible */, true /* performLayout */); - taskCloseTransition.addParticipant(closeTask, false); - taskOpenTransition.addParticipant(openTask, true); - mActivitySnapshotController.handleCloseTaskTransition(taskCloseTransition); - mActivitySnapshotController.handleOpenTaskTransition(taskOpenTransition); + final ArrayList<WindowContainer> windows = new ArrayList<>(); + windows.add(closeTask); + windows.add(openTask); + mActivitySnapshotController.handleTransitionFinish(windows); assertEquals(1, mActivitySnapshotController.mPendingRemoveActivity.size()); assertEquals(closingWindowBelow.mActivityRecord, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 4290f4b9ee80..d169a5854699 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -490,6 +490,11 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { .build(); final Task task = activity.getTask(); final TaskDisplayArea tda = task.getDisplayArea(); + // Ensure the display is not a large screen + if (tda.getConfiguration().smallestScreenWidthDp + >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP) { + resizeDisplay(activity.mDisplayContent, 500, 800); + } // Ignore the activity min width/height for determine multi window eligibility. mAtm.mRespectsActivityMinWidthHeightMultiWindow = -1; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppSnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/AppSnapshotControllerTests.java deleted file mode 100644 index 83af181481d9..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/AppSnapshotControllerTests.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; - -import static com.android.server.wm.SnapshotController.ACTIVITY_CLOSE; -import static com.android.server.wm.SnapshotController.ACTIVITY_OPEN; -import static com.android.server.wm.SnapshotController.TASK_CLOSE; -import static com.android.server.wm.SnapshotController.TASK_OPEN; - -import static org.junit.Assert.assertTrue; - -import android.platform.test.annotations.Presubmit; -import android.util.ArraySet; - -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - - -/** - * Test class for {@link SnapshotController}. - * - * Build/Install/Run: - * * atest WmTests:AppSnapshotControllerTests - */ -@SmallTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class AppSnapshotControllerTests extends WindowTestsBase { - final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>(); - final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>(); - - final TransitionMonitor mOpenActivityMonitor = new TransitionMonitor(); - final TransitionMonitor mCloseActivityMonitor = new TransitionMonitor(); - final TransitionMonitor mOpenTaskMonitor = new TransitionMonitor(); - final TransitionMonitor mCloseTaskMonitor = new TransitionMonitor(); - - @Before - public void setUp() throws Exception { - resetStatus(); - mWm.mSnapshotController.registerTransitionStateConsumer( - ACTIVITY_CLOSE, mCloseActivityMonitor::handleTransition); - mWm.mSnapshotController.registerTransitionStateConsumer( - ACTIVITY_OPEN, mOpenActivityMonitor::handleTransition); - mWm.mSnapshotController.registerTransitionStateConsumer( - TASK_CLOSE, mCloseTaskMonitor::handleTransition); - mWm.mSnapshotController.registerTransitionStateConsumer( - TASK_OPEN, mOpenTaskMonitor::handleTransition); - } - - @After - public void tearDown() throws Exception { - mWm.mSnapshotController.unregisterTransitionStateConsumer( - ACTIVITY_CLOSE, mCloseActivityMonitor::handleTransition); - mWm.mSnapshotController.unregisterTransitionStateConsumer( - ACTIVITY_OPEN, mOpenActivityMonitor::handleTransition); - mWm.mSnapshotController.unregisterTransitionStateConsumer( - TASK_CLOSE, mCloseTaskMonitor::handleTransition); - mWm.mSnapshotController.unregisterTransitionStateConsumer( - TASK_OPEN, mOpenTaskMonitor::handleTransition); - } - - private static class TransitionMonitor { - private final ArraySet<WindowContainer> mOpenParticipant = new ArraySet<>(); - private final ArraySet<WindowContainer> mCloseParticipant = new ArraySet<>(); - void handleTransition(SnapshotController.TransitionState<ActivityRecord> state) { - mOpenParticipant.addAll(state.getParticipant(true /* open */)); - mCloseParticipant.addAll(state.getParticipant(false /* close */)); - } - void reset() { - mOpenParticipant.clear(); - mCloseParticipant.clear(); - } - } - - private void resetStatus() { - mClosingApps.clear(); - mOpeningApps.clear(); - mOpenActivityMonitor.reset(); - mCloseActivityMonitor.reset(); - mOpenTaskMonitor.reset(); - mCloseTaskMonitor.reset(); - } - - @Test - public void testHandleAppTransition_openActivityTransition() { - final Task task = createTask(mDisplayContent); - // note for createAppWindow: the new child is added at index 0 - final WindowState openingWindow = createAppWindow(task, - ACTIVITY_TYPE_STANDARD, "openingWindow"); - openingWindow.mActivityRecord.commitVisibility( - true /* visible */, true /* performLayout */); - final WindowState closingWindow = createAppWindow(task, ACTIVITY_TYPE_STANDARD, - "closingWindow"); - closingWindow.mActivityRecord.commitVisibility( - false /* visible */, true /* performLayout */); - mClosingApps.add(closingWindow.mActivityRecord); - mOpeningApps.add(openingWindow.mActivityRecord); - mWm.mSnapshotController.handleAppTransition(mClosingApps, mOpeningApps); - assertTrue(mOpenActivityMonitor.mCloseParticipant.contains(closingWindow.mActivityRecord)); - assertTrue(mOpenActivityMonitor.mOpenParticipant.contains(openingWindow.mActivityRecord)); - } - - @Test - public void testHandleAppTransition_closeActivityTransition() { - final Task task = createTask(mDisplayContent); - // note for createAppWindow: the new child is added at index 0 - final WindowState closingWindow = createAppWindow(task, ACTIVITY_TYPE_STANDARD, - "closingWindow"); - closingWindow.mActivityRecord.commitVisibility( - false /* visible */, true /* performLayout */); - final WindowState openingWindow = createAppWindow(task, - ACTIVITY_TYPE_STANDARD, "openingWindow"); - openingWindow.mActivityRecord.commitVisibility( - true /* visible */, true /* performLayout */); - mClosingApps.add(closingWindow.mActivityRecord); - mOpeningApps.add(openingWindow.mActivityRecord); - mWm.mSnapshotController.handleAppTransition(mClosingApps, mOpeningApps); - assertTrue(mCloseActivityMonitor.mCloseParticipant.contains(closingWindow.mActivityRecord)); - assertTrue(mCloseActivityMonitor.mOpenParticipant.contains(openingWindow.mActivityRecord)); - } - - @Test - public void testHandleAppTransition_TaskTransition() { - final Task closeTask = createTask(mDisplayContent); - // note for createAppWindow: the new child is added at index 0 - final WindowState closingWindow = createAppWindow(closeTask, ACTIVITY_TYPE_STANDARD, - "closingWindow"); - closingWindow.mActivityRecord.commitVisibility( - false /* visible */, true /* performLayout */); - final WindowState closingWindowBelow = createAppWindow(closeTask, ACTIVITY_TYPE_STANDARD, - "closingWindowBelow"); - closingWindowBelow.mActivityRecord.commitVisibility( - false /* visible */, true /* performLayout */); - - final Task openTask = createTask(mDisplayContent); - final WindowState openingWindow = createAppWindow(openTask, ACTIVITY_TYPE_STANDARD, - "openingWindow"); - openingWindow.mActivityRecord.commitVisibility( - true /* visible */, true /* performLayout */); - final WindowState openingWindowBelow = createAppWindow(openTask, ACTIVITY_TYPE_STANDARD, - "openingWindowBelow"); - openingWindowBelow.mActivityRecord.commitVisibility( - false /* visible */, true /* performLayout */); - - mClosingApps.add(closingWindow.mActivityRecord); - mOpeningApps.add(openingWindow.mActivityRecord); - mWm.mSnapshotController.handleAppTransition(mClosingApps, mOpeningApps); - assertTrue(mCloseTaskMonitor.mCloseParticipant.contains(closeTask)); - assertTrue(mOpenTaskMonitor.mOpenParticipant.contains(openTask)); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java index 41c0caaea06e..c84eab3a6660 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java @@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import android.app.WindowConfiguration; @@ -507,6 +508,7 @@ public class ContentRecorderTests extends WindowTestsBase { // Return the default display as the value to mirror to ensure the VD with flag mirroring // creates a ContentRecordingSession automatically. doReturn(DEFAULT_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt()); + clearInvocations(virtualDisplay); virtualDisplay.updateRecording(); // THEN mirroring is initiated for the default display's DisplayArea. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java index f536cd0b0ed4..87dbca51e24e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -114,8 +114,9 @@ public class DisplayAreaGroupTest extends WindowTestsBase { @Test public void testResolveOverrideConfiguration_reverseOrientationWhenDifferentFromParentRoot() { - mDisplayContent.setBounds(0, 0, 600, 900); - mDisplayContent.updateOrientation(); + // Rotate the display to portrait. + final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); + displayRotation.setRotation(displayRotation.getPortraitRotation()); mDisplayContent.sendNewConfiguration(); // DAG fills Display @@ -128,7 +129,7 @@ public class DisplayAreaGroupTest extends WindowTestsBase { assertThat(mDisplayAreaGroup.getConfiguration().orientation) .isEqualTo(ORIENTATION_LANDSCAPE); - // DisplayAreaGroup is portriat, same as Display + // DisplayAreaGroup is portrait, same as Display mDisplayAreaGroup.setBounds(0, 0, 300, 450); assertThat(mDisplayAreaGroup.getConfiguration().orientation) 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 1c0fd4f770f3..a2b7da379eea 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1819,7 +1819,7 @@ public class DisplayContentTests extends WindowTestsBase { final ActivityRecord activity = createActivityRecord(mDisplayContent); final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent); - recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController(); // Do not rotate if the recents animation is animating on top. diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 6e52af1b4e7d..c1be5ca562b1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -353,6 +353,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { @Test public void testRecentViewInFixedPortraitWhenTopAppInLandscape() { + makeDisplayPortrait(mDefaultDisplay); unblockDisplayRotation(mDefaultDisplay); mWm.setRecentsAnimationController(mController); @@ -488,6 +489,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { @Test public void testWallpaperHasFixedRotationApplied() { + makeDisplayPortrait(mDefaultDisplay); unblockDisplayRotation(mDefaultDisplay); mWm.setRecentsAnimationController(mController); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index d5afe3b2f078..0cdd9b8a9e0b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -1880,6 +1880,11 @@ public class SizeCompatTests extends WindowTestsBase { final int dh = 2500; final int notchHeight = 200; setUpApp(new TestDisplayContent.Builder(mAtm, dw, dh).setNotch(notchHeight).build()); + // The test assumes the notch will be at left side when the orientation is landscape. + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_reverseDefaultRotation)) { + setReverseDefaultRotation(mActivity.mDisplayContent, false); + } addStatusBar(mActivity.mDisplayContent); mActivity.setVisible(false); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index be436bfc8bd9..7634d9f601d4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -384,7 +384,16 @@ public class SystemServicesTestRule implements TestRule { } private void tearDown() { - mWmService.mRoot.forAllDisplayPolicies(DisplayPolicy::release); + for (int i = mWmService.mRoot.getChildCount() - 1; i >= 0; i--) { + final DisplayContent dc = mWmService.mRoot.getChildAt(i); + // Unregister SettingsObserver. + dc.getDisplayPolicy().release(); + // Unregister SensorEventListener (foldable device may register for hinge angle). + dc.getDisplayRotation().onDisplayRemoved(); + if (dc.mDisplayRotationCompatPolicy != null) { + dc.mDisplayRotationCompatPolicy.dispose(); + } + } // Unregister display listener from root to avoid issues with subsequent tests. mContext.getSystemService(DisplayManager.class) diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 54b935132957..bfa279d5c3d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -143,6 +143,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { @Before public void setup() throws RemoteException { MockitoAnnotations.initMocks(this); + removeGlobalMinSizeRestriction(); mWindowOrganizerController = mAtm.mWindowOrganizerController; mTransitionController = mWindowOrganizerController.mTransitionController; mController = mWindowOrganizerController.mTaskFragmentOrganizerController; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index 16c38ac9fac3..08438c8cca5d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -354,25 +354,37 @@ public class TaskStackChangedListenerTest { } }); - final LandscapeActivity activity = - (LandscapeActivity) startTestActivity(LandscapeActivity.class); - - int[] taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, - candidate -> candidate[0] == activity.getTaskId()); - assertNotNull(taskIdAndOrientation); - assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, taskIdAndOrientation[1]); - - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); - taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, - candidate -> candidate[0] == activity.getTaskId()); - assertNotNull(taskIdAndOrientation); - assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, taskIdAndOrientation[1]); - - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, - candidate -> candidate[0] == activity.getTaskId()); - assertNotNull(taskIdAndOrientation); - assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, taskIdAndOrientation[1]); + final boolean isIgnoringOrientationRequest = + CommonUtils.getIgnoreOrientationRequest(Display.DEFAULT_DISPLAY); + if (isIgnoringOrientationRequest) { + CommonUtils.setIgnoreOrientationRequest(Display.DEFAULT_DISPLAY, false); + } + + try { + final LandscapeActivity activity = + (LandscapeActivity) startTestActivity(LandscapeActivity.class); + + int[] taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, + candidate -> candidate[0] == activity.getTaskId()); + assertNotNull(taskIdAndOrientation); + assertEquals( + ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, taskIdAndOrientation[1]); + + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, + candidate -> candidate[0] == activity.getTaskId()); + assertNotNull(taskIdAndOrientation); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, taskIdAndOrientation[1]); + + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, + candidate -> candidate[0] == activity.getTaskId()); + assertNotNull(taskIdAndOrientation); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, taskIdAndOrientation[1]); + } finally { + CommonUtils.setIgnoreOrientationRequest( + Display.DEFAULT_DISPLAY, isIgnoringOrientationRequest); + } } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 40b152186b66..9b6d4e216744 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -39,6 +39,7 @@ import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; +import static android.window.TransitionInfo.FLAG_SYNC; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static android.window.TransitionInfo.isIndependent; @@ -46,7 +47,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealM import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.server.wm.SnapshotController.TASK_CLOSE; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; @@ -1387,8 +1387,6 @@ public class TransitionTests extends WindowTestsBase { @Test public void testTransientLaunch() { spyOn(mWm.mSnapshotController.mTaskSnapshotController); - mWm.mSnapshotController.registerTransitionStateConsumer(TASK_CLOSE, - mWm.mSnapshotController.mTaskSnapshotController::handleTaskClose); final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>(); final TransitionController controller = new TestTransitionController(mAtm) { @Override @@ -2393,6 +2391,37 @@ public class TransitionTests extends WindowTestsBase { assertFalse(controller.isCollecting()); } + @Test + public void testNoSyncFlagIfOneTrack() { + final TransitionController controller = mAtm.getTransitionController(); + final TestTransitionPlayer player = registerTestTransitionPlayer(); + + mSyncEngine = createTestBLASTSyncEngine(); + controller.setSyncEngine(mSyncEngine); + + final Transition transitA = createTestTransition(TRANSIT_OPEN, controller); + final Transition transitB = createTestTransition(TRANSIT_OPEN, controller); + final Transition transitC = createTestTransition(TRANSIT_OPEN, controller); + + controller.startCollectOrQueue(transitA, (deferred) -> {}); + controller.startCollectOrQueue(transitB, (deferred) -> {}); + controller.startCollectOrQueue(transitC, (deferred) -> {}); + + // Verify that, as-long as there is <= 1 track, we won't get a SYNC flag + transitA.start(); + transitA.setAllReady(); + mSyncEngine.tryFinishForTest(transitA.getSyncId()); + assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0); + transitB.start(); + transitB.setAllReady(); + mSyncEngine.tryFinishForTest(transitB.getSyncId()); + assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0); + transitC.start(); + transitC.setAllReady(); + mSyncEngine.tryFinishForTest(transitC.getSyncId()); + assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0); + } + private static void makeTaskOrganized(Task... tasks) { final ITaskOrganizer organizer = mock(ITaskOrganizer.class); for (Task t : tasks) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index 6305bb621bc8..6216acbfe465 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -116,10 +116,7 @@ public class WallpaperControllerTests extends WindowTestsBase { public void testWallpaperSizeWithFixedTransform() { // No wallpaper final DisplayContent dc = mDisplayContent; - if (dc.mBaseDisplayHeight == dc.mBaseDisplayWidth) { - // Make sure the size is different when changing orientation. - resizeDisplay(dc, 500, 1000); - } + makeDisplayPortrait(dc); // No wallpaper WSA Surface final WindowState wallpaperWindow = createWallpaperWindow(dc); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 55fda0521e01..76576f76355d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -107,6 +107,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import java.util.ArrayList; + /** * Build/Install/Run: * atest WmTests:WindowManagerServiceTests @@ -975,6 +977,28 @@ public class WindowManagerServiceTests extends WindowTestsBase { verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0); } + @Test + public void testReportSystemGestureExclusionChanged_invalidWindow() { + final Session session = mock(Session.class); + final IWindow window = mock(IWindow.class); + final IBinder binder = mock(IBinder.class); + doReturn(binder).when(window).asBinder(); + + // No exception even if the window doesn't exist + mWm.reportSystemGestureExclusionChanged(session, window, new ArrayList<>()); + } + + @Test + public void testReportKeepClearAreasChanged_invalidWindow() { + final Session session = mock(Session.class); + final IWindow window = mock(IWindow.class); + final IBinder binder = mock(IBinder.class); + doReturn(binder).when(window).asBinder(); + + // No exception even if the window doesn't exist + mWm.reportKeepClearAreasChanged(session, window, new ArrayList<>(), new ArrayList<>()); + } + class TestResultReceiver implements IResultReceiver { public android.os.Bundle resultData; private final IBinder mBinder = mock(IBinder.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 62de67a165c5..99688dabda6e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -48,6 +48,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -59,6 +60,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import android.annotation.IntDef; @@ -956,6 +958,38 @@ class WindowTestsBase extends SystemServiceTestsBase { return testPlayer; } + /** Overrides the behavior of config_reverseDefaultRotation for the given display. */ + void setReverseDefaultRotation(DisplayContent dc, boolean reverse) { + final DisplayRotation displayRotation = dc.getDisplayRotation(); + if (!Mockito.mockingDetails(displayRotation).isSpy()) { + spyOn(displayRotation); + } + doAnswer(invocation -> { + invocation.callRealMethod(); + final int w = invocation.getArgument(0); + final int h = invocation.getArgument(1); + if (w > h) { + if (reverse) { + displayRotation.mPortraitRotation = Surface.ROTATION_90; + displayRotation.mUpsideDownRotation = Surface.ROTATION_270; + } else { + displayRotation.mPortraitRotation = Surface.ROTATION_270; + displayRotation.mUpsideDownRotation = Surface.ROTATION_90; + } + } else { + if (reverse) { + displayRotation.mLandscapeRotation = Surface.ROTATION_270; + displayRotation.mSeascapeRotation = Surface.ROTATION_90; + } else { + displayRotation.mLandscapeRotation = Surface.ROTATION_90; + displayRotation.mSeascapeRotation = Surface.ROTATION_270; + } + } + return null; + }).when(displayRotation).configure(anyInt(), anyInt()); + displayRotation.configure(dc.mBaseDisplayWidth, dc.mBaseDisplayHeight); + } + /** * Avoids rotating screen disturbed by some conditions. It is usually used for the default * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions). @@ -963,6 +997,8 @@ class WindowTestsBase extends SystemServiceTestsBase { * @see DisplayRotation#updateRotationUnchecked */ void unblockDisplayRotation(DisplayContent dc) { + dc.mOpeningApps.clear(); + mWm.mAppsFreezingScreen = 0; mWm.stopFreezingDisplayLocked(); // The rotation animation won't actually play, it needs to be cleared manually. dc.setRotationAnimation(null); @@ -971,11 +1007,19 @@ class WindowTestsBase extends SystemServiceTestsBase { static void resizeDisplay(DisplayContent displayContent, int width, int height) { displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity, displayContent.mBaseDisplayPhysicalXDpi, displayContent.mBaseDisplayPhysicalYDpi); + displayContent.getDisplayRotation().configure(width, height); final Configuration c = new Configuration(); displayContent.computeScreenConfiguration(c); displayContent.onRequestedOverrideConfigurationChanged(c); } + /** Used for the tests that assume the display is portrait by default. */ + static void makeDisplayPortrait(DisplayContent displayContent) { + if (displayContent.mBaseDisplayHeight <= displayContent.mBaseDisplayWidth) { + resizeDisplay(displayContent, 500, 1000); + } + } + // The window definition for UseTestDisplay#addWindows. The test can declare to add only // necessary windows, that avoids adding unnecessary overhead of unused windows. static final int W_NOTIFICATION_SHADE = TYPE_NOTIFICATION_SHADE; diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java b/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java index ed23296785c5..bfbba5f33f6c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java @@ -21,9 +21,12 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import android.app.Activity; import android.app.KeyguardManager; import android.app.UiAutomation; +import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; +import android.view.IWindowManager; import android.view.KeyEvent; +import android.view.WindowManagerGlobal; import androidx.test.uiautomator.UiDevice; @@ -48,6 +51,37 @@ public class CommonUtils { } } + public static boolean getIgnoreOrientationRequest(int displayId) { + final UiDevice uiDevice = UiDevice.getInstance(getInstrumentation()); + final String result; + try { + result = uiDevice.executeShellCommand("cmd window get-ignore-orientation-request -d " + + displayId); + } catch (IOException e) { + throw new RuntimeException(e); + } + + final String[] tokens = result.split(" "); + if (tokens.length != 4) { + throw new RuntimeException("Expecting a result with 4 tokens, but got " + result); + } + + // The output looks like "ignoreOrientationRequest true for displayId=0" + return Boolean.parseBoolean(tokens[1]); + } + + public static void setIgnoreOrientationRequest( + int displayId, boolean ignoreOrientationRequest) { + runWithShellPermissionIdentity(() -> { + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + wm.setIgnoreOrientationRequest(displayId, ignoreOrientationRequest); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + /** Dismisses the Keyguard if it is locked. */ public static void dismissKeyguard() { final KeyguardManager keyguardManager = getInstrumentation().getContext().getSystemService( diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 58da4b43aa05..3d78a1dd8943 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -54,6 +54,7 @@ import android.provider.DeviceConfig; import android.service.voice.HotwordDetectionService; import android.service.voice.HotwordDetectionServiceFailure; import android.service.voice.HotwordDetector; +import android.service.voice.IDetectorSessionStorageService; import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; import android.service.voice.ISandboxedDetectionService; import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback; @@ -69,6 +70,7 @@ import android.view.contentcapture.IContentCaptureManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVisualQueryDetectionAttentionListener; +import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import com.android.server.LocalServices; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -157,6 +159,8 @@ final class HotwordDetectionConnection { @NonNull private ServiceConnection mRemoteVisualQueryDetectionService; @GuardedBy("mLock") @Nullable private IBinder mAudioFlinger; + + @Nullable private IHotwordRecognitionStatusCallback mHotwordRecognitionCallback; @GuardedBy("mLock") private boolean mDebugHotwordLogging = false; @@ -694,6 +698,7 @@ final class HotwordDetectionConnection { updateContentCaptureManager(connection); updateSpeechService(connection); updateServiceIdentity(connection); + updateStorageService(connection); return connection; } } @@ -910,6 +915,7 @@ final class HotwordDetectionConnection { mVoiceInteractionServiceUid, mVoiceInteractorIdentity, mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener); } + mHotwordRecognitionCallback = callback; mDetectorSessions.put(detectorType, session); session.initialize(options, sharedMemory); } @@ -1035,6 +1041,23 @@ final class HotwordDetectionConnection { })); } + private void updateStorageService(ServiceConnection connection) { + connection.run(service -> { + service.registerRemoteStorageService(new IDetectorSessionStorageService.Stub() { + @Override + public void openFile(String filename, AndroidFuture future) + throws RemoteException { + Slog.v(TAG, "BinderCallback#onFileOpen"); + try { + mHotwordRecognitionCallback.onOpenFile(filename, future); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + }); + }); + } + private void addServiceUidForAudioPolicy(int uid) { mScheduledExecutorService.execute(() -> { AudioManagerInternal audioManager = diff --git a/telecomm/OWNERS b/telecomm/OWNERS index dcaf858a0a0b..b57b7c79326e 100644 --- a/telecomm/OWNERS +++ b/telecomm/OWNERS @@ -4,7 +4,6 @@ breadley@google.com tgunn@google.com xiaotonj@google.com rgreenwalt@google.com -chinmayd@google.com grantmenke@google.com pmadapurmath@google.com tjstuart@google.com
\ No newline at end of file diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 943d8d6cdd55..4a541da9e5aa 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -3388,7 +3388,11 @@ public abstract class Connection extends Conferenceable { public void onAbort() {} /** - * Notifies this Connection of a request to hold. + * Notifies this Connection of a request to hold. {@link Connection#setOnHold} should be within + * the onHold() body in order to transition the call state to {@link Connection#STATE_HOLDING}. + * <p> + * Note: If the Connection does not transition to {@link Connection#STATE_HOLDING} within 2 + * seconds, the call will be disconnected. */ public void onHold() {} diff --git a/telecomm/java/android/telecom/RemoteConnectionManager.java b/telecomm/java/android/telecom/RemoteConnectionManager.java index fbbfefd9d00e..fbf8eeffd947 100644 --- a/telecomm/java/android/telecom/RemoteConnectionManager.java +++ b/telecomm/java/android/telecom/RemoteConnectionManager.java @@ -39,18 +39,21 @@ public class RemoteConnectionManager { void addConnectionService( ComponentName componentName, IConnectionService outgoingConnectionServiceRpc) { - if (!mRemoteConnectionServices.containsKey(componentName)) { - try { - RemoteConnectionService remoteConnectionService = new RemoteConnectionService( - outgoingConnectionServiceRpc, - mOurConnectionServiceImpl); - mRemoteConnectionServices.put(componentName, remoteConnectionService); - } catch (RemoteException e) { - Log.w(RemoteConnectionManager.this, - "error when addConnectionService of %s: %s", componentName, - e.toString()); - } - } + mRemoteConnectionServices.computeIfAbsent( + componentName, + key -> { + try { + return new RemoteConnectionService( + outgoingConnectionServiceRpc, mOurConnectionServiceImpl); + } catch (RemoteException e) { + Log.w( + RemoteConnectionManager.this, + "error when addConnectionService of %s: %s", + componentName, + e.toString()); + return null; + } + }); } public RemoteConnection createRemoteConnection( @@ -63,17 +66,14 @@ public class RemoteConnectionManager { } ComponentName componentName = request.getAccountHandle().getComponentName(); - if (!mRemoteConnectionServices.containsKey(componentName)) { + RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName); + if (remoteService == null) { throw new UnsupportedOperationException("accountHandle not supported: " + componentName); } - RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName); - if (remoteService != null) { - return remoteService.createRemoteConnection( - connectionManagerPhoneAccount, request, isIncoming); - } - return null; + return remoteService.createRemoteConnection( + connectionManagerPhoneAccount, request, isIncoming); } /** @@ -94,17 +94,14 @@ public class RemoteConnectionManager { } ComponentName componentName = request.getAccountHandle().getComponentName(); - if (!mRemoteConnectionServices.containsKey(componentName)) { + RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName); + if (remoteService == null) { throw new UnsupportedOperationException("accountHandle not supported: " + componentName); } - RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName); - if (remoteService != null) { - return remoteService.createRemoteConference( - connectionManagerPhoneAccount, request, isIncoming); - } - return null; + return remoteService.createRemoteConference( + connectionManagerPhoneAccount, request, isIncoming); } public void conferenceRemoteConnections(RemoteConnection a, RemoteConnection b) { diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 64c2a4c82d1d..baacb574bde3 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -85,11 +85,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -1328,10 +1328,24 @@ public class SubscriptionManager { private final Context mContext; - // Cache of Resource that has been created in getResourcesForSubId. Key is a Pair containing - // the Context and subId. - private static final Map<Pair<Context, Integer>, Resources> sResourcesCache = - new ConcurrentHashMap<>(); + /** + * In order to prevent the overflow of the heap size due to an indiscriminate increase in the + * cache, the heap size of the resource cache is set sufficiently large. + */ + private static final int MAX_RESOURCE_CACHE_ENTRY_COUNT = 10_000; + + /** + * Cache of Resources that has been created in getResourcesForSubId. Key contains package name, + * and Configuration of Resources. If more than the maximum number of resources are stored in + * this cache, the least recently used Resources will be removed to maintain the maximum size. + */ + private static final Map<Pair<String, Configuration>, Resources> sResourcesCache = + Collections.synchronizedMap(new LinkedHashMap<>(16, 0.75f, true) { + @Override + protected boolean removeEldestEntry(Entry eldest) { + return size() > MAX_RESOURCE_CACHE_ENTRY_COUNT; + } + }); /** * A listener class for monitoring changes to {@link SubscriptionInfo} records. @@ -2817,14 +2831,20 @@ public class SubscriptionManager { @NonNull public static Resources getResourcesForSubId(Context context, int subId, boolean useRootLocale) { - // Check if resources for this context and subId already exist in the resource cache. - // Resources that use the root locale are not cached. - Pair<Context, Integer> cacheKey = null; - if (isValidSubscriptionId(subId) && !useRootLocale) { - cacheKey = Pair.create(context, subId); - if (sResourcesCache.containsKey(cacheKey)) { + // Check if the Resources already exists in the cache based on the given context. Find a + // Resource that match Configuration. + Pair<String, Configuration> cacheKey = null; + if (isValidSubscriptionId(subId)) { + Configuration configurationKey = + new Configuration(context.getResources().getConfiguration()); + if (useRootLocale) { + configurationKey.setLocale(Locale.ROOT); + } + cacheKey = Pair.create(context.getPackageName(), configurationKey); + Resources cached = sResourcesCache.get(cacheKey); + if (cached != null) { // Cache hit. Use cached Resources. - return sResourcesCache.get(cacheKey); + return cached; } } diff --git a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl index a81444d51374..06fc3c631eba 100644 --- a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl +++ b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl @@ -45,6 +45,7 @@ oneway interface ISatelliteTransmissionUpdateCallback { * Called when the satellite position changed. * * @param pointingInfo The pointing info containing the satellite location. + * Satellite location is based on magnetic north direction. */ void onSatellitePositionChanged(in PointingInfo pointingInfo); } diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java index 47dbdaff23bc..dc4d38b02f57 100644 --- a/telephony/java/android/telephony/satellite/PointingInfo.java +++ b/telephony/java/android/telephony/satellite/PointingInfo.java @@ -26,7 +26,7 @@ import java.util.Objects; /** * PointingInfo is used to store the position of satellite received from satellite modem. * The position of satellite is represented by azimuth and elevation angles - * with degrees as unit of measurement. + * with degrees as unit of measurement. Satellite position is based on magnetic north direction. * @hide */ @SystemApi diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp index dc6bdff6716c..615990f22e3c 100644 --- a/tests/BinaryTransparencyHostTest/Android.bp +++ b/tests/BinaryTransparencyHostTest/Android.bp @@ -35,6 +35,8 @@ java_test_host { data: [ ":BinaryTransparencyTestApp", ":EasterEgg", + ":FeatureSplitBase", + ":FeatureSplit1", ":com.android.apex.cts.shim.v2_rebootless_prebuilt", ], test_suites: [ diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java index 346622f0f467..6e5f08a11ed8 100644 --- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java +++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java @@ -91,20 +91,20 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { public void testCollectAllSilentInstalledMbaInfo() throws Exception { try { new InstallMultiple() - .addFile("ApkVerityTestApp.apk") - .addFile("ApkVerityTestAppSplit.apk") + .addFile("FeatureSplitBase.apk") + .addFile("FeatureSplit1.apk") .run(); updatePreloadApp(); - assertNotNull(getDevice().getAppPackageInfo("com.android.apkverity")); + assertNotNull(getDevice().getAppPackageInfo("com.android.test.split.feature")); assertNotNull(getDevice().getAppPackageInfo("com.android.egg")); assertTrue(getDevice().setProperty("debug.transparency.bg-install-apps", - "com.android.apkverity,com.android.egg")); + "com.android.test.split.feature,com.android.egg")); runDeviceTest("testCollectAllSilentInstalledMbaInfo"); } finally { // No need to wait until job complete, since we can't verifying very meaningfully. cancelPendingJob(); - uninstallPackage("com.android.apkverity"); + uninstallPackage("com.android.test.split.feature"); uninstallPackage("com.android.egg"); } } diff --git a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java index c087a85da2a8..2bc056ee743f 100644 --- a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java +++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java @@ -121,7 +121,7 @@ public class BinaryTransparencyTest { // Verify assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side - var expectedAppNames = Set.of("com.android.apkverity", "com.android.egg"); + var expectedAppNames = Set.of("com.android.test.split.feature", "com.android.egg"); var actualAppNames = appInfoList.stream().map((appInfo) -> appInfo.packageName) .collect(Collectors.toList()); assertThat(actualAppNames).containsAtLeastElementsIn(expectedAppNames); @@ -141,6 +141,6 @@ public class BinaryTransparencyTest { } } } - assertThat(actualSplitNames).containsExactly("feature_x"); // Name of ApkVerityTestAppSplit + assertThat(actualSplitNames).containsExactly("feature1"); // Name of FeatureSplit1 } } diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 3e67286998bc..2ccc0fa9e1e7 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -39,7 +39,11 @@ filegroup { "src/**/activityembedding/*.kt", "src/**/activityembedding/open/*.kt", "src/**/activityembedding/close/*.kt", + "src/**/activityembedding/layoutchange/*.kt", + "src/**/activityembedding/pip/*.kt", "src/**/activityembedding/rotation/*.kt", + "src/**/activityembedding/rtl/*.kt", + "src/**/activityembedding/splitscreen/*.kt", ], } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt index 4530ef3b88da..0c36c59a8a83 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized * Setup: Launch A|B in split with B being the secondary activity. Transitions: Finish B and expect * A to become fullscreen. * - * To run this test: `atest FlickerTests:CloseSecondaryActivityInSplitTest` + * To run this test: `atest FlickerTestsOther:CloseSecondaryActivityInSplitTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt index baf109bc4d55..adff5792c42d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.activityembedding +package com.android.server.wm.flicker.activityembedding.layoutchange import android.platform.test.annotations.Presubmit import android.tools.common.datatypes.Rect @@ -23,8 +23,9 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -38,7 +39,7 @@ import org.junit.runners.Parameterized * windows are equal in size. B is on the top and A is on the bottom. Transitions: Change the split * ratio to A:B=0.7:0.3, expect bounds change for both A and B. * - * To run this test: `atest FlickerTests:HorizontalSplitChangeRatioTest` + * To run this test: `atest FlickerTestsOther:HorizontalSplitChangeRatioTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt index d97027e7d06b..ce9c337ff9bd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized * Setup: Launch A|B in split with B being the secondary activity. Transitions: A start C with * alwaysExpand=true, expect C to launch in fullscreen and cover split A|B. * - * To run this test: `atest FlickerTests:MainActivityStartsSecondaryWithAlwaysExpandTest` + * To run this test: `atest FlickerTestsOther:MainActivityStartsSecondaryWithAlwaysExpandTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt index 8a997ddb5b35..48edf6ddeba6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt @@ -34,7 +34,7 @@ import org.junit.runners.Parameterized * Test opening an activity that will launch another activity as ActivityEmbedding placeholder in * split. * - * To run this test: `atest FlickerTests:OpenActivityEmbeddingPlaceholderSplitTest` + * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingPlaceholderSplitTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt index 49aa84bbffb4..365782012c54 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt @@ -34,7 +34,7 @@ import org.junit.runners.Parameterized /** * Test opening a secondary activity that will split with the main activity. * - * To run this test: `atest FlickerTests:OpenActivityEmbeddingSecondaryToSplitTest` + * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingSecondaryToSplitTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt index 4bb224620175..9f9fc23081c5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized * * Transitions: Let B start C, expect C to cover B and end up in split A|C. * - * To run this test: `atest FlickerTests:OpenThirdActivityOverSplitTest` + * To run this test: `atest FlickerTestsOther:OpenThirdActivityOverSplitTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt index 0fdf63c2ec76..30e833f433a8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt @@ -41,7 +41,7 @@ import org.junit.runners.Parameterized * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and finishes * itself, end up in split A|B. * - * To run this test: `atest FlickerTests:OpenTrampolineActivityTest` + * To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt index e4c35b2eb6b2..359845dc0de6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.activityembedding +package com.android.server.wm.flicker.activityembedding.pip import android.platform.test.annotations.Presubmit import android.tools.common.datatypes.Rect @@ -25,6 +25,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.FixMethodOrder import org.junit.Test @@ -38,7 +39,7 @@ import org.junit.runners.Parameterized * Setup: Start from a split A|B. Transition: B enters PIP, observe the window shrink to the bottom * right corner on screen. * - * To run this test: `atest FlickerTests:SecondaryActivityEnterPipTest` + * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt index da565001ae10..4f7d8a474da1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt @@ -23,6 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import com.android.server.wm.flicker.rotation.RotationTransition import org.junit.FixMethodOrder @@ -37,7 +38,7 @@ import org.junit.runners.Parameterized * Setup: Launch A|B in split with B being the secondary activity. Transitions: Rotate display, and * expect A and B to split evenly in new rotation. * - * To run this test: `atest FlickerTests:RotateSplitNoChangeTest` + * To run this test: `atest FlickerTestsOther:RotateSplitNoChangeTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt index 4bc17ed12b77..6be78f829b34 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.activityembedding +package com.android.server.wm.flicker.activityembedding.rtl import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -22,6 +22,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.FixMethodOrder import org.junit.Test @@ -36,7 +37,7 @@ import org.junit.runners.Parameterized * PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. Expect split * PlaceholderSecondary|PlaceholderPrimary covering split B|A. * - * To run this test: `atest FlickerTests:RTLStartSecondaryWithPlaceholderTest` + * To run this test: `atest FlickerTestsOther:RTLStartSecondaryWithPlaceholderTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt new file mode 100644 index 000000000000..87231c86ef19 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.activityembedding.splitscreen + +import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.RequiresDevice +import android.tools.common.datatypes.Rect +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import android.tools.device.traces.parsers.toFlickerComponent +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.wm.shell.flicker.utils.* +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/*** + * Test entering System SplitScreen with Activity Embedding Split and another app. + * + * Setup: Launch A|B in split and secondaryApp, return to home. + * Transitions: Let AE Split A|B enter splitscreen with secondaryApp. Resulting in A|B|secondaryApp. + * + * To run this test: `atest FlickerTestsOther:EnterSystemSplitTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class EnterSystemSplitTest(flicker: LegacyFlickerTest) : + ActivityEmbeddingTestBase(flicker) { + + private val secondaryApp = SplitScreenUtils.getPrimary(instrumentation) + override val transition: FlickerBuilder.() -> Unit = { + setup { + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivity(wmHelper) + secondaryApp.launchViaIntent(wmHelper) + tapl.goHome() + wmHelper + .StateSyncBuilder() + .withAppTransitionIdle() + .withHomeActivityVisible() + .waitForAndVerify() + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds ?: + error("Display not found") + } + transitions { + SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp) + SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp) + } + } + + @Presubmit + @Test + fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible() + + @Presubmit + @Test + fun activityEmbeddingSplitLayerBecomesVisible() { + flicker.splitAppLayerBoundsIsVisibleAtEnd( + testApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false) + } + + @Presubmit + @Test + fun activityEmbeddingSplitWindowBecomesVisible() = flicker.appWindowIsVisibleAtEnd(testApp) + + @Presubmit + @Test + fun secondaryLayerBecomesVisible() { + flicker.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true) + } + + @Presubmit + @Test + fun secondaryAppWindowBecomesVisible() = flicker.appWindowIsVisibleAtEnd(secondaryApp) + + /** + * After the transition there should be both ActivityEmbedding activities, + * SplitScreenPrimaryActivity and the system split divider on screen. + * Verify the layers are in expected sizes. + */ + @Presubmit + @Test + fun activityEmbeddingSplitSurfaceAreEven() { + flicker.assertLayersEnd { + val leftAELayerRegion = + visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val rightAELayerRegion = + visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + val secondaryAppLayerRegion = + visibleRegion( + ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) + val systemDivider = visibleRegion(SPLIT_SCREEN_DIVIDER_COMPONENT) + leftAELayerRegion + .plus(rightAELayerRegion.region) + .plus(secondaryAppLayerRegion.region) + .plus(systemDivider.region) + .coversExactly(startDisplayBounds) + check { "ActivityEmbeddingSplitHeight" } + .that(leftAELayerRegion.region.height) + .isEqual(rightAELayerRegion.region.height) + check { "SystemSplitHeight" } + .that(rightAELayerRegion.region.height) + .isEqual(secondaryAppLayerRegion.region.height) + // TODO(b/292283182): Remove this special case handling. + check { "ActivityEmbeddingSplitWidth" } + .that(Math.abs( + leftAELayerRegion.region.width - rightAELayerRegion.region.width)) + .isLower(2) + check { "SystemSplitWidth" } + .that(Math.abs(secondaryAppLayerRegion.region.width - + 2 * rightAELayerRegion.region.width)) + .isLower(2) + } + } + + /** + * Verify the windows are in expected sizes. + */ + @Presubmit + @Test + fun activityEmbeddingSplitWindowsAreEven() { + flicker.assertWmEnd { + val leftAEWindowRegion = + visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val rightAEWindowRegion = + visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + // There's no window for the divider bar. + val secondaryAppLayerRegion = + visibleRegion( + ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) + check { "ActivityEmbeddingSplitHeight" } + .that(leftAEWindowRegion.region.height) + .isEqual(rightAEWindowRegion.region.height) + check { "SystemSplitHeight" } + .that(rightAEWindowRegion.region.height) + .isEqual(secondaryAppLayerRegion.region.height) + check { "ActivityEmbeddingSplitWidth" } + .that(Math.abs( + leftAEWindowRegion.region.width - rightAEWindowRegion.region.width)) + .isLower(2) + check { "SystemSplitWidth" } + .that(Math.abs(secondaryAppLayerRegion.region.width - + 2 * rightAEWindowRegion.region.width)) + .isLower(2) + } + } + + @Ignore("Not applicable to this CUJ.") + override fun visibleLayersShownMoreThanOneConsecutiveEntry() {} + + companion object { + /** {@inheritDoc} */ + private var startDisplayBounds = Rect.EMPTY + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() + } +}
\ No newline at end of file diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java new file mode 100644 index 000000000000..1b98887199e3 --- /dev/null +++ b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.view.InputChannel; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; +import android.view.MotionEvent.PointerProperties; +import android.view.ViewConfiguration; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Build/Install/Run: + * atest FocusEventDebugViewTest + */ +@RunWith(AndroidJUnit4.class) +public class FocusEventDebugViewTest { + + private FocusEventDebugView mFocusEventDebugView; + private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView; + private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView; + private float mScaledVerticalScrollFactor; + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getContext(); + mScaledVerticalScrollFactor = + ViewConfiguration.get(context).getScaledVerticalScrollFactor(); + InputManagerService mockService = mock(InputManagerService.class); + when(mockService.monitorInput(anyString(), anyInt())) + .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]); + + mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context); + mRotaryInputGraphView = new FocusEventDebugView.RotaryInputGraphView(context); + mFocusEventDebugView = new FocusEventDebugView(context, mockService, + () -> mRotaryInputValueView, () -> mRotaryInputGraphView); + } + + @Test + public void startsRotaryInputValueViewWithDefaultValue() { + assertEquals("+0.0", mRotaryInputValueView.getText()); + } + + @Test + public void startsRotaryInputGraphViewWithDefaultFrameCenter() { + assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01); + } + + @Test + public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() { + mFocusEventDebugView.handleUpdateShowRotaryInput(true); + + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f)); + + assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor), + mRotaryInputValueView.getText()); + } + + @Test + public void handleRotaryInput_translatesRotaryInputGraphViewWithHighScrollValue() { + mFocusEventDebugView.handleUpdateShowRotaryInput(true); + + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f)); + + assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0); + } + + @Test + public void updateActivityStatus_setsAndRemovesColorFilter() { + // It should not be active initially. + assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + + mRotaryInputValueView.updateActivityStatus(true); + // It should be active after rotary input. + assertNotNull(mRotaryInputValueView.getBackground().getColorFilter()); + + mRotaryInputValueView.updateActivityStatus(false); + // It should not be active after waiting for mUpdateActivityStatusCallback. + assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + } + + private MotionEvent createRotaryMotionEvent(float scrollAxisValue) { + PointerCoords pointerCoords = new PointerCoords(); + pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue); + PointerProperties pointerProperties = new PointerProperties(); + + return MotionEvent.obtain( + /* downTime */ 0, + /* eventTime */ 0, + /* action */ MotionEvent.ACTION_SCROLL, + /* pointerCount */ 1, + /* pointerProperties */ new PointerProperties[] {pointerProperties}, + /* pointerCoords */ new PointerCoords[] {pointerCoords}, + /* metaState */ 0, + /* buttonState */ 0, + /* xPrecision */ 0, + /* yPrecision */ 0, + /* deviceId */ 0, + /* edgeFlags */ 0, + /* source */ InputDevice.SOURCE_ROTARY_ENCODER, + /* flags */ 0 + ); + } +} diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt index 31ea8327c2b3..c92d768439fd 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt @@ -16,10 +16,6 @@ package com.android.test.silkfx.hdr -import android.animation.AnimatorSet -import android.animation.ObjectAnimator -import android.animation.ValueAnimator -import android.animation.ValueAnimator.AnimatorUpdateListener import android.content.Context import android.graphics.Bitmap import android.graphics.Canvas @@ -46,7 +42,7 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context private var selectedImage = -1 private var outputMode = R.id.output_hdr private var bitmap: Bitmap? = null - private var gainmap: Gainmap? = null + private var originalGainmap: Gainmap? = null private var gainmapVisualizer: Bitmap? = null private lateinit var imageView: SubsamplingScaleImageView private lateinit var gainmapMetadataEditor: GainmapMetadataEditor @@ -70,7 +66,6 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context it.check(outputMode) it.setOnCheckedChangeListener { _, checkedId -> outputMode = checkedId - // Intentionally don't do anything fancy so that mode A/B comparisons are easy updateDisplay() } } @@ -101,41 +96,10 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context imageView.apply { isClickable = true - // Example of animating between SDR and HDR using gainmap params; animates HDR->SDR->HDR - // with a brief pause on SDR. The key thing here is that the gainmap's - // minDisplayRatioForHdrTransition is animated between its original value (for full HDR) - // and displayRatioForFullHdr (for full SDR). The view must also be invalidated during - // the animation for the updates to take effect. setOnClickListener { - if (gainmap != null && (outputMode == R.id.output_hdr || - outputMode == R.id.output_hdr_test)) { - val animationLengthMs: Long = 500 - val updateListener = object : AnimatorUpdateListener { - override fun onAnimationUpdate(animation: ValueAnimator) { - imageView.invalidate() - } - } - val hdrToSdr = ObjectAnimator.ofFloat( - gainmap, "minDisplayRatioForHdrTransition", - gainmap!!.minDisplayRatioForHdrTransition, - gainmap!!.displayRatioForFullHdr).apply { - duration = animationLengthMs - addUpdateListener(updateListener) - } - val sdrToHdr = ObjectAnimator.ofFloat( - gainmap, "minDisplayRatioForHdrTransition", - gainmap!!.displayRatioForFullHdr, - gainmap!!.minDisplayRatioForHdrTransition).apply { - duration = animationLengthMs - addUpdateListener(updateListener) - } - - AnimatorSet().apply { - play(hdrToSdr) - play(sdrToHdr).after(animationLengthMs) - start() - } - } + animate().alpha(.5f).withEndAction { + animate().alpha(1f).start() + }.start() } } } @@ -149,7 +113,7 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context } private fun doDecode(source: ImageDecoder.Source) { - gainmap = null + originalGainmap = null bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, source -> decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE } @@ -167,9 +131,10 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context findViewById<TextView>(R.id.error_msg)!!.visibility = View.GONE findViewById<RadioGroup>(R.id.output_mode)!!.visibility = View.VISIBLE - gainmap = bitmap!!.gainmap - gainmapMetadataEditor.setGainmap(gainmap) - val map = gainmap!!.gainmapContents + val gainmap = bitmap!!.gainmap!! + originalGainmap = gainmap + gainmapMetadataEditor.setGainmap(Gainmap(gainmap, gainmap.gainmapContents)) + val map = gainmap.gainmapContents if (map.config != Bitmap.Config.ALPHA_8) { gainmapVisualizer = map } else { @@ -198,14 +163,12 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context imageView.setImage(ImageSource.cachedBitmap(when (outputMode) { R.id.output_hdr -> { - gainmapMetadataEditor.useOriginalMetadata() - bitmap!!.gainmap = gainmap + bitmap!!.gainmap = originalGainmap bitmap!! } R.id.output_hdr_test -> { - gainmapMetadataEditor.useEditMetadata() - bitmap!!.gainmap = gainmap + bitmap!!.gainmap = gainmapMetadataEditor.editedGainmap() bitmap!! } diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt index 8a653045c97b..c4bc6001533e 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt @@ -28,23 +28,27 @@ import android.widget.TextView import com.android.test.silkfx.R data class GainmapMetadata( - var ratioMin: Float, - var ratioMax: Float, - var capacityMin: Float, - var capacityMax: Float, - var gamma: Float, - var offsetSdr: Float, - var offsetHdr: Float + var ratioMin: Float, + var ratioMax: Float, + var capacityMin: Float, + var capacityMax: Float, + var gamma: Float, + var offsetSdr: Float, + var offsetHdr: Float ) +/** + * Note: This can only handle single-channel gainmaps nicely. It will force all 3-channel + * metadata to have the same value single value and is not intended to be a robust demonstration + * of gainmap metadata editing + */ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { - private var gainmap: Gainmap? = null - private var showingEdits = false + private lateinit var gainmap: Gainmap private var metadataPopup: PopupWindow? = null private var originalMetadata: GainmapMetadata = GainmapMetadata( - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f) + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f) private var currentMetadata: GainmapMetadata = originalMetadata.copy() private val maxProgress = 100.0f @@ -61,23 +65,18 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { private val maxGamma = 3.0f // Min and max offsets are 0.0 and 1.0 respectively - fun setGainmap(newGainmap: Gainmap?) { + fun setGainmap(newGainmap: Gainmap) { gainmap = newGainmap - originalMetadata = GainmapMetadata(gainmap!!.getRatioMin()[0], - gainmap!!.getRatioMax()[0], gainmap!!.getMinDisplayRatioForHdrTransition(), - gainmap!!.getDisplayRatioForFullHdr(), gainmap!!.getGamma()[0], - gainmap!!.getEpsilonSdr()[0], gainmap!!.getEpsilonHdr()[0]) + originalMetadata = GainmapMetadata(gainmap.getRatioMin()[0], + gainmap.getRatioMax()[0], gainmap.getMinDisplayRatioForHdrTransition(), + gainmap.getDisplayRatioForFullHdr(), gainmap.getGamma()[0], + gainmap.getEpsilonSdr()[0], gainmap.getEpsilonHdr()[0]) currentMetadata = originalMetadata.copy() } - fun useOriginalMetadata() { - showingEdits = false - applyMetadata(originalMetadata) - } - - fun useEditMetadata() { - showingEdits = true + fun editedGainmap(): Gainmap { applyMetadata(currentMetadata) + return gainmap } fun closeEditor() { @@ -93,7 +92,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gainmap_metadata, null) metadataPopup = PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT) + ViewGroup.LayoutParams.WRAP_CONTENT) metadataPopup!!.showAtLocation(view, Gravity.CENTER, 0, 0) (view.getParent() as ViewGroup).removeView(view) @@ -117,7 +116,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr) val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr) arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek, - offsetSdrSeek, offsetHdrSeek).forEach { + offsetSdrSeek, offsetHdrSeek).forEach { it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { if (!fromUser) return @@ -149,37 +148,37 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr) gainmapMinSeek.setProgress( - ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt()) + ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt()) gainmapMaxSeek.setProgress( - ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt()) + ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt()) capacityMinSeek.setProgress( - ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt()) + ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt()) capacityMaxSeek.setProgress( - ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt()) + ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt()) gammaSeek.setProgress( - ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt()) + ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt()) // Log base 3 via: log_b(x) = log_y(x) / log_y(b) offsetSdrSeek.setProgress( - ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0) - .toFloat() * maxProgress).toInt()) + ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0) + .toFloat() * maxProgress).toInt()) offsetHdrSeek.setProgress( - ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0) - .toFloat() * maxProgress).toInt()) + ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0) + .toFloat() * maxProgress).toInt()) parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText( - "%.3f".format(currentMetadata.ratioMin)) + "%.3f".format(currentMetadata.ratioMin)) parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText( - "%.3f".format(currentMetadata.ratioMax)) + "%.3f".format(currentMetadata.ratioMax)) parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText( - "%.3f".format(currentMetadata.capacityMin)) + "%.3f".format(currentMetadata.capacityMin)) parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText( - "%.3f".format(currentMetadata.capacityMax)) + "%.3f".format(currentMetadata.capacityMax)) parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText( - "%.3f".format(currentMetadata.gamma)) + "%.3f".format(currentMetadata.gamma)) parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText( - "%.5f".format(currentMetadata.offsetSdr)) + "%.5f".format(currentMetadata.offsetSdr)) parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText( - "%.5f".format(currentMetadata.offsetHdr)) + "%.5f".format(currentMetadata.offsetHdr)) } private fun resetGainmapMetadata() { @@ -189,69 +188,59 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { } private fun applyMetadata(newMetadata: GainmapMetadata) { - gainmap!!.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin) - gainmap!!.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax) - gainmap!!.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin) - gainmap!!.setDisplayRatioForFullHdr(newMetadata.capacityMax) - gainmap!!.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma) - gainmap!!.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr) - gainmap!!.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr) + gainmap.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin) + gainmap.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax) + gainmap.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin) + gainmap.setDisplayRatioForFullHdr(newMetadata.capacityMax) + gainmap.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma) + gainmap.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr) + gainmap.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr) renderView.invalidate() } private fun updateGainmapMin(normalized: Float) { val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin) parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText( - "%.3f".format(newValue)) + "%.3f".format(newValue)) currentMetadata.ratioMin = newValue - if (showingEdits) { - gainmap!!.setRatioMin(newValue, newValue, newValue) - renderView.invalidate() - } + gainmap.setRatioMin(newValue, newValue, newValue) + renderView.invalidate() } private fun updateGainmapMax(normalized: Float) { val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax) parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText( - "%.3f".format(newValue)) + "%.3f".format(newValue)) currentMetadata.ratioMax = newValue - if (showingEdits) { - gainmap!!.setRatioMax(newValue, newValue, newValue) - renderView.invalidate() - } + gainmap.setRatioMax(newValue, newValue, newValue) + renderView.invalidate() } private fun updateCapacityMin(normalized: Float) { val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin) parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText( - "%.3f".format(newValue)) + "%.3f".format(newValue)) currentMetadata.capacityMin = newValue - if (showingEdits) { - gainmap!!.setMinDisplayRatioForHdrTransition(newValue) - renderView.invalidate() - } + gainmap.setMinDisplayRatioForHdrTransition(newValue) + renderView.invalidate() } private fun updateCapacityMax(normalized: Float) { val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax) parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText( - "%.3f".format(newValue)) + "%.3f".format(newValue)) currentMetadata.capacityMax = newValue - if (showingEdits) { - gainmap!!.setDisplayRatioForFullHdr(newValue) - renderView.invalidate() - } + gainmap.setDisplayRatioForFullHdr(newValue) + renderView.invalidate() } private fun updateGamma(normalized: Float) { val newValue = minGamma + normalized * (maxGamma - minGamma) parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText( - "%.3f".format(newValue)) + "%.3f".format(newValue)) currentMetadata.gamma = newValue - if (showingEdits) { - gainmap!!.setGamma(newValue, newValue, newValue) - renderView.invalidate() - } + gainmap.setGamma(newValue, newValue, newValue) + renderView.invalidate() } private fun updateOffsetSdr(normalized: Float) { @@ -260,12 +249,10 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat() } parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText( - "%.5f".format(newValue)) + "%.5f".format(newValue)) currentMetadata.offsetSdr = newValue - if (showingEdits) { - gainmap!!.setEpsilonSdr(newValue, newValue, newValue) - renderView.invalidate() - } + gainmap.setEpsilonSdr(newValue, newValue, newValue) + renderView.invalidate() } private fun updateOffsetHdr(normalized: Float) { @@ -274,11 +261,9 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat() } parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText( - "%.5f".format(newValue)) + "%.5f".format(newValue)) currentMetadata.offsetHdr = newValue - if (showingEdits) { - gainmap!!.setEpsilonHdr(newValue, newValue, newValue) - renderView.invalidate() - } + gainmap.setEpsilonHdr(newValue, newValue, newValue) + renderView.invalidate() } } diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java index ba12acb2c877..2b605c594ce8 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java @@ -18,6 +18,7 @@ package com.google.android.test.windowinsetstests; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; + import static java.lang.Math.max; import static java.lang.Math.min; @@ -41,11 +42,11 @@ import android.view.WindowInsetsAnimationController; import android.view.animation.LinearInterpolator; import android.widget.LinearLayout; +import androidx.appcompat.app.AppCompatActivity; + import java.util.ArrayList; import java.util.List; -import androidx.appcompat.app.AppCompatActivity; - public class ChatActivity extends AppCompatActivity { private View mRoot; @@ -148,7 +149,7 @@ public class ChatActivity extends AppCompatActivity { inset = min(inset, shown); mAnimationController.setInsetsAndAlpha( Insets.of(0, 0, 0, inset), - 1f, (inset - start) / (float)(end - start)); + 1f, start == end ? 1f : (inset - start) / (float) (end - start)); } }); diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index d02fd83df6af..51cf38bce64e 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -353,8 +353,8 @@ static void printCompatibleScreens(ResXMLTree& tree, String8* outError) { } static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1, - const String8& requiredFeature = String8::empty(), - const String8& requiredNotFeature = String8::empty()) { + const String8& requiredFeature = String8(), + const String8& requiredNotFeature = String8()) { printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string()); if (maxSdkVersion != -1) { printf(" maxSdkVersion='%d'", maxSdkVersion); diff --git a/tools/aapt2/trace/TraceBuffer.cpp b/tools/aapt2/trace/TraceBuffer.cpp index da5373936306..fab2df383e3f 100644 --- a/tools/aapt2/trace/TraceBuffer.cpp +++ b/tools/aapt2/trace/TraceBuffer.cpp @@ -36,13 +36,14 @@ constexpr char kBegin = 'B'; constexpr char kEnd = 'E'; struct TracePoint { + char type; pid_t tid; int64_t time; std::string tag; - char type; }; std::vector<TracePoint> traces; +bool enabled = true; int64_t GetTime() noexcept { auto now = std::chrono::steady_clock::now(); @@ -51,34 +52,39 @@ int64_t GetTime() noexcept { } // namespace anonymous -void AddWithTime(const std::string& tag, char type, int64_t time) noexcept { - TracePoint t = {getpid(), time, tag, type}; - traces.emplace_back(t); +void AddWithTime(std::string tag, char type, int64_t time) noexcept { + TracePoint t = {type, getpid(), time, std::move(tag)}; + traces.emplace_back(std::move(t)); } -void Add(const std::string& tag, char type) noexcept { - AddWithTime(tag, type, GetTime()); +void Add(std::string tag, char type) noexcept { + AddWithTime(std::move(tag), type, GetTime()); } - - - void Flush(const std::string& basePath) { - TRACE_CALL(); if (basePath.empty()) { return; } + BeginTrace(__func__); // We can't do much here, only record that it happened. - std::stringstream s; + std::ostringstream s; s << basePath << aapt::file::sDirSep << "report_aapt2_" << getpid() << ".json"; FILE* f = android::base::utf8::fopen(s.str().c_str(), "a"); if (f == nullptr) { return; } + // Wrap the trace in a JSON array [] to make Chrome/Perfetto UI handle it. + char delimiter = '['; for(const TracePoint& trace : traces) { - fprintf(f, "{\"ts\" : \"%" PRIu64 "\", \"ph\" : \"%c\", \"tid\" : \"%d\" , \"pid\" : \"%d\", " - "\"name\" : \"%s\" },\n", trace.time, trace.type, 0, trace.tid, trace.tag.c_str()); + fprintf(f, + "%c{\"ts\" : \"%" PRIu64 + "\", \"ph\" : \"%c\", \"tid\" : \"%d\" , \"pid\" : \"%d\", \"name\" : \"%s\" }\n", + delimiter, trace.time, trace.type, 0, trace.tid, trace.tag.c_str()); + delimiter = ','; + } + if (!traces.empty()) { + fprintf(f, "]"); } fclose(f); traces.clear(); @@ -86,66 +92,82 @@ void Flush(const std::string& basePath) { } // namespace tracebuffer -void BeginTrace(const std::string& tag) { - tracebuffer::Add(tag, tracebuffer::kBegin); +void BeginTrace(std::string tag) { + if (!tracebuffer::enabled) return; + tracebuffer::Add(std::move(tag), tracebuffer::kBegin); +} + +void EndTrace(std::string tag) { + if (!tracebuffer::enabled) return; + tracebuffer::Add(std::move(tag), tracebuffer::kEnd); } -void EndTrace() { - tracebuffer::Add("", tracebuffer::kEnd); +bool Trace::enable(bool value) { + return tracebuffer::enabled = value; } -Trace::Trace(const std::string& tag) { - tracebuffer::Add(tag, tracebuffer::kBegin); +Trace::Trace(const char* tag) { + if (!tracebuffer::enabled) return; + tag_.assign(tag); + tracebuffer::Add(tag_, tracebuffer::kBegin); } -Trace::Trace(const std::string& tag, const std::vector<android::StringPiece>& args) { - std::stringstream s; +Trace::Trace(std::string tag) : tag_(std::move(tag)) { + if (!tracebuffer::enabled) return; + tracebuffer::Add(tag_, tracebuffer::kBegin); +} + +template <class SpanOfStrings> +std::string makeTag(std::string_view tag, const SpanOfStrings& args) { + std::ostringstream s; s << tag; - s << " "; - for (auto& arg : args) { - s << arg; - s << " "; + if (!args.empty()) { + for (const auto& arg : args) { + s << ' '; + s << arg; + } } - tracebuffer::Add(s.str(), tracebuffer::kBegin); + return std::move(s).str(); +} + +Trace::Trace(std::string_view tag, const std::vector<android::StringPiece>& args) { + if (!tracebuffer::enabled) return; + tag_ = makeTag(tag, args); + tracebuffer::Add(tag_, tracebuffer::kBegin); } Trace::~Trace() { - tracebuffer::Add("", tracebuffer::kEnd); + if (!tracebuffer::enabled) return; + tracebuffer::Add(std::move(tag_), tracebuffer::kEnd); } -FlushTrace::FlushTrace(const std::string& basepath, const std::string& tag) - : basepath_(basepath) { - tracebuffer::Add(tag, tracebuffer::kBegin); +FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag) { + if (!Trace::enable(!basepath.empty())) return; + basepath_.assign(basepath); + tag_.assign(tag); + tracebuffer::Add(tag_, tracebuffer::kBegin); } -FlushTrace::FlushTrace(const std::string& basepath, const std::string& tag, - const std::vector<android::StringPiece>& args) : basepath_(basepath) { - std::stringstream s; - s << tag; - s << " "; - for (auto& arg : args) { - s << arg; - s << " "; - } - tracebuffer::Add(s.str(), tracebuffer::kBegin); +FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag, + const std::vector<android::StringPiece>& args) { + if (!Trace::enable(!basepath.empty())) return; + basepath_.assign(basepath); + tag_ = makeTag(tag, args); + tracebuffer::Add(tag_, tracebuffer::kBegin); } -FlushTrace::FlushTrace(const std::string& basepath, const std::string& tag, - const std::vector<std::string>& args) : basepath_(basepath){ - std::stringstream s; - s << tag; - s << " "; - for (auto& arg : args) { - s << arg; - s << " "; - } - tracebuffer::Add(s.str(), tracebuffer::kBegin); +FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag, + const std::vector<std::string>& args) { + if (!Trace::enable(!basepath.empty())) return; + basepath_.assign(basepath); + tag_ = makeTag(tag, args); + tracebuffer::Add(tag_, tracebuffer::kBegin); } FlushTrace::~FlushTrace() { - tracebuffer::Add("", tracebuffer::kEnd); + if (!tracebuffer::enabled) return; + tracebuffer::Add(tag_, tracebuffer::kEnd); tracebuffer::Flush(basepath_); } -} // namespace aapt - +} // namespace aapt diff --git a/tools/aapt2/trace/TraceBuffer.h b/tools/aapt2/trace/TraceBuffer.h index ba751dd72f41..f0333d1fe071 100644 --- a/tools/aapt2/trace/TraceBuffer.h +++ b/tools/aapt2/trace/TraceBuffer.h @@ -17,41 +17,50 @@ #ifndef AAPT_TRACEBUFFER_H #define AAPT_TRACEBUFFER_H +#include <androidfw/StringPiece.h> + #include <string> +#include <string_view> #include <vector> -#include <androidfw/StringPiece.h> - namespace aapt { // Record timestamps for beginning and end of a task and generate systrace json fragments. // This is an in-process ftrace which has the advantage of being platform independent. // These methods are NOT thread-safe since aapt2 is not multi-threaded. -// Convenience RIAA object to automatically finish an event when object goes out of scope. +// Convenience RAII object to automatically finish an event when object goes out of scope. class Trace { public: - Trace(const std::string& tag); - Trace(const std::string& tag, const std::vector<android::StringPiece>& args); - ~Trace(); + Trace(const char* tag); + Trace(std::string tag); + Trace(std::string_view tag, const std::vector<android::StringPiece>& args); + ~Trace(); + + static bool enable(bool value = true); + +private: + std::string tag_; }; // Manual markers. -void BeginTrace(const std::string& tag); -void EndTrace(); +void BeginTrace(std::string tag); +void EndTrace(std::string tag); // A main trace is required to flush events to disk. Events are formatted in systrace // json format. class FlushTrace { public: - explicit FlushTrace(const std::string& basepath, const std::string& tag); - explicit FlushTrace(const std::string& basepath, const std::string& tag, - const std::vector<android::StringPiece>& args); - explicit FlushTrace(const std::string& basepath, const std::string& tag, - const std::vector<std::string>& args); - ~FlushTrace(); + explicit FlushTrace(std::string_view basepath, std::string_view tag); + explicit FlushTrace(std::string_view basepath, std::string_view tag, + const std::vector<android::StringPiece>& args); + explicit FlushTrace(std::string_view basepath, std::string_view tag, + const std::vector<std::string>& args); + ~FlushTrace(); + private: std::string basepath_; + std::string tag_; }; #define TRACE_CALL() Trace __t(__func__) diff --git a/tools/hiddenapi/OWNERS b/tools/hiddenapi/OWNERS index afbeef5a0b41..dc82aac9d41c 100644 --- a/tools/hiddenapi/OWNERS +++ b/tools/hiddenapi/OWNERS @@ -1,5 +1,4 @@ # compat-team@ for changes to hiddenapi files -andreionea@google.com mathewi@google.com satayev@google.com diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java index 5ad3ede8498d..c828de9f221d 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java @@ -196,7 +196,7 @@ public final class SharedConnectivitySettingsState implements Parcelable { public String toString() { return new StringBuilder("SharedConnectivitySettingsState[") .append("instantTetherEnabled=").append(mInstantTetherEnabled) - .append("PendingIntent=").append(mInstantTetherSettingsPendingIntent.toString()) + .append("PendingIntent=").append(mInstantTetherSettingsPendingIntent) .append("extras=").append(mExtras.toString()) .append("]").toString(); } |