diff options
1321 files changed, 25196 insertions, 9438 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 95b61559e839..433cd50bdc13 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -21,6 +21,7 @@ aconfig_srcjars = [ ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}", ":android.security.flags-aconfig-java{.generated_srcjars}", ":android.view.flags-aconfig-java{.generated_srcjars}", + ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}", ":camera_platform_flags_core_java_lib{.generated_srcjars}", ":com.android.window.flags.window-aconfig-java{.generated_srcjars}", ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}", @@ -36,10 +37,13 @@ aconfig_srcjars = [ ":android.permission.flags-aconfig-java{.generated_srcjars}", ":hwui_flags_java_lib{.generated_srcjars}", ":display_flags_lib{.generated_srcjars}", + ":com.android.internal.foldables.flags-aconfig-java{.generated_srcjars}", ":android.multiuser.flags-aconfig-java{.generated_srcjars}", ":android.app.flags-aconfig-java{.generated_srcjars}", ":android.credentials.flags-aconfig-java{.generated_srcjars}", ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}", + ":android.service.voice.flags-aconfig-java{.generated_srcjars}", + ":android.service.autofill.flags-aconfig-java{.generated_srcjars}", ] filegroup { @@ -240,6 +244,29 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +cc_aconfig_library { + name: "aconfig_view_flags_c_lib", + aconfig_declarations: "android.view.flags-aconfig", +} + +// View.accessibility +aconfig_declarations { + name: "android.view.accessibility.flags-aconfig", + package: "android.view.accessibility", + srcs: ["core/java/android/view/accessibility/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.view.accessibility.flags-aconfig-java", + aconfig_declarations: "android.view.accessibility.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +cc_aconfig_library { + name: "aconfig_view_accessibility_flags_c_lib", + aconfig_declarations: "android.view.accessibility.flags-aconfig", +} + // Widget aconfig_declarations { name: "android.widget.flags-aconfig", @@ -319,6 +346,12 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +java_aconfig_library { + name: "com.android.internal.foldables.flags-aconfig-java", + aconfig_declarations: "fold_lock_setting_flags", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Multi user aconfig_declarations { name: "android.multiuser.flags-aconfig", @@ -383,3 +416,32 @@ java_aconfig_library { aconfig_declarations: "android.view.contentprotection.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// Voice +aconfig_declarations { + name: "android.service.voice.flags-aconfig", + package: "android.service.voice.flags", + srcs: ["core/java/android/service/voice/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.service.voice.flags-aconfig-java", + aconfig_declarations: "android.service.voice.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +// Autofill +aconfig_declarations { + name: "android.service.autofill.flags-aconfig", + package: "android.service.autofill", + srcs: [ + "services/autofill/bugfixes.aconfig", + "services/autofill/features.aconfig" + ], +} + +java_aconfig_library { + name: "android.service.autofill.flags-aconfig-java", + aconfig_declarations: "android.service.autofill.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Android.bp b/Android.bp index b1b332a9a2b5..a507465aa419 100644 --- a/Android.bp +++ b/Android.bp @@ -64,6 +64,7 @@ filegroup { srcs: [ // Java/AIDL sources under frameworks/base ":framework-annotations", + ":ravenwood-annotations", ":framework-blobstore-sources", ":framework-core-sources", ":framework-drm-sources", @@ -284,6 +285,7 @@ java_defaults { enforce_permissions_exceptions: [ // Do not add entries to this list. ":framework-annotations", + ":ravenwood-annotations", ":framework-blobstore-sources", ":framework-core-sources", ":framework-drm-sources", @@ -409,7 +411,6 @@ java_defaults { "audiopolicy-aidl-java", "sounddose-aidl-java", "modules-utils-expresslog", - "hoststubgen-annotations", ], } @@ -838,4 +839,5 @@ build = [ "AconfigFlags.bp", "ProtoLibraries.bp", "TestProtoLibraries.bp", + "Ravenwood.bp", ] @@ -34,3 +34,6 @@ per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS per-file SQLITE_OWNERS = file:/SQLITE_OWNERS + +per-file *ravenwood* = file:ravenwood/OWNERS +per-file *Ravenwood* = file:ravenwood/OWNERS diff --git a/Ravenwood.bp b/Ravenwood.bp new file mode 100644 index 000000000000..9218cc9bc3f8 --- /dev/null +++ b/Ravenwood.bp @@ -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. + +// We need this "trampoline" rule to force soong to give a host-side jar to +// framework-minus-apex.ravenwood. Otherwise, soong would mix up the arch (?) and we'd get +// a dex jar. +java_library { + name: "framework-minus-apex-for-hoststubgen", + installable: false, // host only jar. + static_libs: [ + "framework-minus-apex", + ], + sdk_version: "core_platform", + visibility: ["//visibility:private"], +} + +// Generate the stub/impl from framework-all, with hidden APIs. +java_genrule_host { + name: "framework-minus-apex.ravenwood-base", + tools: ["hoststubgen"], + cmd: "$(location hoststubgen) " + + "@$(location :ravenwood-standard-options) " + + + "--out-stub-jar $(location ravenwood_stub.jar) " + + "--out-impl-jar $(location ravenwood.jar) " + + + "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " + + "--gen-input-dump-file $(location hoststubgen_dump.txt) " + + + "--in-jar $(location :framework-minus-apex-for-hoststubgen) " + + "--policy-override-file $(location framework-minus-apex-ravenwood-policies.txt) ", + srcs: [ + ":framework-minus-apex-for-hoststubgen", + "framework-minus-apex-ravenwood-policies.txt", + ":ravenwood-standard-options", + ], + out: [ + "ravenwood.jar", + "ravenwood_stub.jar", // It's not used. TODO: Update hoststubgen to make it optional. + + // Following files are created just as FYI. + "hoststubgen_keep_all.txt", + "hoststubgen_dump.txt", + ], + visibility: ["//visibility:private"], +} + +// Extract the impl jar from "framework-minus-apex.ravenwood-base" for subsequent build rules. +java_genrule_host { + name: "framework-minus-apex.ravenwood", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-minus-apex.ravenwood-base{ravenwood.jar}", + ], + out: [ + "framework-minus-apex.ravenwood.jar", + ], + visibility: ["//visibility:public"], +} diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp index a4a0b4b29200..887f7fe3a0e2 100644 --- a/apex/jobscheduler/service/Android.bp +++ b/apex/jobscheduler/service/Android.bp @@ -13,6 +13,10 @@ java_library { name: "service-jobscheduler", installable: true, + defaults: [ + "service-jobscheduler-aconfig-libraries", + ], + srcs: [ "java/**/*.java", ":framework-jobscheduler-shared-srcs", diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp new file mode 100644 index 000000000000..24ecd3d73814 --- /dev/null +++ b/apex/jobscheduler/service/aconfig/Android.bp @@ -0,0 +1,29 @@ +// JobScheduler +aconfig_declarations { + name: "service-job.flags-aconfig", + package: "com.android.server.job", + srcs: [ + "job.aconfig", + ], +} + +java_aconfig_library { + name: "service-jobscheduler-job.flags-aconfig-java", + aconfig_declarations: "service-job.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], + visibility: ["//frameworks/base:__subpackages__"], +} + +service_jobscheduler_aconfig_srcjars = [ + ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}", +] + +// Aconfig declarations and libraries for the core framework +java_defaults { + name: "service-jobscheduler-aconfig-libraries", + // Add java_aconfig_libraries to here to add them to the core framework + srcs: service_jobscheduler_aconfig_srcjars, + // Add aconfig-annotations-lib as a dependency for the optimization + libs: ["aconfig-annotations-lib"], + visibility: ["//frameworks/base:__subpackages__"], +} diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig new file mode 100644 index 000000000000..4e3cb7d83451 --- /dev/null +++ b/apex/jobscheduler/service/aconfig/job.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.job" + +flag { + name: "relax_prefetch_connectivity_constraint_only_on_charger" + namespace: "backstagepower" + description: "Only relax a prefetch job's connectivity constraint when the device is charging" + bug: "299329948" +}
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index f252a0ba48cf..158d914575c6 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -1030,6 +1030,12 @@ public class DeviceIdleController extends SystemService "light_idle_to_initial_flex"; private static final String KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX = "light_max_idle_to_flex"; private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor"; + private static final String KEY_LIGHT_IDLE_INCREASE_LINEARLY = + "light_idle_increase_linearly"; + private static final String KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = + "light_idle_linear_increase_factor_ms"; + private static final String KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = + "light_idle_flex_linear_increase_factor_ms"; private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to"; private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = "light_idle_maintenance_min_budget"; @@ -1079,6 +1085,10 @@ public class DeviceIdleController extends SystemService private long mDefaultLightIdleTimeoutMaxFlex = !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L; private float mDefaultLightIdleFactor = 2f; + private boolean mDefaultLightIdleIncreaseLinearly; + private long mDefaultLightIdleLinearIncreaseFactorMs = mDefaultLightIdleTimeout; + private long mDefaultLightIdleFlexLinearIncreaseFactorMs = + mDefaultLightIdleTimeoutInitialFlex; private long mDefaultLightMaxIdleTimeout = !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L; private long mDefaultLightIdleMaintenanceMinBudget = @@ -1174,6 +1184,37 @@ public class DeviceIdleController extends SystemService public float LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor; /** + * Whether to increase the light idle mode time linearly or exponentially. + * If true, will increase linearly + * (i.e. {@link #LIGHT_IDLE_TIMEOUT} + x * {@link #LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS}). + * If false, will increase by exponentially + * (i.e. {@link #LIGHT_IDLE_TIMEOUT} * ({@link #LIGHT_IDLE_FACTOR} ^ x)). + * This will also impact how the light idle flex value + * ({@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX}) is increased (using + * {@link #LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS} for the linear increase).. + * + * @see #KEY_LIGHT_IDLE_INCREASE_LINEARLY + */ + public boolean LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly; + + /** + * Amount of time to increase the light idle time by, if increasing it linearly. + * + * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS + * @see #LIGHT_IDLE_INCREASE_LINEARLY + */ + public long LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs; + + /** + * Amount of time to increase the light idle flex time by, if increasing it linearly. + * + * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS + * @see #LIGHT_IDLE_INCREASE_LINEARLY + */ + public long LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = + mDefaultLightIdleFlexLinearIncreaseFactorMs; + + /** * This is the maximum time we will stay in light idle mode. * * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT @@ -1409,6 +1450,16 @@ public class DeviceIdleController extends SystemService mDefaultLightIdleTimeoutMaxFlex); mDefaultLightIdleFactor = res.getFloat( com.android.internal.R.integer.device_idle_light_idle_factor); + mDefaultLightIdleIncreaseLinearly = res.getBoolean( + com.android.internal.R.bool.device_idle_light_idle_increase_linearly); + mDefaultLightIdleLinearIncreaseFactorMs = getTimeout(res.getInteger( + com.android.internal.R.integer + .device_idle_light_idle_linear_increase_factor_ms), + mDefaultLightIdleLinearIncreaseFactorMs); + mDefaultLightIdleFlexLinearIncreaseFactorMs = getTimeout(res.getInteger( + com.android.internal.R.integer + .device_idle_light_idle_flex_linear_increase_factor_ms), + mDefaultLightIdleFlexLinearIncreaseFactorMs); mDefaultLightMaxIdleTimeout = getTimeout( res.getInteger(com.android.internal.R.integer.device_idle_light_max_idle_to_ms), mDefaultLightMaxIdleTimeout); @@ -1487,6 +1538,9 @@ public class DeviceIdleController extends SystemService LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mDefaultLightIdleTimeoutInitialFlex; LIGHT_IDLE_TIMEOUT_MAX_FLEX = mDefaultLightIdleTimeoutMaxFlex; LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor; + LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly; + LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs; + LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleFlexLinearIncreaseFactorMs; LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout; LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget; LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget; @@ -1556,6 +1610,21 @@ public class DeviceIdleController extends SystemService LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat( KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor)); break; + case KEY_LIGHT_IDLE_INCREASE_LINEARLY: + LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean( + KEY_LIGHT_IDLE_INCREASE_LINEARLY, + mDefaultLightIdleIncreaseLinearly); + break; + case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS: + LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong( + KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS, + mDefaultLightIdleLinearIncreaseFactorMs); + break; + case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS: + LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong( + KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS, + mDefaultLightIdleFlexLinearIncreaseFactorMs); + break; case KEY_LIGHT_MAX_IDLE_TIMEOUT: LIGHT_MAX_IDLE_TIMEOUT = properties.getLong( KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout); @@ -1716,6 +1785,20 @@ public class DeviceIdleController extends SystemService pw.print(LIGHT_IDLE_FACTOR); pw.println(); + pw.print(" "); pw.print(KEY_LIGHT_IDLE_INCREASE_LINEARLY); pw.print("="); + pw.print(LIGHT_IDLE_INCREASE_LINEARLY); + pw.println(); + + pw.print(" "); pw.print(KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS); + pw.print("="); + pw.print(LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS); + pw.println(); + + pw.print(" "); pw.print(KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS); + pw.print("="); + pw.print(LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS); + pw.println(); + pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT, pw); pw.println(); @@ -3694,10 +3777,18 @@ public class DeviceIdleController extends SystemService } mMaintenanceStartTime = 0; scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex, true); - mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT, - (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR)); - mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX, - (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR)); + if (!mConstants.LIGHT_IDLE_INCREASE_LINEARLY) { + mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT, + (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR)); + mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX, + (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR)); + } else { + mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT, + mNextLightIdleDelay + mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS); + mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX, + mNextLightIdleDelayFlex + + mConstants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS); + } moveToLightStateLocked(LIGHT_STATE_IDLE, reason); addEvent(EVENT_LIGHT_IDLE, null); mGoingIdleWakeLock.acquire(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 43d2ae9e077b..f47766ed0393 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -1335,13 +1335,13 @@ public final class JobServiceContext implements ServiceConnection { private void handleOpTimeoutLocked() { switch (mVerb) { case VERB_BINDING: - onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true, + // The system may have been too busy. Don't drop the job or trigger an ANR. + onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true, /* texCounterMetricId */ "job_scheduler.value_cntr_w_uid_slow_app_response_binding", /* debugReason */ "timed out while binding", /* anrMessage */ "Timed out while trying to bind", - CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES, - mRunningJob.getUid())); + /* triggerAnr */ false); break; case VERB_STARTING: // Client unresponsive - wedged or failed to respond in time. We don't really diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 6d938debde10..45f15db93a0d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -22,6 +22,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_MET import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER; +import static com.android.server.job.Flags.relaxPrefetchConnectivityConstraintOnlyOnCharger; import android.annotation.NonNull; import android.annotation.Nullable; @@ -929,6 +931,11 @@ public final class ConnectivityController extends RestrictingController implemen // Need to at least know the estimated download bytes for a prefetch job. return false; } + if (relaxPrefetchConnectivityConstraintOnlyOnCharger()) { + if (!mService.isBatteryCharging()) { + return false; + } + } // See if we match after relaxing any unmetered request final NetworkCapabilities.Builder builder = @@ -1735,6 +1742,14 @@ public final class ConnectivityController extends RestrictingController implemen Predicate<JobStatus> predicate) { final long nowElapsed = sElapsedRealtimeClock.millis(); + pw.println("Aconfig flags:"); + pw.increaseIndent(); + pw.print(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER, + relaxPrefetchConnectivityConstraintOnlyOnCharger()); + pw.println(); + pw.decreaseIndent(); + pw.println(); + if (mRequestedWhitelistJobs.size() > 0) { pw.print("Requested standby exceptions:"); for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) { diff --git a/api/Android.bp b/api/Android.bp index 45e70719e6c0..222275f9a4e4 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -82,7 +82,6 @@ combined_apis { "framework-media", "framework-mediaprovider", "framework-ondevicepersonalization", - "framework-pdf", "framework-permission", "framework-permission-s", "framework-scheduling", diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp index fbcaa52f9bb4..8d8fc12ebc4d 100644 --- a/api/ApiDocs.bp +++ b/api/ApiDocs.bp @@ -195,6 +195,8 @@ framework_docs_only_args = " -android -manifest $(location :frameworks-base-core doc_defaults { name: "framework-docs-default", + sdk_version: "none", + system_modules: "none", libs: framework_docs_only_libs + [ "stub-annotations", "unsupportedappusage", @@ -209,6 +211,7 @@ doc_defaults { custom_template: "droiddoc-templates-sdk", resourcesdir: "docs/html/reference/images/", resourcesoutdir: "reference/android/images/", + lint_baseline: "javadoc-lint-baseline", hdf: [ "dac true", "sdk.codename O", diff --git a/api/api.go b/api/api.go index 83804c65d81f..8df6dab715ef 100644 --- a/api/api.go +++ b/api/api.go @@ -115,6 +115,7 @@ type defaultsProps struct { } type Bazel_module struct { + Label *string Bp2build_available *bool } type bazelProperties struct { @@ -141,6 +142,8 @@ type MergedTxtDefinition struct { ModuleTag string // public, system, module-lib or system-server Scope string + // True if there is a bp2build definition for this module + Bp2buildDefined bool } func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { @@ -153,8 +156,10 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { if txt.Scope != "public" { filename = txt.Scope + "-" + filename } + moduleName := ctx.ModuleName() + "-" + filename + props := genruleProps{} - props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) + props.Name = proptools.StringPtr(moduleName) props.Tools = []string{"metalava"} props.Out = []string{filename} props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)") @@ -172,7 +177,20 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { }, } props.Visibility = []string{"//visibility:public"} - ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable) + bazelProps := bazelProperties{ + &Bazel_module{ + Bp2build_available: proptools.BoolPtr(false), + }, + } + if txt.Bp2buildDefined { + moduleDir := ctx.ModuleDir() + if moduleDir == android.Bp2BuildTopLevel { + moduleDir = "" + } + label := fmt.Sprintf("//%s:%s", moduleDir, moduleName) + bazelProps.Label = &label + } + ctx.CreateModule(genrule.GenRuleFactory, &props, &bazelProps) } func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) { @@ -304,38 +322,43 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ tagSuffix := []string{".api.txt}", ".removed-api.txt}"} distFilename := []string{"android.txt", "android-removed.txt"} + bp2BuildDefined := []bool{true, false} for i, f := range []string{"current.txt", "removed.txt"} { textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-" + f, - Modules: bootclasspath, - ModuleTag: "{.public" + tagSuffix[i], - Scope: "public", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-" + f, + Modules: bootclasspath, + ModuleTag: "{.public" + tagSuffix[i], + Scope: "public", + Bp2buildDefined: bp2BuildDefined[i], }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-system-" + f, - Modules: bootclasspath, - ModuleTag: "{.system" + tagSuffix[i], - Scope: "system", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-" + f, + Modules: bootclasspath, + ModuleTag: "{.system" + tagSuffix[i], + Scope: "system", + Bp2buildDefined: bp2BuildDefined[i], }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-module-lib-" + f, - Modules: bootclasspath, - ModuleTag: "{.module-lib" + tagSuffix[i], - Scope: "module-lib", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-module-lib-" + f, + Modules: bootclasspath, + ModuleTag: "{.module-lib" + tagSuffix[i], + Scope: "module-lib", + Bp2buildDefined: bp2BuildDefined[i], }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-system-server-" + f, - Modules: system_server_classpath, - ModuleTag: "{.system-server" + tagSuffix[i], - Scope: "system-server", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-server-" + f, + Modules: system_server_classpath, + ModuleTag: "{.system-server" + tagSuffix[i], + Scope: "system-server", + Bp2buildDefined: bp2BuildDefined[i], }) } for _, txt := range textFiles { diff --git a/api/api_test.go b/api/api_test.go index 1f4c2af32493..70f2162348ad 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -33,6 +33,8 @@ func runCombinedApisTestCase(t *testing.T, tc bp2build.Bp2buildTestCase) { t.Helper() runCombinedApisTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_defaults", java.DefaultsFactory) + ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory) + ctx.RegisterModuleType("filegroup", android.FileGroupFactory) }) } @@ -44,6 +46,33 @@ func TestCombinedApisGeneral(t *testing.T) { bootclasspath: ["bcp"], system_server_classpath: ["ssc"], } + +java_sdk_library { + name: "bcp", + srcs: ["a.java", "b.java"], + shared_library: false, +} +java_sdk_library { + name: "ssc", + srcs: ["a.java", "b.java"], + shared_library: false, +} +filegroup { + name: "non-updatable-current.txt", + srcs: ["current.txt"], +} +filegroup { + name: "non-updatable-system-current.txt", + srcs: ["system-current.txt"], +} +filegroup { + name: "non-updatable-module-lib-current.txt", + srcs: ["system-removed.txt"], +} +filegroup { + name: "non-updatable-system-server-current.txt", + srcs: ["system-lint-baseline.txt"], +} `, Filesystem: map[string]string{ "a/Android.bp": ` @@ -51,27 +80,35 @@ func TestCombinedApisGeneral(t *testing.T) { name: "android.jar_defaults", } `, + "api/current.txt": "", + "api/removed.txt": "", + "api/system-current.txt": "", + "api/system-removed.txt": "", + "api/test-current.txt": "", + "api/test-removed.txt": "", }, + StubbedBuildDefinitions: []string{"bcp", "ssc", "non-updatable-current.txt", "non-updatable-system-current.txt", "non-updatable-module-lib-current.txt", "non-updatable-system-server-current.txt"}, + ExpectedHandcraftedModules: []string{"foo-current.txt", "foo-system-current.txt", "foo-module-lib-current.txt", "foo-system-server-current.txt"}, ExpectedBazelTargets: []string{ bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-current.txt", bp2build.AttrNameToString{ "scope": `"public"`, - "base": `":non-updatable-current.txt__BP2BUILD__MISSING__DEP"`, - "deps": `[":bcp__BP2BUILD__MISSING__DEP"]`, + "base": `":non-updatable-current.txt"`, + "deps": `[":bcp"]`, }), bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-current.txt", bp2build.AttrNameToString{ "scope": `"system"`, - "base": `":non-updatable-system-current.txt__BP2BUILD__MISSING__DEP"`, - "deps": `[":bcp__BP2BUILD__MISSING__DEP"]`, + "base": `":non-updatable-system-current.txt"`, + "deps": `[":bcp"]`, }), bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-module-lib-current.txt", bp2build.AttrNameToString{ "scope": `"module-lib"`, - "base": `":non-updatable-module-lib-current.txt__BP2BUILD__MISSING__DEP"`, - "deps": `[":bcp__BP2BUILD__MISSING__DEP"]`, + "base": `":non-updatable-module-lib-current.txt"`, + "deps": `[":bcp"]`, }), bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-server-current.txt", bp2build.AttrNameToString{ "scope": `"system-server"`, - "base": `":non-updatable-system-server-current.txt__BP2BUILD__MISSING__DEP"`, - "deps": `[":ssc__BP2BUILD__MISSING__DEP"]`, + "base": `":non-updatable-system-server-current.txt"`, + "deps": `[":ssc"]`, }), }, }) diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline new file mode 100644 index 000000000000..2cc5078c8844 --- /dev/null +++ b/api/javadoc-lint-baseline @@ -0,0 +1,303 @@ +android/adservices/ondevicepersonalization/DownloadCompletedInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedInput [101] +android/adservices/ondevicepersonalization/DownloadCompletedOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedOutput [101] +android/adservices/ondevicepersonalization/EventLogRecord.java:13: lint: Unresolved link/see tag "RequestRecordRecord" in android.adservices.ondevicepersonalization.EventLogRecord [101] +android/adservices/ondevicepersonalization/EventUrlProvider.java:43: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onEvent IsolatedWorker#onEvent" in android.adservices.ondevicepersonalization.EventUrlProvider [101] +android/adservices/ondevicepersonalization/ExecuteInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteInput [101] +android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101] +android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute() OnDevicePersonalizationManager#execute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101] +android/adservices/ondevicepersonalization/ExecuteOutput.java:31: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101] +android/adservices/ondevicepersonalization/ExecuteOutput.java:93: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput.Builder [101] +android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.IsolatedService [101] +android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedService [101] +android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onWebViewEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101] +android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101] +android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "WebView" in android.adservices.ondevicepersonalization.IsolatedService [101] +android/adservices/ondevicepersonalization/IsolatedWorker.java:9: lint: Unresolved link/see tag "RunTimeException" in android.adservices.ondevicepersonalization.IsolatedWorker [101] +android/adservices/ondevicepersonalization/IsolatedWorker.java:24: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedWorker [101] +android/adservices/ondevicepersonalization/IsolatedWorker.java:57: lint: Unresolved link/see tag "#onExecute()" in android.adservices.ondevicepersonalization.IsolatedWorker [101] +android/adservices/ondevicepersonalization/IsolatedWorker.java:74: lint: Unresolved link/see tag "#onRender()" in android.adservices.ondevicepersonalization.IsolatedWorker [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:-11: lint: Unresolved link/see tag "requestSurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedService#onExecute() IsolatedService#onExecute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:19: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView#getHostToken()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "execute" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "#execute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:64: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:69: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:70: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101] +android/adservices/ondevicepersonalization/RenderInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderInput [101] +android/adservices/ondevicepersonalization/RenderInput.java:53: lint: Unresolved link/see tag "onExecute" in android.adservices.ondevicepersonalization.RenderInput [101] +android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderOutput [101] +android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#requestSurfacePackage() OnDevicePersonalizationManager#requestSurfacePackage()" in android.adservices.ondevicepersonalization.RenderOutput [101] +android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101] +android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput [101] +android/adservices/ondevicepersonalization/RenderOutput.java:41: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput [101] +android/adservices/ondevicepersonalization/RenderOutput.java:52: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101] +android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101] +android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101] +android/adservices/ondevicepersonalization/RenderOutput.java:114: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101] +android/adservices/ondevicepersonalization/RenderOutput.java:127: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101] +android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.RenderingConfig [101] +android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderingConfig [101] +android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderingConfig [101] +android/adservices/ondevicepersonalization/RenderingConfig.java:33: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig [101] +android/adservices/ondevicepersonalization/RenderingConfig.java:85: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig.Builder [101] +android/adservices/ondevicepersonalization/RequestLogRecord.java:19: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RequestLogRecord [101] +android/adservices/ondevicepersonalization/SurfacePackageToken.java:20: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.SurfacePackageToken [101] +android/adservices/ondevicepersonalization/WebViewEventInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventInput [101] +android/adservices/ondevicepersonalization/WebViewEventInput.java:30: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.WebViewEventInput [101] +android/adservices/ondevicepersonalization/WebViewEventInput.java:41: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.EventUrlProvider#createEventTrackingUrlWithResponse() EventUrlProvider#createEventTrackingUrlWithResponse()" in android.adservices.ondevicepersonalization.WebViewEventInput [101] +android/adservices/ondevicepersonalization/WebViewEventOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventOutput [101] +android/app/ActivityOptions.java:366: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101] +android/app/ActivityOptions.java:370: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101] +android/app/ActivityOptions.java:384: lint: Unresolved link/see tag "android.app.ComponentOptions.BackgroundActivityStartMode" in android.app.ActivityOptions [101] +android/app/ApplicationStartInfo.java:96: lint: Unresolved link/see tag "#START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE" in android.app.ApplicationStartInfo [101] +android/app/BroadcastOptions.java:132: lint: Unresolved link/see tag "#setDeliveryGroupMatchingFilter(android.content.IntentFilter)" in android.app.BroadcastOptions [101] +android/app/GrammaticalInflectionManager.java:60: lint: Unresolved link/see tag "android.os.Environment#getDataSystemCeDirectory(int)" in android.app.GrammaticalInflectionManager [101] +android/app/Notification.java:509: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.app.Notification [101] +android/app/Notification.java:650: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.app.Notification [101] +android/app/Notification.java:1866: lint: Unresolved link/see tag "/*missing*/" in android.app.Notification.Action [101] +android/app/Notification.java:4796: lint: Unresolved link/see tag "android.content.pm.ShortcutInfo#setLongLived() ShortcutInfo#setLongLived()" in android.app.Notification.MessagingStyle [101] +android/app/admin/DevicePolicyManager.java:2670: lint: Unresolved link/see tag "android.os.UserManager#DISALLOW_CAMERA UserManager#DISALLOW_CAMERA" in android.app.admin.DevicePolicyManager [101] +android/app/admin/DevicePolicyManager.java:7257: lint: Unresolved link/see tag "android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY" in android.app.admin.DevicePolicyManager [101] +android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "ACTION_DEVICE_FINANCING_STATE_CHANGED" in android.app.admin.DevicePolicyManager [101] +android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK" in android.app.admin.DevicePolicyManager [101] +android/app/admin/DevicePolicyManager.java:7428: lint: Unresolved link/see tag "android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT" in android.app.admin.DevicePolicyManager [101] +android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Drawables DevicePolicyResources.Drawables" in android.app.admin.DevicePolicyManager [101] +android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyManager [101] +android/app/admin/DevicePolicyResourcesManager.java:179: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyResourcesManager [101] +android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101] +android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101] +android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.AppSearchSession [101] +android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.AppSearchSession [101] +android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#isFeatureSupported" in android.app.appsearch.AppSearchSession [101] +android/app/appsearch/JoinSpec.java:219: lint: Unresolved link/see tag "android.app.appsearch.SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE" in android.app.appsearch.JoinSpec.Builder [101] +android/app/appsearch/SearchSpec.java:230: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec [101] +android/app/appsearch/SearchSpec.java:237: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec [101] +android/app/appsearch/SearchSpec.java:244: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec [101] +android/app/appsearch/SearchSpec.java:913: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec.Builder [101] +android/app/appsearch/SearchSpec.java:925: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec.Builder [101] +android/app/appsearch/SearchSpec.java:929: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec.Builder [101] +android/app/job/JobParameters.java:128: lint: Unresolved link/see tag "android.app.job.JobInfo.Builder#setPeriodic(boolean) periodic jobs" in android.app.job.JobParameters [101] +android/app/sdksandbox/AppOwnedSdkSandboxInterface.java:9: lint: Unresolved link/see tag "SdkSandboxController#getAppOwnedSdkSandboxInterfaces" in android.app.sdksandbox.AppOwnedSdkSandboxInterface [101] +android/app/sdksandbox/SdkSandboxManager.java:112: lint: Unresolved link/see tag "AppOwnedSdkSandboxInterfaces" in android.app.sdksandbox.SdkSandboxManager [101] +android/companion/CompanionDeviceService.java:273: lint: Unresolved link/see tag "android.companion.AssociationInfo#isSelfManaged() self-managed" in android.companion.CompanionDeviceService [101] +android/companion/CompanionDeviceService.java:282: lint: Unresolved link/see tag "android.companion.AssociationInfo#isSelfManaged() self-managed" in android.companion.CompanionDeviceService [101] +android/companion/virtual/VirtualDevice.java:15: lint: Unresolved link/see tag "android.companion.virtual.VirtualDeviceManager.VirtualDevice VirtualDeviceManager.VirtualDevice" in android.companion.virtual.VirtualDevice [101] +android/companion/virtual/VirtualDevice.java:70: lint: Unresolved link/see tag "android.companion.virtual.VirtualDeviceParams.Builder#setName(String)" in android.companion.virtual.VirtualDevice [101] +android/content/AttributionSource.java:291: lint: Unresolved link/see tag "setNextAttributionSource" in android.content.AttributionSource.Builder [101] +android/content/Context.java:2872: lint: Unresolved link/see tag "android.telephony.MmsManager" in android.content.Context [101] +android/content/Intent.java:4734: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101] +android/content/Intent.java:4760: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101] +android/content/Intent.java:4778: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101] +android/content/Intent.java:4802: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101] +android/content/om/OverlayIdentifier.java:20: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)" in android.content.om.OverlayIdentifier [101] +android/content/om/OverlayInfo.java:78: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)" in android.content.om.OverlayInfo [101] +android/content/om/OverlayManager.java:9: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction#commit()" in android.content.om.OverlayManager [101] +android/content/pm/PackageInstaller.java:2232: lint: Unresolved link/see tag "android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS INSTALL_GRANT_RUNTIME_PERMISSIONS" in android.content.pm.PackageInstaller.SessionParams [101] +android/content/pm/ServiceInfo.java:176: lint: Unresolved link/see tag "android.app.job.JobInfo.Builder#setDataTransfer" in android.content.pm.ServiceInfo [101] +android/content/pm/verify/domain/DomainVerificationUserState.java:82: lint: Unresolved link/see tag "android.content.pm.verify.domain.DomainVerificationUserState.DomainState DomainState" in android.content.pm.verify.domain.DomainVerificationUserState [101] +android/content/res/Resources.java:958: lint: Unresolved link/see tag "android.annotation.UiContext" in android.content.res.Resources [101] +android/credentials/CreateCredentialException.java:22: lint: Unresolved link/see tag "android.credentials.CredentialManager#createCredential(android.credentials.CreateCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#createCredential(CreateCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.CreateCredentialException [101] +android/credentials/CreateCredentialException.java:101: lint: Unresolved link/see tag "android.credentials.CredentialManager#createCredential(android.credentials.CreateCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#createCredential(CreateCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.CreateCredentialException [101] +android/credentials/CreateCredentialRequest.java:107: lint: Unresolved link/see tag "androidx.credentials.CreateCredentialRequest" in android.credentials.CreateCredentialRequest.Builder [101] +android/credentials/CredentialDescription.java:89: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mSupportedElementKeys CredentialDescription#mSupportedElementKeys" in android.credentials.CredentialDescription [101] +android/credentials/CredentialDescription.java:89: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mType CredentialDescription#mType" in android.credentials.CredentialDescription [101] +android/credentials/CredentialDescription.java:101: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mSupportedElementKeys CredentialDescription#mSupportedElementKeys" in android.credentials.CredentialDescription [101] +android/credentials/CredentialDescription.java:101: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mType CredentialDescription#mType" in android.credentials.CredentialDescription [101] +android/credentials/GetCredentialException.java:22: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.GetCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.GetCredentialException [101] +android/credentials/GetCredentialException.java:103: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.GetCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver)" in android.credentials.GetCredentialException [101] +android/credentials/PrepareGetCredentialResponse.java:20: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse [101] +android/credentials/PrepareGetCredentialResponse.java:68: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse [101] +android/credentials/PrepareGetCredentialResponse.java:83: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle [101] +android/graphics/Paint.java:838: lint: Unresolved link/see tag "android.annotation.ColorLong ColorLong" in android.graphics.Paint [101] +android/graphics/text/LineBreaker.java:246: lint: Unresolved link/see tag "StaticLayout.Builder#setUseBoundsForWidth(boolean)" in android.graphics.text.LineBreaker.Builder [101] +android/hardware/camera2/CameraCharacteristics.java:2169: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2361: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors guideline" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2361: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors tables" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2361: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2390: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2390: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations tables" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2402: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2402: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2445: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2445: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2473: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2473: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraCharacteristics.java:2473: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101] +android/hardware/camera2/CameraMetadata.java:1934: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#level-3-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101] +android/hardware/camera2/CameraMetadata.java:1980: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#full-level-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101] +android/hardware/camera2/CameraMetadata.java:2019: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101] +android/hardware/camera2/CameraMetadata.java:2038: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#limited-level-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101] +android/hardware/camera2/CameraMetadata.java:2533: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101] +android/hardware/camera2/CameraMetadata.java:3095: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations" in android.hardware.camera2.CameraMetadata [101] +android/hardware/camera2/CaptureRequest.java:704: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureRequest [101] +android/hardware/camera2/CaptureRequest.java:1501: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureRequest [101] +android/hardware/camera2/CaptureResult.java:923: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101] +android/hardware/camera2/CaptureResult.java:2337: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101] +android/hardware/input/InputManager.java:215: lint: Unresolved link/see tag "android.hardware.input.InputManagerGlobal#getInputDevice InputManagerGlobal#getInputDevice" in android.hardware.input.InputManager.InputDeviceListener [101] +android/inputmethodservice/AbstractInputMethodService.java:155: lint: Unresolved link/see tag "android.app.ActivityThread ActivityThread" in android.inputmethodservice.AbstractInputMethodService [101] +android/inputmethodservice/InputMethodService.java:1078: lint: Unresolved link/see tag "android.widget.Editor" in android.inputmethodservice.InputMethodService [101] +android/location/GnssSignalType.java:14: lint: Unresolved link/see tag "android.location.GnssStatus.ConstellationType GnssStatus.ConstellationType" in android.location.GnssSignalType [101] +android/location/GnssSignalType.java:48: lint: Unresolved link/see tag "android.location.GnssStatus.ConstellationType GnssStatus.ConstellationType" in android.location.GnssSignalType [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ALARM AttributeSdkUsage#USAGE_ALARM" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANT AttributeSdkUsage#USAGE_ASSISTANT" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_GAME AttributeSdkUsage#USAGE_GAME" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_MEDIA AttributeSdkUsage#USAGE_MEDIA" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_EVENT AttributeSdkUsage#USAGE_NOTIFICATION_EVENT" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_UNKNOWN AttributeSdkUsage#USAGE_UNKNOWN" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION AttributeSdkUsage#USAGE_VOICE_COMMUNICATION" in android.media.AudioAttributes.Builder [101] +android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING" in android.media.AudioAttributes.Builder [101] +android/media/AudioFormat.java:963: lint: Unresolved link/see tag "android.media.AudioSystem#OUT_CHANNEL_COUNT_MAX AudioSystem#OUT_CHANNEL_COUNT_MAX" in android.media.AudioFormat.Builder [101] +android/media/AudioManager.java:275: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101] +android/media/AudioManager.java:287: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101] +android/media/AudioManager.java:311: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101] +android/media/AudioManager.java:313: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101] +android/media/AudioMetadata.java:118: lint: Unresolved link/see tag "android.media.AudioPresentation.ContentClassifier One of {@link android.media.AudioPresentation#CONTENT_UNKNOWN AudioPresentation#CONTENT_UNKNOWN}, {@link android.media.AudioPresentation#CONTENT_MAIN AudioPresentation#CONTENT_MAIN}, {@link android.media.AudioPresentation#CONTENT_MUSIC_AND_EFFECTS AudioPresentation#CONTENT_MUSIC_AND_EFFECTS}, {@link android.media.AudioPresentation#CONTENT_VISUALLY_IMPAIRED AudioPresentation#CONTENT_VISUALLY_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_HEARING_IMPAIRED AudioPresentation#CONTENT_HEARING_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_DIALOG AudioPresentation#CONTENT_DIALOG}, {@link android.media.AudioPresentation#CONTENT_COMMENTARY AudioPresentation#CONTENT_COMMENTARY}, {@link android.media.AudioPresentation#CONTENT_EMERGENCY AudioPresentation#CONTENT_EMERGENCY}, {@link android.media.AudioPresentation#CONTENT_VOICEOVER AudioPresentation#CONTENT_VOICEOVER}." in android.media.AudioMetadata.Format [101] +android/media/MediaRouter2.java:162: lint: Unresolved link/see tag "#getInstance(android.content.Context,java.lang.String)" in android.media.MediaRouter2 [101] +android/media/midi/MidiUmpDeviceService.java:-1: lint: Unresolved link/see tag "#MidiDeviceService" in android.media.midi.MidiUmpDeviceService [101] +android/media/tv/SectionRequest.java:44: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionRequest [101] +android/media/tv/SectionResponse.java:39: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionResponse [101] +android/media/tv/TableRequest.java:48: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableRequest [101] +android/media/tv/TableResponse.java:82: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableResponse [101] +android/net/EthernetNetworkSpecifier.java:21: lint: Unresolved link/see tag "android.net.EthernetManager" in android.net.EthernetNetworkSpecifier [101] +android/net/eap/EapSessionConfig.java:120: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101] +android/net/eap/EapSessionConfig.java:135: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101] +android/net/eap/EapSessionConfig.java:148: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101] +android/net/eap/EapSessionConfig.java:161: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101] +android/net/eap/EapSessionConfig.java:288: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaConfig [101] +android/net/eap/EapSessionConfig.java:390: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaPrimeConfig [101] +android/net/eap/EapSessionConfig.java:587: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapSimConfig [101] +android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.MloLink [101] +android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.MloLink [101] +android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_6_GHZ WifiScanner#WIFI_BAND_6_GHZ" in android.net.wifi.MloLink [101] +android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_UNSPECIFIED WifiScanner#WIFI_BAND_UNSPECIFIED" in android.net.wifi.MloLink [101] +android/net/wifi/SoftApConfiguration.java:9: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder SoftApConfiguration.Builder" in android.net.wifi.SoftApConfiguration [101] +android/net/wifi/SoftApConfiguration.java:66: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setSsid(java.lang.String) Builder#setSsid(String)" in android.net.wifi.SoftApConfiguration [101] +android/net/wifi/SoftApConfiguration.java:85: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setWifiSsid(android.net.wifi.WifiSsid) Builder#setWifiSsid(WifiSsid)" in android.net.wifi.SoftApConfiguration [101] +android/net/wifi/SoftApConfiguration.java:96: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBssid(android.net.MacAddress) Builder#setBssid(MacAddress)" in android.net.wifi.SoftApConfiguration [101] +android/net/wifi/SoftApConfiguration.java:107: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setPassphrase(java.lang.String,int) Builder#setPassphrase(String, int)" in android.net.wifi.SoftApConfiguration [101] +android/net/wifi/SoftApConfiguration.java:118: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setHiddenSsid(boolean) Builder#setHiddenSsid(boolean)" in android.net.wifi.SoftApConfiguration [101] +android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101] +android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101] +android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101] +android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101] +android/net/wifi/WifiManager.java:2466: lint: Unresolved link/see tag "TelephonyManager#hasCarrierPrivileges()." in android.net.wifi.WifiManager [101] +android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig [101] +android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig [101] +android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101] +android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101] +android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig [101] +android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig [101] +android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101] +android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101] +android/os/BugreportManager.java:146: lint: Unresolved link/see tag "android.os.BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT" in android.os.BugreportManager.BugreportCallback [101] +android/os/PowerManager.java:796: lint: Unresolved link/see tag "android.os.Temperature" in android.os.PowerManager.OnThermalStatusChangedListener [101] +android/os/RemoteException.java:49: lint: Unresolved link/see tag "android.os.DeadSystemRuntimeException DeadSystemRuntimeException" in android.os.RemoteException [101] +android/provider/Settings.java:374: lint: Unresolved link/see tag "android.credentials.CredentialManager#isEnabledCredentialProviderService()" in android.provider.Settings [101] +android/provider/Settings.java:908: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService" in android.provider.Settings [101] +android/provider/Settings.java:2181: lint: Unresolved link/see tag "android.app.time.TimeManager" in android.provider.Settings.Global [101] +android/provider/Settings.java:2195: lint: Unresolved link/see tag "android.app.time.TimeManager" in android.provider.Settings.Global [101] +android/security/KeyStoreException.java:27: lint: Unresolved link/see tag "android.security.KeyStoreException.PublicErrorCode PublicErrorCode" in android.security.KeyStoreException [101] +android/service/autofill/FillResponse.java:86: lint: Unresolved link/see tag "setFieldClassificationIds" in android.service.autofill.FillResponse.Builder [101] +android/service/autofill/SaveInfo.java:623: lint: Unresolved link/see tag "FillRequest.getHints()" in android.service.autofill.SaveInfo.Builder [101] +android/service/credentials/Action.java:3: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.Action [101] +android/service/credentials/Action.java:3: lint: Unresolved link/see tag "androidx.credentials.provider.Action" in android.service.credentials.Action [101] +android/service/credentials/BeginCreateCredentialResponse.java:85: lint: Unresolved link/see tag "Manifest.permission.PROVIDE_REMOTE_CREDENTIALS" in android.service.credentials.BeginCreateCredentialResponse.Builder [101] +android/service/credentials/BeginGetCredentialResponse.java:80: lint: Unresolved link/see tag "Manifest.permission.PROVIDE_REMOTE_CREDENTIALS" in android.service.credentials.BeginGetCredentialResponse.Builder [101] +android/service/credentials/CallingAppInfo.java:73: lint: Unresolved link/see tag "android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN" in android.service.credentials.CallingAppInfo [101] +android/service/credentials/CreateEntry.java:6: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.CreateEntry [101] +android/service/credentials/CreateEntry.java:6: lint: Unresolved link/see tag "androidx.credentials.provider.CreateEntry" in android.service.credentials.CreateEntry [101] +android/service/credentials/CredentialEntry.java:11: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.CredentialEntry [101] +android/service/credentials/CredentialEntry.java:11: lint: Unresolved link/see tag "androidx.credentials.provider.CredentialEntry" in android.service.credentials.CredentialEntry [101] +android/service/credentials/RemoteEntry.java:13: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.RemoteEntry [101] +android/service/credentials/RemoteEntry.java:13: lint: Unresolved link/see tag "androidx.credentials.provider.RemoteEntry" in android.service.credentials.RemoteEntry [101] +android/service/notification/NotificationListenerService.java:417: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService notification assistant" in android.service.notification.NotificationListenerService [101] +android/service/notification/NotificationListenerService.java:435: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService notification assistant" in android.service.notification.NotificationListenerService [101] +android/service/notification/NotificationListenerService.java:1155: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService NotificationAssistantService" in android.service.notification.NotificationListenerService.Ranking [101] +android/service/notification/NotificationListenerService.java:1166: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService NotificationAssistantService" in android.service.notification.NotificationListenerService.Ranking [101] +android/service/quickaccesswallet/WalletCard.java:285: lint: Unresolved link/see tag "PackageManager.FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS" in android.service.quickaccesswallet.WalletCard.Builder [101] +android/service/voice/VoiceInteractionSession.java:293: lint: Unresolved link/see tag "android.service.voice.VoiceInteractionService#KEY_SHOW_SESSION_ID VoiceInteractionService#KEY_SHOW_SESSION_ID" in android.service.voice.VoiceInteractionSession [101] +android/text/DynamicLayout.java:141: lint: Unresolved link/see tag "LineBreakconfig" in android.text.DynamicLayout [101] +android/text/WordSegmentFinder.java:13: lint: Unresolved link/see tag "android.text.method.WordIterator WordIterator" in android.text.WordSegmentFinder [101] +android/view/InputDevice.java:71: lint: Unresolved link/see tag "InputManagerGlobal.InputDeviceListener" in android.view.InputDevice [101] +android/view/PixelCopy.java:468: lint: Unresolved link/see tag "android.view.PixelCopy.CopyResultStatus CopyResultStatus" in android.view.PixelCopy.Result [101] +android/view/ScrollFeedbackProvider.java:-25: lint: Unresolved link/see tag "InputManager" in android.view.ScrollFeedbackProvider [101] +android/view/ScrollFeedbackProvider.java:-25: lint: Unresolved link/see tag "InputManager#getInputDeviceIds()" in android.view.ScrollFeedbackProvider [101] +android/view/SurfaceControl.java:823: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101] +android/view/SurfaceControl.java:900: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101] +android/view/SurfaceControl.java:908: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101] +android/view/View.java:1647: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setAccessibilityPaneTitle(View, CharSequence)" in android.view.View [101] +android/view/View.java:4669: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setScreenReaderFocusable(View, boolean)" in android.view.View [101] +android/view/View.java:4712: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setAccessibilityHeading(View, boolean)" in android.view.View [101] +android/view/WindowManager.java:230: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowManager [101] +android/view/WindowManager.java:247: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowManager [101] +android/view/WindowManager.java:822: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106] +android/view/WindowManager.java:832: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106] +android/view/WindowMetrics.java:22: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101] +android/view/WindowMetrics.java:57: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101] +android/view/WindowMetrics.java:114: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101] +android/view/WindowMetrics.java:127: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101] +android/view/accessibility/AccessibilityNodeInfo.java:368: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#addAccessibilityAction(View, AccessibilityNodeInfoCompat.AccessibilityActionCompat)" in android.view.accessibility.AccessibilityNodeInfo [101] +android/view/accessibility/AccessibilityNodeInfo.java:3246: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#addAccessibilityAction(View, AccessibilityNodeInfoCompat.AccessibilityActionCompat)" in android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction [101] +android/view/displayhash/DisplayHashResultCallback.java:38: lint: Unresolved link/see tag "android.view.displayhash.DisplayHashResultCallback.DisplayHashErrorCode DisplayHashErrorCode" in android.view.displayhash.DisplayHashResultCallback [101] +android/view/inputmethod/EditorInfo.java:107: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.EditorInfo [101] +android/view/inputmethod/EditorInfo.java:122: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.EditorInfo [101] +android/view/inputmethod/InputMethodManager.java:423: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101] +android/view/inputmethod/InputMethodManager.java:447: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101] +android/view/inputmethod/InputMethodManager.java:456: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101] +android/view/inspector/PropertyReader.java:141: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.view.inspector.PropertyReader [101] +android/window/BackEvent.java:24: lint: Unresolved link/see tag "android.window.BackMotionEvent BackMotionEvent" in android.window.BackEvent [101] + +android/net/wifi/SoftApConfiguration.java:173: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)" in android.net.wifi.SoftApConfiguration [101] +android/content/pm/ActivityInfo.java:1197: lint: Unresolved link/see tag "android.view.ViewRootImpl" in android.content.pm.ActivityInfo [101] +android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware @UserHandleAware" in android.os.UserManager [101] +android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware#enabledSinceTargetSdkVersion" in android.os.UserManager [101] +android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "#initialize( PersistableBundle, SharedMemory, SoundTrigger.ModuleProperties)" in android.service.voice.AlwaysOnHotwordDetector [101] +android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "STATE_HARDWARE_UNAVAILABLE" in android.service.voice.AlwaysOnHotwordDetector [101] +android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "#STATE_ERROR" in android [101] +android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onFailure" in android [101] +android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onUnknownFailure" in android [101] +android/view/animation/AnimationUtils.java:64: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in android.view.animation.AnimationUtils [101] +android/view/contentcapture/ContentCaptureSession.java:188: lint: Unresolved link/see tag "UPSIDE_DOWN_CAKE" in android.view.contentcapture.ContentCaptureSession [101] +com/android/internal/policy/PhoneWindow.java:172: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in com.android.internal.policy.PhoneWindow [101] + +com/android/server/companion/virtual/VirtualDeviceImpl.java:134: lint: Unresolved link/see tag "DisplayManager" in android [101] +com/android/server/companion/virtual/VirtualDeviceImpl.java:134: lint: Unresolved link/see tag "VirtualDeviceManager.VirtualDevice" in android [101] +com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "IdentifierType#DAB_SID_EXT" in android [101] +com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT" in android [101] +com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "RadioTuner" in android [101] +com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "ComponentName" in android [101] +com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "IllegalArgumentException" in android [101] +com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "MediaSession#setMediaButtonBroadcastReceiver(ComponentName)" in android [101] +com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "IllegalArgumentException" in android [101] +com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "MediaSession#setMediaButtonReceiver(PendingIntent)" in android [101] +com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "PendingIntent" in android [101] +com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "Build.VERSION_CODES#S API 31" in android [101] +com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequireUserAction" in android [101] +com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101] +com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34" in android [101] +com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)" in android [101] +com/android/server/pm/PackageInstallerSession.java:358: lint: Unresolved link/see tag "IntentSender" in android [101] +com/android/server/devicepolicy/DevicePolicyManagerService.java:860: lint: Unresolved link/see tag "android.security.IKeyChainService#setGrant" in android [101] + +android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:-4: lint: Invalid tag: @Override [131] +android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:-1: lint: Invalid tag: @Override [131] +android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:2: lint: Invalid tag: @Override [131] +android/os/BatteryStatsManager.java:260: lint: Invalid tag: @Deprecated [131] +android/os/BatteryStatsManager.java:275: lint: Invalid tag: @Deprecated [131] +android/view/WindowManager.java:906: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106] +android/view/WindowManager.java:916: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106] + +java/lang/ClassLoader.java:853: lint: Unknown tag: @systemProperty [103] diff --git a/config/preloaded-classes b/config/preloaded-classes index aa34bad29b26..7f8f5e3f362b 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -6519,12 +6519,6 @@ android.security.Scrypt android.security.attestationverification.AttestationVerificationManager android.security.keymaster.ExportResult$1 android.security.keymaster.ExportResult -android.security.keymaster.IKeyAttestationApplicationIdProvider$Stub -android.security.keymaster.IKeyAttestationApplicationIdProvider -android.security.keymaster.KeyAttestationApplicationId$1 -android.security.keymaster.KeyAttestationApplicationId -android.security.keymaster.KeyAttestationPackageInfo$1 -android.security.keymaster.KeyAttestationPackageInfo android.security.keymaster.KeyCharacteristics$1 android.security.keymaster.KeyCharacteristics android.security.keymaster.KeymasterArgument$1 @@ -6549,7 +6543,13 @@ android.security.keystore.AttestationUtils android.security.keystore.BackendBusyException android.security.keystore.DelegatingX509Certificate android.security.keystore.DeviceIdAttestationException +android.security.keystore.IKeyAttestationApplicationIdProvider$Stub +android.security.keystore.IKeyAttestationApplicationIdProvider +android.security.keystore.KeyAttestationApplicationId$Stub +android.security.keystore.KeyAttestationApplicationId android.security.keystore.KeyAttestationException +android.security.keystore.KeyAttestationPackageInfo$Stub +android.security.keystore.KeyAttestationPackageInfo android.security.keystore.KeyExpiredException android.security.keystore.KeyGenParameterSpec$Builder android.security.keystore.KeyGenParameterSpec @@ -6572,6 +6572,8 @@ android.security.keystore.KeystoreResponse$1 android.security.keystore.KeystoreResponse android.security.keystore.ParcelableKeyGenParameterSpec$1 android.security.keystore.ParcelableKeyGenParameterSpec +android.security.keystore.Signature$Stub +android.security.keystore.Signature android.security.keystore.SecureKeyImportUnavailableException android.security.keystore.StrongBoxUnavailableException android.security.keystore.UserAuthArgs diff --git a/core/api/current.txt b/core/api/current.txt index 2b50e382a34f..e8baa77f9ad1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4463,7 +4463,7 @@ package android.app { method public void onProvideAssistData(android.os.Bundle); method public android.net.Uri onProvideReferrer(); method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]); - method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int); method @CallSuper protected void onRestart(); method protected void onRestoreInstanceState(@NonNull android.os.Bundle); method public void onRestoreInstanceState(@Nullable android.os.Bundle, @Nullable android.os.PersistableBundle); @@ -4505,7 +4505,7 @@ package android.app { method public android.view.DragAndDropPermissions requestDragAndDropPermissions(android.view.DragEvent); method public void requestFullscreenMode(int, @Nullable android.os.OutcomeReceiver<java.lang.Void,java.lang.Throwable>); method public final void requestPermissions(@NonNull String[], int); - method public final void requestPermissions(@NonNull String[], int, int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public final void requestPermissions(@NonNull String[], int, int); method public final void requestShowKeyboardShortcuts(); method @Deprecated public boolean requestVisibleBehind(boolean); method public final boolean requestWindowFeature(int); @@ -4553,7 +4553,7 @@ package android.app { method public void setVrModeEnabled(boolean, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean shouldDockBigOverlays(); method public boolean shouldShowRequestPermissionRationale(@NonNull String); - method public boolean shouldShowRequestPermissionRationale(@NonNull String, int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public boolean shouldShowRequestPermissionRationale(@NonNull String, int); method public boolean shouldUpRecreateTask(android.content.Intent); method public boolean showAssist(android.os.Bundle); method @Deprecated public final void showDialog(int); @@ -9683,7 +9683,7 @@ package android.companion.virtual { method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @NonNull public int[] getDisplayIds(); method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public CharSequence getDisplayName(); method @Nullable public String getName(); - method @Nullable public String getPersistentDeviceId(); + method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public String getPersistentDeviceId(); method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomSensorSupport(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDevice> CREATOR; @@ -9769,7 +9769,7 @@ package android.content { method public int describeContents(); method public void enforceCallingUid(); method @Nullable public String getAttributionTag(); - method public int getDeviceId(); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public int getDeviceId(); method @Nullable public android.content.AttributionSource getNext(); method @Nullable public String getPackageName(); method public int getPid(); @@ -9785,7 +9785,7 @@ package android.content { ctor public AttributionSource.Builder(@NonNull android.content.AttributionSource); method @NonNull public android.content.AttributionSource build(); method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String); - method @NonNull public android.content.AttributionSource.Builder setDeviceId(int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int); method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource); method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource); method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String); @@ -10992,6 +10992,7 @@ package android.content { field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED"; field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED"; field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; + field @FlaggedApi("android.content.pm.stay_stopped") public static final String ACTION_PACKAGE_UNSTOPPED = "android.intent.action.PACKAGE_UNSTOPPED"; field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED"; field public static final String ACTION_PASTE = "android.intent.action.PASTE"; field public static final String ACTION_PICK = "android.intent.action.PICK"; @@ -12673,6 +12674,7 @@ package android.content.pm { method public boolean isDeviceUpgrading(); method public abstract boolean isInstantApp(); method public abstract boolean isInstantApp(@NonNull String); + method @FlaggedApi("android.content.pm.stay_stopped") public boolean isPackageStopped(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean isPackageSuspended(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean isPackageSuspended(); method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String); @@ -16086,10 +16088,12 @@ package android.graphics { method public String getFontFeatureSettings(); method public float getFontMetrics(android.graphics.Paint.FontMetrics); method public android.graphics.Paint.FontMetrics getFontMetrics(); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void getFontMetricsForLocale(@NonNull android.graphics.Paint.FontMetrics); method public void getFontMetricsInt(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt); method public void getFontMetricsInt(@NonNull char[], @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt); method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt); method public android.graphics.Paint.FontMetricsInt getFontMetricsInt(); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void getFontMetricsIntForLocale(@NonNull android.graphics.Paint.FontMetricsInt); method public float getFontSpacing(); method public String getFontVariationSettings(); method public int getHinting(); @@ -17666,6 +17670,7 @@ package android.graphics.text { field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1 field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0 field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2 + field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_STYLE_NO_BREAK = 4; // 0x4 field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3 field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0 @@ -18539,8 +18544,10 @@ package android.hardware.biometrics { ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac); ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential); ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession); + ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement); method public javax.crypto.Cipher getCipher(); method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential(); + method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement(); method public javax.crypto.Mac getMac(); method @Nullable public android.security.identity.PresentationSession getPresentationSession(); method public java.security.Signature getSignature(); @@ -41552,7 +41559,7 @@ package android.telecom { method public android.telecom.GatewayInfo getGatewayInfo(); method public android.net.Uri getHandle(); method public int getHandlePresentation(); - method @NonNull public String getId(); + method @FlaggedApi("com.android.server.telecom.flags.call_details_id_changes") @NonNull public String getId(); method public android.os.Bundle getIntentExtras(); method public final int getState(); method public android.telecom.StatusHints getStatusHints(); @@ -42982,7 +42989,6 @@ package android.telephony { field public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array"; field public static final String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool"; field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool"; - field @Deprecated public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool"; field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool"; field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool"; field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool"; @@ -44904,7 +44910,7 @@ package android.telephony { method public int getSubscriptionType(); method public int getUsageSetting(); method public boolean isEmbedded(); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isNtn(); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isOnlyNonTerrestrialNetwork(); method public boolean isOpportunistic(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SubscriptionInfo> CREATOR; @@ -46671,10 +46677,10 @@ package android.text { method @NonNull public android.text.DynamicLayout.Builder setHyphenationFrequency(int); method @NonNull public android.text.DynamicLayout.Builder setIncludePad(boolean); method @NonNull public android.text.DynamicLayout.Builder setJustificationMode(int); - method @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig); + method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float); method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic); - method @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean); method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean); } @@ -47198,7 +47204,7 @@ package android.text { method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int); method public android.text.StaticLayout.Builder setText(CharSequence); method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic); - method @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean); + method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean); method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean); } @@ -47911,6 +47917,10 @@ package android.text.style { method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig(); } + @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoBreakSpan extends android.text.style.LineBreakConfigSpan { + ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoBreakSpan(); + } + @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan { ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoHyphenationSpan(); } diff --git a/core/api/removed.txt b/core/api/removed.txt index 57e2e73854c1..5a4be65ef559 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -472,6 +472,10 @@ package android.speech.tts { package android.telephony { + public class CarrierConfigManager { + field @Deprecated public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool"; + } + public class NetworkScan { method @Deprecated public void stop() throws android.os.RemoteException; } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 358c8e72064b..0dc8632afe26 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -297,6 +297,7 @@ package android { field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"; field public static final String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"; field public static final String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST"; + field @FlaggedApi("android.permission.flags.voice_activation_permission_apis") public static final String RECEIVE_SANDBOX_TRIGGER_AUDIO = "android.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO"; field public static final String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"; field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO"; field public static final String RECOVERY = "android.permission.RECOVERY"; @@ -3209,7 +3210,7 @@ package android.companion.virtual { method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig); method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method public int getDeviceId(); - method @Nullable public String getPersistentDeviceId(); + method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public String getPersistentDeviceId(); method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensor> getVirtualSensorList(); method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer); method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback); @@ -3946,7 +3947,7 @@ package android.content.pm { field public static final int DELETE_FAILED_OWNER_BLOCKED = -4; // 0xfffffffc field public static final int DELETE_KEEP_DATA = 1; // 0x1 field public static final int DELETE_SUCCEEDED = 1; // 0x1 - field public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; + field @FlaggedApi("android.permission.flags.device_aware_permission_apis") public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; field public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; @@ -4529,7 +4530,7 @@ package android.hardware.display { method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float); - field public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 128; // 0x80 + field @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 128; // 0x80 field public static final int VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED = 65536; // 0x10000 field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } @@ -13228,7 +13229,7 @@ package android.telecom { method public void requestStreamingState(int); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telecom.StreamingCall> CREATOR; - field public static final String EXTRA_CALL_ID = "android.telecom.extra.CALL_ID"; + field @FlaggedApi("com.android.server.telecom.flags.call_details_id_changes") public static final String EXTRA_CALL_ID = "android.telecom.extra.CALL_ID"; field public static final int STATE_DISCONNECTED = 3; // 0x3 field public static final int STATE_HOLDING = 2; // 0x2 field public static final int STATE_STREAMING = 1; // 0x1 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 40c6fa8986f7..3968155b7c42 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -875,7 +875,7 @@ package android.content { ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder); ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource); ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource); - ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource); + ctor @FlaggedApi("android.permission.flags.device_aware_permission_apis") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource); method public void enforceCallingPid(); } @@ -1903,7 +1903,7 @@ package android.media { method public android.media.PlaybackParams setAudioStretchMode(int); } - public final class RingtoneSelection { + @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection { method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int); method public int getSoundSource(); method @Nullable public android.net.Uri getSoundUri(); @@ -1915,14 +1915,16 @@ package android.media { field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3 field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1 field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2 - field public static final int SOUND_SOURCE_DEFAULT = 0; // 0x0 field public static final int SOUND_SOURCE_OFF = 1; // 0x1 + field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3 + field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0 field public static final int SOUND_SOURCE_URI = 2; // 0x2 - field public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3; // 0x3 + field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4 field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa - field public static final int VIBRATION_SOURCE_DEFAULT = 0; // 0x0 field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1 + field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3 + field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0 field public static final int VIBRATION_SOURCE_URI = 2; // 0x2 } @@ -2610,17 +2612,17 @@ package android.os.vibrator { package android.os.vibrator.persistence { - public class ParsedVibration { + @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public class ParsedVibration { method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects(); method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); } - public final class VibrationXmlParser { + @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlParser { method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException; method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException; } - public final class VibrationXmlSerializer { + @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlSerializer { method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException; } @@ -2955,10 +2957,6 @@ package android.service.notification { method @Deprecated public boolean isBound(); } - public class NotificationRankingUpdate implements android.os.Parcelable { - method public final boolean isFdNotNullAndClosed(); - } - } package android.service.quickaccesswallet { @@ -3281,12 +3279,12 @@ package android.text { } public class MeasuredParagraph { - method @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback); + method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback); } - public static interface MeasuredParagraph.StyleRunCallback { - method public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float); - method public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean); + @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static interface MeasuredParagraph.StyleRunCallback { + method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float); + method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean); } public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher { @@ -3569,8 +3567,8 @@ package android.view { method public default void holdLock(android.os.IBinder, int); method public default boolean isGlobalKey(int); method public default boolean isTaskSnapshotSupported(); - method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window); - method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl); + method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window); + method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl); method public default void setDisplayImePolicy(int, int); method public default void setShouldShowSystemDecors(int, boolean); method public default void setShouldShowWithInsecureKeyguard(int, boolean); diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 107be8be42e2..93e39d5f41f7 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -191,8 +191,14 @@ UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_DEFAULT: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF +UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT +UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI +UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL: @@ -203,6 +209,10 @@ UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_HAPTIC_GENERATOR: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF +UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT +UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int): diff --git a/core/java/Android.bp b/core/java/Android.bp index 13a1bd6ca176..0293f66061c1 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -23,11 +23,6 @@ filegroup { visibility: ["//frameworks/base"], } -filegroup { - name: "IKeyAttestationApplicationIdProvider.aidl", - srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"], -} - aidl_library { name: "IDropBoxManagerService_aidl", srcs: [ @@ -431,6 +426,16 @@ aidl_interface { }, } +aidl_interface { + name: "android.companion.virtual.virtualdevice_aidl", + unstable: true, + host_supported: true, + srcs: [ + "android/companion/virtualnative/IVirtualDeviceManagerNative.aidl", + ], + local_include_dir: ".", +} + filegroup { name: "frameworks-base-java-overview", srcs: ["overview.html"], diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index e40ea090dda3..3370c121acfe 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -3543,9 +3543,9 @@ public abstract class AccessibilityService extends Service { * @param executor Executor on which to run the callback. * @param callback The callback invoked when attaching the overlay has succeeded or failed. The * callback is a {@link java.util.function.IntConsumer} of the result status code. - * @see OVERLAY_RESULT_SUCCESS - * @see OVERLAY_RESULT_INVALID - * @see OVERLAY_RESULT_INTERNAL_ERROR + * @see #OVERLAY_RESULT_SUCCESS + * @see #OVERLAY_RESULT_INVALID + * @see #OVERLAY_RESULT_INTERNAL_ERROR */ public void attachAccessibilityOverlayToDisplay( int displayId, @@ -3619,9 +3619,9 @@ public abstract class AccessibilityService extends Service { * @param executor Executor on which to run the callback. * @param callback The callback invoked when attaching the overlay has succeeded or failed. The * callback is a {@link java.util.function.IntConsumer} of the result status code. - * @see OVERLAY_RESULT_SUCCESS - * @see OVERLAY_RESULT_INVALID - * @see OVERLAY_RESULT_INTERNAL_ERROR + * @see #OVERLAY_RESULT_SUCCESS + * @see #OVERLAY_RESULT_INVALID + * @see #OVERLAY_RESULT_INTERNAL_ERROR */ public void attachAccessibilityOverlayToWindow( int accessibilityWindowId, diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 6550f30508d3..3f9cc65ba1db 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -1020,7 +1020,6 @@ public class AccessibilityServiceInfo implements Parcelable { * * @param motionEventSources A bit mask of {@link android.view.InputDevice} sources. * @see AccessibilityService#onMotionEvent - * @see #MotionEventSources */ public void setMotionEventSources(@MotionEventSources int motionEventSources) { mMotionEventSources = motionEventSources; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 7bc6f9bfafc7..e51a41e8d4bc 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -31,6 +31,7 @@ import android.annotation.CallSuper; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.FlaggedApi; import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.LayoutRes; @@ -98,6 +99,7 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; +import android.permission.flags.Flags; import android.service.voice.VoiceInteractionSession; import android.text.Selection; import android.text.SpannableStringBuilder; @@ -5556,8 +5558,7 @@ public class Activity extends ContextThemeWrapper * reported to {@link #onRequestPermissionsResult}. * Should be >= 0. * @param deviceId The app is requesting permissions for this device. The primary/physical - * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link - * android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices} + * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and virtual devices * are assigned unique device Ids. * * @throws IllegalArgumentException if requestCode is negative. @@ -5567,6 +5568,7 @@ public class Activity extends ContextThemeWrapper * @see #shouldShowRequestPermissionRationale * @see Context#DEVICE_ID_DEFAULT */ + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public final void requestPermissions(@NonNull String[] permissions, int requestCode, int deviceId) { if (requestCode < 0) { @@ -5634,12 +5636,12 @@ public class Activity extends ContextThemeWrapper * {@link android.content.pm.PackageManager#PERMISSION_GRANTED} or * {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null. * @param deviceId The deviceId for which permissions were requested. The primary/physical - * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link - * android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices} + * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and virtual devices * are assigned unique device Ids. * * @see #requestPermissions */ + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults, int deviceId) { onRequestPermissionsResult(requestCode, permissions, grantResults); @@ -5664,8 +5666,7 @@ public class Activity extends ContextThemeWrapper * * @param permission A permission your app wants to request. * @param deviceId The app is requesting permissions for this device. The primary/physical - * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link - * android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices} + * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and virtual devices * are assigned unique device Ids. * @return Whether you should show permission rationale UI. * @@ -5673,6 +5674,7 @@ public class Activity extends ContextThemeWrapper * @see #requestPermissions * @see #onRequestPermissionsResult */ + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public boolean shouldShowRequestPermissionRationale(@NonNull String permission, int deviceId) { final PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager() : createDeviceContext(deviceId).getPackageManager(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 3bf2ccaf9923..f68681b54e48 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4288,7 +4288,7 @@ public class ActivityManager { } /** - * Start monitoring changes to the imoportance of uids running in the system. + * Start monitoring changes to the importance of uids running in the system. * @param listener The listener callback that will receive change reports. * @param importanceCutpoint The level of importance in which the caller is interested * in differences. For example, if {@link RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE} diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9a90df93b2cd..e12181a08db3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -101,6 +101,7 @@ import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.ResourcesImpl; import android.content.res.loader.ResourcesLoader; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDebug; @@ -297,6 +298,7 @@ public final class ActivityThread extends ClientTransactionHandler public static final boolean DEBUG_MEMORY_TRIM = false; private static final boolean DEBUG_PROVIDER = false; public static final boolean DEBUG_ORDER = false; + private static final boolean DEBUG_APP_INFO = true; private static final long MIN_TIME_BETWEEN_GCS = 5*1000; /** * The delay to release the provider when it has no more references. It reduces the number of @@ -6473,10 +6475,35 @@ public final class ActivityThread extends ClientTransactionHandler resApk.updateApplicationInfo(ai, oldPaths); } + ResourcesImpl beforeImpl = getApplication().getResources().getImpl(); + synchronized (mResourcesManager) { // Update all affected Resources objects to use new ResourcesImpl mResourcesManager.applyAllPendingAppInfoUpdates(); } + + ResourcesImpl afterImpl = getApplication().getResources().getImpl(); + + if ((beforeImpl != afterImpl) && !Arrays.equals(beforeImpl.getAssets().getApkAssets(), + afterImpl.getAssets().getApkAssets())) { + List<String> beforeAssets = Arrays.asList(beforeImpl.getAssets().getApkPaths()); + List<String> afterAssets = Arrays.asList(afterImpl.getAssets().getApkPaths()); + + List<String> onlyBefore = new ArrayList<>(beforeAssets); + onlyBefore.removeAll(afterAssets); + List<String> onlyAfter = new ArrayList<>(afterAssets); + onlyAfter.removeAll(beforeAssets); + + Slog.i(TAG, "ApplicationInfo updating for " + ai.packageName + ", new timestamp: " + + ai.createTimestamp + "\nassets removed: " + onlyBefore + "\nassets added: " + + onlyAfter); + + if (DEBUG_APP_INFO) { + Slog.v(TAG, "ApplicationInfo updating for " + ai.packageName + + ", assets before change: " + beforeAssets + "\n assets after change: " + + afterAssets); + } + } } /** diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index ca10d144d4cd..68589456dec3 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1478,7 +1478,8 @@ public class AppOpsManager { AppProtoEnums.APP_OP_RECORD_AUDIO_SANDBOXED; /** - * Allows the assistant app to receive the PCC-validated hotword and be voice-triggered. + * Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection + * service. * * @hide */ @@ -1486,13 +1487,13 @@ public class AppOpsManager { AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; /** - * Allows the assistant app to get the training data from the PCC sandbox to improve the + * Allows the assistant app to get the training data from the trusted process to improve the * hotword training model. * * @hide */ - public static final int OP_RECEIVE_SANDBOX_TRAINING_DATA = - AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRAINING_DATA; + public static final int OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA = + AppProtoEnums.APP_OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA; /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -1640,7 +1641,7 @@ public class AppOpsManager { OPSTR_CAMERA_SANDBOXED, OPSTR_RECORD_AUDIO_SANDBOXED, OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO, - OPSTR_RECEIVE_SANDBOX_TRAINING_DATA + OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA }) public @interface AppOpString {} @@ -2252,7 +2253,8 @@ public class AppOpsManager { public static final String OPSTR_USE_FULL_SCREEN_INTENT = "android:use_full_screen_intent"; /** - * Allows the assistant app to receive the PCC-validated hotword and be voice-triggered. + * Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection + * service. * * @hide */ @@ -2261,13 +2263,13 @@ public class AppOpsManager { "android:receive_sandbox_trigger_audio"; /** - * Allows the assistant app to get the training data from the PCC sandbox to improve + * Allows the assistant app to get the training data from the trusted process to improve * the hotword training model. * * @hide */ - public static final String OPSTR_RECEIVE_SANDBOX_TRAINING_DATA = - "android:receive_sandbox_training_data"; + public static final String OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA = + "android:receive_trusted_process_training_data"; /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; @@ -2379,7 +2381,8 @@ public class AppOpsManager { OP_RUN_USER_INITIATED_JOBS, OP_FOREGROUND_SERVICE_SPECIAL_USE, OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD, - OP_USE_FULL_SCREEN_INTENT + OP_USE_FULL_SCREEN_INTENT, + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO }; static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{ @@ -2810,10 +2813,11 @@ public class AppOpsManager { new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO, "RECEIVE_SANDBOX_TRIGGER_AUDIO") - .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), - new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRAINING_DATA, - OPSTR_RECEIVE_SANDBOX_TRAINING_DATA, - "RECEIVE_SANDBOX_TRAINING_DATA").build() + .setPermission(Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO) + .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(), + new AppOpInfo.Builder(OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA, + OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA, + "RECEIVE_TRUSTED_PROCESS_TRAINING_DATA").build() }; // The number of longs needed to form a full bitmask of app ops diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index 7b3d01712867..d15c79f41d3f 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -468,6 +468,15 @@ public final class ApplicationExitInfo implements Parcelable { */ public static final int SUBREASON_EXCESSIVE_BINDER_OBJECTS = 29; + /** + * The process was killed by the [kernel] Out-of-memory (OOM) killer; this + * would be set only when the reason is {@link #REASON_LOW_MEMORY}. + * + * For internal use only. + * @hide + */ + public static final int SUBREASON_OOM_KILL = 30; + // If there is any OEM code which involves additional app kill reasons, it should // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000. @@ -644,6 +653,7 @@ public final class ApplicationExitInfo implements Parcelable { SUBREASON_PACKAGE_UPDATE, SUBREASON_UNDELIVERED_BROADCAST, SUBREASON_EXCESSIVE_BINDER_OBJECTS, + SUBREASON_OOM_KILL, }) @Retention(RetentionPolicy.SOURCE) public @interface SubReason {} @@ -1371,6 +1381,8 @@ public final class ApplicationExitInfo implements Parcelable { return "UNDELIVERED BROADCAST"; case SUBREASON_EXCESSIVE_BINDER_OBJECTS: return "EXCESSIVE BINDER OBJECTS"; + case SUBREASON_OOM_KILL: + return "OOM KILL"; default: return "UNKNOWN"; } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index e5a73be5023a..21ed098f448a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2954,6 +2954,17 @@ public class ApplicationPackageManager extends PackageManager { } } + @Override + public boolean isPackageStopped(@NonNull String packageName) throws NameNotFoundException { + try { + return mPM.isPackageStoppedForUser(packageName, getUserId()); + } catch (IllegalArgumentException ie) { + throw new NameNotFoundException(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide */ @Override public void setApplicationCategoryHint(String packageName, int categoryHint) { diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index a6a57cd5745a..37111e931d71 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -473,9 +473,8 @@ public final class ApplicationStartInfo implements Parcelable { * available. * For {@link #STARTUP_STATE_ERROR}, no additional timestamps are guaranteed available. * For {@link #STARTUP_STATE_FIRST_FRAME_DRAWN}, timestamps - * {@link #START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE}, {@link #START_TIMESTAMP_APPLICATION_ONCREATE}, - * {@link #START_TIMESTAMP_BIND_APPLICATION}, and {@link #START_TIMESTAMP_FIRST_FRAME} will - * additionally be available. + * {@link #START_TIMESTAMP_APPLICATION_ONCREATE}, {@link #START_TIMESTAMP_BIND_APPLICATION}, + * and {@link #START_TIMESTAMP_FIRST_FRAME} will additionally be available. * * Timestamp {@link #START_TIMESTAMP_FULLY_DRAWN} is never guaranteed to be available as it is * dependant on devloper calling {@link Activity#reportFullyDrawn}. diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java index bc6fe6146764..a55121aaa12c 100644 --- a/core/java/android/app/GrammaticalInflectionManager.java +++ b/core/java/android/app/GrammaticalInflectionManager.java @@ -100,7 +100,7 @@ public class GrammaticalInflectionManager { /** * Sets the current grammatical gender for all privileged applications. The value will be - * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int) + * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int)} * * @param grammaticalGender the terms of address the user preferred in system. * @@ -121,8 +121,7 @@ public class GrammaticalInflectionManager { } /** - * Get the current grammatical gender of privileged application from the encrypted file, - * which is stored under {@link android.os.Environment#getDataSystemCeDirectory(int)}. + * Get the current grammatical gender of privileged application from the encrypted file. * * @return the value of grammatical gender * diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 93c2b5ad4d86..dd7db23d668b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -9195,6 +9195,12 @@ public class Notification implements Parcelable * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}. * <p> * + * <p> + * Starting at {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V} the + * {@link Notification#FLAG_NO_CLEAR NO_CLEAR flag} will be set for valid MediaStyle + * notifications. + * <p> + * * To use this style with your Notification, feed it to * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so: * <pre class="prettyprint"> diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 7ee1332ea76a..15d692ab1673 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -272,6 +272,10 @@ public final class NotificationChannel implements Parcelable { private boolean mDemoted = false; private boolean mImportantConvo = false; private long mDeletedTime = DEFAULT_DELETION_TIME_MS; + /** Do not (de)serialize this value: it only affects logic in system_server and that logic + * is reset on each boot {@link NotificationAttentionHelper#buzzBeepBlinkLocked}. + */ + private long mLastNotificationUpdateTimeMs = 0; /** * Creates a notification channel. @@ -932,6 +936,23 @@ public final class NotificationChannel implements Parcelable { } /** + * Returns the time of the notification post or last update for this channel. + * @return time of post / last update + * @hide + */ + public long getLastNotificationUpdateTimeMs() { + return mLastNotificationUpdateTimeMs; + } + + /** + * Sets the time of the notification post or last update for this channel. + * @hide + */ + public void setLastNotificationUpdateTimeMs(long updateTimeMs) { + mLastNotificationUpdateTimeMs = updateTimeMs; + } + + /** * @hide */ public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled, @@ -1408,7 +1429,8 @@ public final class NotificationChannel implements Parcelable { + ", mParent=" + mParentId + ", mConversationId=" + mConversationId + ", mDemoted=" + mDemoted - + ", mImportantConvo=" + mImportantConvo; + + ", mImportantConvo=" + mImportantConvo + + ", mLastNotificationUpdateTimeMs=" + mLastNotificationUpdateTimeMs; } /** @hide */ diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index 456c6af134cf..7c69d376817c 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -1172,6 +1172,12 @@ public final class DevicePolicyResources { public static final String WORK_CATEGORY_HEADER = PREFIX + "WORK_CATEGORY_HEADER"; /** + * Header for items under the private user + */ + public static final String PRIVATE_CATEGORY_HEADER = + PREFIX + "PRIVATE_CATEGORY_HEADER"; + + /** * Header for items under the personal user */ public static final String PERSONAL_CATEGORY_HEADER = @@ -1208,6 +1214,12 @@ public final class DevicePolicyResources { public static final String AUTO_SYNC_WORK_DATA = PREFIX + "AUTO_SYNC_WORK_DATA"; /** + * Text for toggle to enable auto-sycing private data + */ + public static final String AUTO_SYNC_PRIVATE_DATA = PREFIX + + "AUTO_SYNC_PRIVATE_DATA"; + + /** * Summary for "More security settings" section when a work profile is on the device. */ public static final String MORE_SECURITY_SETTINGS_WORK_PROFILE_SUMMARY = PREFIX diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 2fb428b3e0b4..a84845a15a79 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -240,6 +240,12 @@ public final class CompanionDeviceManager { public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES /** + * The length limit of Association tag. + * @hide + */ + private static final int ASSOCIATION_TAG_LENGTH_LIMIT = 100; + + /** * Callback for applications to receive updates about and the outcome of * {@link AssociationRequest} issued via {@code associate()} call. * @@ -1409,7 +1415,7 @@ public final class CompanionDeviceManager { /** * Sets the {@link AssociationInfo#getTag() tag} for this association. * - * <p>The length of the tag must be at most 20 characters. + * <p>The length of the tag must be at most 100 characters to save disk space. * * <p>This allows to store useful information about the associated devices. * @@ -1421,8 +1427,8 @@ public final class CompanionDeviceManager { public void setAssociationTag(int associationId, @NonNull String tag) { Objects.requireNonNull(tag, "tag cannot be null"); - if (tag.length() > 20) { - throw new IllegalArgumentException("Length of the tag must be at most 20 characters"); + if (tag.length() > ASSOCIATION_TAG_LENGTH_LIMIT) { + throw new IllegalArgumentException("Length of the tag must be at most 100 characters"); } try { diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java index ce883cddc952..0af4c92e0b63 100644 --- a/core/java/android/companion/virtual/VirtualDevice.java +++ b/core/java/android/companion/virtual/VirtualDevice.java @@ -113,6 +113,7 @@ public final class VirtualDevice implements Parcelable { * <p class="note">This identifier may not be unique across virtual devices, in case there are * more than one virtual devices corresponding to the same physical device. */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public @Nullable String getPersistentDeviceId() { return mPersistentId; } diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 39800f73058b..25693660e1ab 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -476,6 +476,7 @@ public final class VirtualDeviceManager { /** * Returns the persistent ID of this virtual device. */ + @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) public @Nullable String getPersistentDeviceId() { return mVirtualDeviceInternal.getPersistentDeviceId(); } diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index d0e13cd977ef..cf274f5cbbb9 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -8,6 +8,14 @@ flag { } flag { + name: "enable_native_vdm" + namespace: "virtual_devices" + description: "Enable native VDM service" + bug: "303535376" + is_fixed_read_only: true +} + +flag { name: "dynamic_policy" namespace: "virtual_devices" description: "Enable dynamic policy API" diff --git a/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl new file mode 100644 index 000000000000..9f09d043a89b --- /dev/null +++ b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl @@ -0,0 +1,65 @@ +/* + * 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.companion.virtualnative; + +/** + * Parallel implementation of certain VirtualDeviceManager APIs that need to be exposed to native + * code. + * + * <p>These APIs are a parallel definition to the APIs in VirtualDeviceManager and/or + * VirtualDeviceManagerInternal, so they can technically diverge. However, it's good practice to + * keep these APIs in sync with each other.</p> + * + * <p>Even though the name implies otherwise, the implementation is actually in Java. The 'native' + * suffix comes from the intended usage - native framework backends that need to communicate with + * VDM for some reason.</p> + * + * <p>Because these APIs are exposed to native code that runs in the app process, they may be + * accessed by apps directly, even though they're hidden. Care should be taken to avoid exposing + * sensitive data or potential security holes.</p> + * + * @hide + */ +interface IVirtualDeviceManagerNative { + /** + * Counterpart to VirtualDeviceParams#DevicePolicy. + */ + const int DEVICE_POLICY_DEFAULT = 0; + const int DEVICE_POLICY_CUSTOM = 1; + + /** + * Counterpart to VirtualDeviceParams#PolicyType. + */ + const int POLICY_TYPE_SENSORS = 0; + const int POLICY_TYPE_AUDIO = 1; + const int POLICY_TYPE_RECENTS = 2; + const int POLICY_TYPE_ACTIVITY = 3; + + /** + * Returns the IDs for all VirtualDevices where an app with the given is running. + * + * Note that this returns only VirtualDevice IDs: if the app is not running on any virtual + * device, then an an empty array is returned. This does not include information about whether + * the app is running on the default device or not. + */ + int[] getDeviceIdsForUid(int uid); + + /** + * Returns the device policy for the given virtual device and policy type. + */ + int getDevicePolicy(int deviceId, int policyType); +}
\ No newline at end of file diff --git a/core/java/android/companion/virtualnative/OWNERS b/core/java/android/companion/virtualnative/OWNERS new file mode 100644 index 000000000000..29681045ac4a --- /dev/null +++ b/core/java/android/companion/virtualnative/OWNERS @@ -0,0 +1 @@ +include /services/companion/java/com/android/server/companion/virtual/OWNERS diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 15678a772ef4..bfc1eec829e8 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -31,6 +32,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.UserHandle; import android.permission.PermissionManager; +import android.permission.flags.Flags; import android.util.ArraySet; import com.android.internal.annotations.Immutable; @@ -163,6 +165,7 @@ public final class AttributionSource implements Parcelable { /** @hide */ @TestApi + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public AttributionSource(int uid, int pid, @Nullable String packageName, @Nullable String attributionTag, @NonNull IBinder token, @Nullable String[] renouncedPermissions, @@ -528,6 +531,7 @@ public final class AttributionSource implements Parcelable { * <p> * This device ID is used for permissions checking during attribution source validation. */ + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public int getDeviceId() { return mAttributionSourceState.deviceId; } @@ -715,6 +719,7 @@ public final class AttributionSource implements Parcelable { * * @return the builder */ + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public @NonNull Builder setDeviceId(int deviceId) { checkNotUsed(); mBuilderFieldsSet |= 0x12; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 44a9acd6ba2f..5f4c05f001ff 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2790,6 +2790,20 @@ public class Intent implements Parcelable, Cloneable { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; + + /** + * Broadcast Action: An application package that was previously in the stopped state has been + * started and is no longer considered stopped. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * </ul> + * + * <p class="note">This is a protected intent that can only be sent by the system. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED) + public static final String ACTION_PACKAGE_UNSTOPPED = "android.intent.action.PACKAGE_UNSTOPPED"; + /** * Broadcast Action: Sent to the system rollback manager when a package * needs to have rollback enabled. diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index aca88d6af033..99264150f7d0 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -308,6 +308,8 @@ interface IPackageManager { boolean isPackageQuarantinedForUser(String packageName, int userId); + boolean isPackageStoppedForUser(String packageName, int userId); + Bundle getSuspendedPackageAppExtras(String packageName, int userId); /** diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 3a9e9bf01f04..673a8a5edcba 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -371,13 +371,6 @@ public class PackageInstaller { "android.content.pm.extra.UNARCHIVE_ALL_USERS"; /** - * A list of warnings that occurred during installation. - * - * @hide - */ - public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS"; - - /** * Streaming installation pending. * Caller should make sure DataLoader is able to prepare image and reinitiate the operation. * diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 3fc515d3d37d..45338bb2c0a2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4802,6 +4802,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; @@ -9877,6 +9878,18 @@ public abstract class PackageManager { } /** + * Query if an app is currently stopped. + * + * @return {@code true} if the given package is stopped, {@code false} otherwise + * @throws NameNotFoundException if the package could not be found. + * @see ApplicationInfo#FLAG_STOPPED + */ + @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED) + public boolean isPackageStopped(@NonNull String packageName) throws NameNotFoundException { + throw new UnsupportedOperationException("isPackageStopped not implemented"); + } + + /** * Query if an app is currently quarantined. * * @return {@code true} if the given package is quarantined, {@code false} otherwise @@ -9887,7 +9900,6 @@ public abstract class PackageManager { public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException { throw new UnsupportedOperationException("isPackageQuarantined not implemented"); } - /** * Provide a hint of what the {@link ApplicationInfo#category} value should * be for the given package. diff --git a/core/java/android/content/pm/Signature.aidl b/core/java/android/content/pm/Signature.aidl deleted file mode 100644 index 36c127ad0384..000000000000 --- a/core/java/android/content/pm/Signature.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* //device/java/android/android/view/WindowManager.aidl -** -** Copyright 2007, The Android Open 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.content.pm; - -/* For the key attestation application id provider service we needed a native implementation - * of the Signature parcelable because the service is used by the native keystore. - * The native implementation is now located at - * system/security/keystore/Signature.cpp - * and - * system/security/keystore/include/keystore/Signature.h. - * and can be used by linking against libkeystore_binder. - * - * This is not the best arrangement. If you, dear reader, happen to implement native implementations - * for the package manager's parcelables, consider moving Signature.cpp/.h to your library and - * adjust keystore's dependencies accordingly. Thank you. - */ -parcelable Signature cpp_header "keystore/Signature.h"; diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java index 96af2b6caf9f..884d463e929d 100644 --- a/core/java/android/content/pm/UserProperties.java +++ b/core/java/android/content/pm/UserProperties.java @@ -49,6 +49,7 @@ public final class UserProperties implements Parcelable { private static final String ATTR_SHOW_IN_LAUNCHER = "showInLauncher"; private static final String ATTR_START_WITH_PARENT = "startWithParent"; private static final String ATTR_SHOW_IN_SETTINGS = "showInSettings"; + private static final String ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE = "hideInSettingsInQuietMode"; private static final String ATTR_INHERIT_DEVICE_POLICY = "inheritDevicePolicy"; private static final String ATTR_USE_PARENTS_CONTACTS = "useParentsContacts"; private static final String ATTR_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA = @@ -78,6 +79,7 @@ public final class UserProperties implements Parcelable { INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT, INDEX_DELETE_APP_WITH_PARENT, INDEX_ALWAYS_VISIBLE, + INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE, }) @Retention(RetentionPolicy.SOURCE) private @interface PropertyIndex { @@ -94,6 +96,7 @@ public final class UserProperties implements Parcelable { private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9; private static final int INDEX_DELETE_APP_WITH_PARENT = 10; private static final int INDEX_ALWAYS_VISIBLE = 11; + private static final int INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE = 12; /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */ private long mPropertiesPresent = 0; @@ -324,6 +327,7 @@ public final class UserProperties implements Parcelable { if (hasManagePermission) { // Add items that require MANAGE_USERS or stronger. setShowInSettings(orig.getShowInSettings()); + setHideInSettingsInQuietMode(orig.getHideInSettingsInQuietMode()); setUseParentsContacts(orig.getUseParentsContacts()); } if (hasQueryOrManagePermission) { @@ -409,6 +413,42 @@ public final class UserProperties implements Parcelable { private @ShowInSettings int mShowInSettings; /** + * Returns whether a user should be shown in the Settings app depending on the quiet mode. + * This is generally inapplicable for non-profile users. + * + * <p> {@link #getShowInSettings()} returns whether / how a user should be shown in Settings. + * However, if this behaviour should be changed based on the quiet mode of the user, then this + * property can be used. If the property is not set then the user is shown in the Settings app + * irrespective of whether the user is in quiet mode or not. If the property is set, then the + * user is shown in the Settings app only if the user is not in the quiet mode. Please note that + * this property takes effect only if {@link #getShowInSettings()} does not return + * {@link #SHOW_IN_SETTINGS_NO}. + * + * <p> The caller must have {@link android.Manifest.permission#MANAGE_USERS} to query this + * property. + * + * @return true if a profile should be shown in the Settings only when the user is not in the + * quiet mode. + * + * See also {@link #getShowInSettings()}, {@link #setShowInSettings(int)}, + * {@link ShowInSettings} + * + * @hide + */ + public boolean getHideInSettingsInQuietMode() { + if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) return mHideInSettingsInQuietMode; + if (mDefaultProperties != null) return mDefaultProperties.mHideInSettingsInQuietMode; + throw new SecurityException( + "You don't have permission to query HideInSettingsInQuietMode"); + } + /** @hide */ + public void setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) { + this.mHideInSettingsInQuietMode = hideInSettingsInQuietMode; + setPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE); + } + private boolean mHideInSettingsInQuietMode; + + /** * Returns whether a profile should be started when its parent starts (unless in quiet mode). * This only applies for users that have parents (i.e. for profiles). * @hide @@ -724,6 +764,9 @@ public final class UserProperties implements Parcelable { case ATTR_SHOW_IN_SETTINGS: setShowInSettings(parser.getAttributeInt(i)); break; + case ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE: + setHideInSettingsInQuietMode(parser.getAttributeBoolean(i)); + break; case ATTR_INHERIT_DEVICE_POLICY: setInheritDevicePolicy(parser.getAttributeInt(i)); break; @@ -777,6 +820,10 @@ public final class UserProperties implements Parcelable { if (isPresent(INDEX_SHOW_IN_SETTINGS)) { serializer.attributeInt(null, ATTR_SHOW_IN_SETTINGS, mShowInSettings); } + if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) { + serializer.attributeBoolean(null, ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE, + mHideInSettingsInQuietMode); + } if (isPresent(INDEX_INHERIT_DEVICE_POLICY)) { serializer.attributeInt(null, ATTR_INHERIT_DEVICE_POLICY, mInheritDevicePolicy); @@ -823,6 +870,7 @@ public final class UserProperties implements Parcelable { dest.writeInt(mShowInLauncher); dest.writeBoolean(mStartWithParent); dest.writeInt(mShowInSettings); + dest.writeBoolean(mHideInSettingsInQuietMode); dest.writeInt(mInheritDevicePolicy); dest.writeBoolean(mUseParentsContacts); dest.writeBoolean(mUpdateCrossProfileIntentFiltersOnOTA); @@ -845,6 +893,7 @@ public final class UserProperties implements Parcelable { mShowInLauncher = source.readInt(); mStartWithParent = source.readBoolean(); mShowInSettings = source.readInt(); + mHideInSettingsInQuietMode = source.readBoolean(); mInheritDevicePolicy = source.readInt(); mUseParentsContacts = source.readBoolean(); mUpdateCrossProfileIntentFiltersOnOTA = source.readBoolean(); @@ -881,6 +930,7 @@ public final class UserProperties implements Parcelable { private @ShowInLauncher int mShowInLauncher = SHOW_IN_LAUNCHER_WITH_PARENT; private boolean mStartWithParent = false; private @ShowInSettings int mShowInSettings = SHOW_IN_SETTINGS_WITH_PARENT; + private boolean mHideInSettingsInQuietMode = false; private @InheritDevicePolicy int mInheritDevicePolicy = INHERIT_DEVICE_POLICY_NO; private boolean mUseParentsContacts = false; private boolean mUpdateCrossProfileIntentFiltersOnOTA = false; @@ -910,6 +960,12 @@ public final class UserProperties implements Parcelable { return this; } + /** Sets the value for {@link #mHideInSettingsInQuietMode} */ + public Builder setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) { + mHideInSettingsInQuietMode = hideInSettingsInQuietMode; + return this; + } + /** Sets the value for {@link #mInheritDevicePolicy}*/ public Builder setInheritDevicePolicy( @InheritDevicePolicy int inheritRestrictionsDevicePolicy) { @@ -972,6 +1028,7 @@ public final class UserProperties implements Parcelable { mShowInLauncher, mStartWithParent, mShowInSettings, + mHideInSettingsInQuietMode, mInheritDevicePolicy, mUseParentsContacts, mUpdateCrossProfileIntentFiltersOnOTA, @@ -989,6 +1046,7 @@ public final class UserProperties implements Parcelable { @ShowInLauncher int showInLauncher, boolean startWithParent, @ShowInSettings int showInSettings, + boolean hideInSettingsInQuietMode, @InheritDevicePolicy int inheritDevicePolicy, boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA, @CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl, @@ -1001,6 +1059,7 @@ public final class UserProperties implements Parcelable { setShowInLauncher(showInLauncher); setStartWithParent(startWithParent); setShowInSettings(showInSettings); + setHideInSettingsInQuietMode(hideInSettingsInQuietMode); setInheritDevicePolicy(inheritDevicePolicy); setUseParentsContacts(useParentsContacts); setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA); diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index ea0f049cece3..3ec239c7125e 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -8,8 +8,23 @@ flag { } flag { + name: "save_global_and_guest_restrictions_on_system_user_xml_read_only" + namespace: "multiuser" + description: "Save guest and device policy global restrictions on the SYSTEM user's XML file. (Read only flag)" + bug: "301067944" + is_fixed_read_only: true +} + +flag { name: "bind_wallpaper_service_on_its_own_thread_during_a_user_switch" namespace: "multiuser" description: "Bind wallpaper service on its own thread instead of system_server's main handler during a user switch." bug: "302100344" } + +flag { + name: "support_communal_profile" + namespace: "multiuser" + description: "Framework support for communal profile." + bug: "285426179" +} diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java index 231e4bc4ac75..1b130a9fb64d 100644 --- a/core/java/android/credentials/GetCandidateCredentialsResponse.java +++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java @@ -53,6 +53,15 @@ public final class GetCandidateCredentialsResponse implements Parcelable { mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList); } + /** + * Returns candidate provider data list. + * + * @hide + */ + public List<GetCredentialProviderData> getCandidateProviderDataList() { + return mCandidateProviderDataList; + } + protected GetCandidateCredentialsResponse(Parcel in) { List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>(); in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR); diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig index 0d0305b5b626..9b819a792f8f 100644 --- a/core/java/android/credentials/flags.aconfig +++ b/core/java/android/credentials/flags.aconfig @@ -5,4 +5,11 @@ flag { name: "settings_activity_enabled" description: "Enable the Credential Manager Settings Activity APIs" bug: "300014059" -}
\ No newline at end of file +} + +flag { + namespace: "credential_manager" + name: "instant_apps_enabled" + description: "Enables Credential Manager to work with Instant Apps" + bug: "302190269" +} diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index af448f0c4917..490ff640885e 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -20,8 +20,10 @@ import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; +import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -53,6 +55,7 @@ import java.util.List; import java.util.concurrent.Executor; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; /** @@ -748,7 +751,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * A wrapper class for the cryptographic operations supported by BiometricPrompt. * * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, - * {@link IdentityCredential}, and {@link PresentationSession}. + * {@link IdentityCredential}, {@link PresentationSession} and {@link KeyAgreement}. * * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and * time-based. This is specified during key creation via the timeout parameter of the @@ -793,6 +796,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan super(session); } + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public CryptoObject(@NonNull KeyAgreement keyAgreement) { + super(keyAgreement); + } + /** * Get {@link Signature} object. * @return {@link Signature} object or null if this doesn't contain one. @@ -834,6 +842,15 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan public @Nullable PresentationSession getPresentationSession() { return super.getPresentationSession(); } + + /** + * Get {@link KeyAgreement} object. + * @return {@link KeyAgreement} object or null if this doesn't contain one. + */ + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public @Nullable KeyAgreement getKeyAgreement() { + return super.getKeyAgreement(); + } } /** diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java index 267ef3637ce7..151f819329c9 100644 --- a/core/java/android/hardware/biometrics/CryptoObject.java +++ b/core/java/android/hardware/biometrics/CryptoObject.java @@ -16,6 +16,9 @@ package android.hardware.biometrics; +import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.security.identity.IdentityCredential; import android.security.identity.PresentationSession; @@ -24,6 +27,7 @@ import android.security.keystore2.AndroidKeyStoreProvider; import java.security.Signature; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; /** @@ -62,6 +66,11 @@ public class CryptoObject { mCrypto = session; } + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public CryptoObject(@NonNull KeyAgreement keyAgreement) { + mCrypto = keyAgreement; + } + /** * Get {@link Signature} object. * @return {@link Signature} object or null if this doesn't contain one. @@ -105,6 +114,15 @@ public class CryptoObject { } /** + * Get {@link PresentationSession} object. + * @return {@link PresentationSession} object or null if this doesn't contain one. + */ + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public KeyAgreement getKeyAgreement() { + return mCrypto instanceof KeyAgreement ? (KeyAgreement) mCrypto : null; + } + + /** * @hide * @return the opId associated with this object or 0 if none */ diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 990ebc5fdbcd..f347909348c8 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.HdrCapabilities.HdrType; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; @@ -27,7 +28,6 @@ import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -377,7 +377,7 @@ public final class DisplayManager { * @see #createVirtualDisplay * @hide */ - @SuppressLint("UnflaggedApi") + @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS) @SystemApi public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7; diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index f59437795b9c..5bfda70f03de 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -24,12 +24,14 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE; +import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_HAS_ENROLLED_FINGERPRINTS; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_IS_HARDWARE_DETECTED; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -76,6 +78,7 @@ import java.util.List; import java.util.concurrent.Executor; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; /** @@ -312,6 +315,16 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public PresentationSession getPresentationSession() { return super.getPresentationSession(); } + + /** + * Get {@link KeyAgreement} object. + * @return {@link KeyAgreement} object or null if this doesn't contain one. + * @hide + */ + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public KeyAgreement getKeyAgreement() { + return super.getKeyAgreement(); + } } /** diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java index bbfed24f9dc1..883f157bbfe5 100644 --- a/core/java/android/hardware/input/KeyboardLayout.java +++ b/core/java/android/hardware/input/KeyboardLayout.java @@ -82,8 +82,8 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo DVORAK(4, LAYOUT_TYPE_DVORAK), COLEMAK(5, LAYOUT_TYPE_COLEMAK), WORKMAN(6, LAYOUT_TYPE_WORKMAN), - TURKISH_F(7, LAYOUT_TYPE_TURKISH_F), - TURKISH_Q(8, LAYOUT_TYPE_TURKISH_Q), + TURKISH_Q(7, LAYOUT_TYPE_TURKISH_Q), + TURKISH_F(8, LAYOUT_TYPE_TURKISH_F), EXTENDED(9, LAYOUT_TYPE_EXTENDED); private final int mValue; diff --git a/core/java/android/os/OomKillRecord.java b/core/java/android/os/OomKillRecord.java new file mode 100644 index 000000000000..151a65fdfaf5 --- /dev/null +++ b/core/java/android/os/OomKillRecord.java @@ -0,0 +1,63 @@ +/* + * 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.os; + + +/** + * Expected data to get back from the OOM event's file. + * Note that this should be equivalent to the struct <b>OomKill</b> inside + * <pre> + * system/memory/libmeminfo/libmemevents/include/memevents.h + * </pre> + * + * @hide + */ +public final class OomKillRecord { + private long mTimeStampInMillis; + private int mPid; + private int mUid; + private String mProcessName; + private short mOomScoreAdj; + + public OomKillRecord(long timeStampInMillis, int pid, int uid, + String processName, short oomScoreAdj) { + this.mTimeStampInMillis = timeStampInMillis; + this.mPid = pid; + this.mUid = uid; + this.mProcessName = processName; + this.mOomScoreAdj = oomScoreAdj; + } + + public long getTimestampMilli() { + return mTimeStampInMillis; + } + + public int getPid() { + return mPid; + } + + public int getUid() { + return mUid; + } + + public String getProcessName() { + return mProcessName; + } + + public short getOomScoreAdj() { + return mOomScoreAdj; + } +} diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index c01ef3d79bb9..88f62f327fdd 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -12,4 +12,18 @@ flag { name: "haptics_customization_enabled" description: "Enables the haptics customization feature" bug: "241918098" -}
\ No newline at end of file +} + +flag { + namespace: "haptics" + name: "haptics_customization_ringtone_v2_enabled" + description: "Enables the usage of the new RingtoneV2 class" + bug: "241918098" +} + +flag { + namespace: "haptics" + name: "enable_vibration_serialization_apis" + description: "Enables the APIs for vibration serialization/deserialization." + bug: "245129509" +} diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java index ded74eab149e..3d1deea57f14 100644 --- a/core/java/android/os/vibrator/persistence/ParsedVibration.java +++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java @@ -16,6 +16,7 @@ package android.os.vibrator.persistence; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -34,6 +35,7 @@ import java.util.List; * * @hide */ +@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS) @TestApi public class ParsedVibration { private final List<VibrationEffect> mEffects; diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java index e08cc4262bed..fed1053e2a12 100644 --- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java +++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java @@ -16,6 +16,7 @@ package android.os.vibrator.persistence; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -115,6 +116,7 @@ import java.util.List; * * @hide */ +@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS) @TestApi public final class VibrationXmlParser { private static final String TAG = "VibrationXmlParser"; diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java index 1cdfa4f74dbd..28804544edaf 100644 --- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java +++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java @@ -16,6 +16,7 @@ package android.os.vibrator.persistence; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; @@ -42,6 +43,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS) @TestApi public final class VibrationXmlSerializer { diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 5626b948618f..d8534dd5fbc1 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -2,6 +2,7 @@ package: "android.permission.flags" flag { name: "device_aware_permission_apis" + is_fixed_read_only: true namespace: "permissions" description: "enable device aware permission APIs" bug: "274852670" @@ -19,4 +20,4 @@ flag { namespace: "permissions" description: "enable role controller in system server" bug: "302562590" -}
\ No newline at end of file +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d09f0a831f30..b19a034d4628 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4866,7 +4866,8 @@ public final class Settings { "display_color_mode_vendor_hint"; /** - * The user selected min refresh rate in frames per second. + * The user selected min refresh rate in frames per second. If infinite, the user wants + * the highest possible refresh rate. * * If this isn't set, 0 will be used. * @hide @@ -4875,7 +4876,8 @@ public final class Settings { public static final String MIN_REFRESH_RATE = "min_refresh_rate"; /** - * The user selected peak refresh rate in frames per second. + * The user selected peak refresh rate in frames per second. If infinite, the user wants + * the highest possible refresh rate. * * If this isn't set, the system falls back to a device specific default. * @hide @@ -5352,6 +5354,37 @@ public final class Settings { public static final Uri NOTIFICATION_SOUND_CACHE_URI = getUriFor(NOTIFICATION_SOUND_CACHE); /** + * When enabled, notifications attention effects: sound, vibration, flashing + * will have a cooldown timer. + * + * The value 1 - enable, 0 - disable + * @hide + */ + public static final String NOTIFICATION_COOLDOWN_ENABLED = + "notification_cooldown_enabled"; + + /** + * When enabled, notification cooldown will apply to all notifications. + * Otherwise cooldown will only apply to conversations. + * + * The value 1 - enable, 0 - disable + * Only valid if {@code NOTIFICATION_COOLDOWN_ENABLED} is enabled. + * @hide + */ + public static final String NOTIFICATION_COOLDOWN_ALL = + "notification_cooldown_all"; + + /** + * When enabled, notification attention effects will be restricted to vibration only + * as long as the screen is unlocked. + * + * The value 1 - enable, 0 - disable + * @hide + */ + public static final String NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED = + "notification_cooldown_vibrate_unlocked"; + + /** * Persistent store for the system-wide default alarm alert. * * @see #RINGTONE @@ -5664,6 +5697,37 @@ public final class Settings { public static final String SHOW_ROTARY_INPUT = "show_rotary_input"; /** + * The screen backlight brightness for automatic mode. + * + * <p>Value should be one of: + * <ul> + * <li>SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT + * <li>SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL + * <li>SCREEN_BRIGHTNESS_AUTOMATIC_DIM + * </ul> + * @hide + */ + public static final String SCREEN_BRIGHTNESS_FOR_ALS = "screen_brightness_for_als"; + + /** + * SCREEN_BRIGHTNESS_FOR_ALS value for automatic bright. + * @hide + */ + public static final int SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT = 1; + + /** + * SCREEN_BRIGHTNESS_FOR_ALS value for automatic normal. + * @hide + */ + public static final int SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL = 2; + + /** + * SCREEN_BRIGHTNESS_FOR_ALS value for automatic dim. + * @hide + */ + public static final int SCREEN_BRIGHTNESS_AUTOMATIC_DIM = 3; + + /** * Log raw orientation data from * {@link com.android.server.policy.WindowOrientationListener} for use with the * orientationplot.py tool. @@ -11147,6 +11211,12 @@ public final class Settings { public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving"; /** + * Volume dialog timeout in ms. + * @hide + */ + public static final String VOLUME_DIALOG_DISMISS_TIMEOUT = "volume_dialog_dismiss_timeout"; + + /** * What behavior should be invoked when the volume hush gesture is triggered * One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE. * @@ -11576,6 +11646,45 @@ public final class Settings { "accessibility_magnification_joystick_enabled"; /** + * Controls magnification enable gesture. Accessibility magnification can have one or more + * enable gestures. + * + * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE + * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP + * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP + * @hide + */ + public static final String ACCESSIBILITY_MAGNIFICATION_GESTURE = + "accessibility_magnification_gesture"; + + /** + * Magnification enable gesture value that is a default value. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE = 0x0; + + /** + * Magnification enable gesture value is single finger triple tap. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP = 0x1; + + /** + * Magnification enable gesture value is two finger triple tap. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP = 0x2; + + /** + * Magnification enable gesture values include single finger triple tap and two finger + * triple tap. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL = + ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP + | ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP; + + /** * Controls magnification capability. Accessibility magnification is capable of at least one * of the magnification modes. * @@ -12393,6 +12502,13 @@ public final class Settings { "wireless_charging_started_sound"; /** + * Whether to auto enable reverse charging once plugged-in. + * @hide + */ + public static final String REVERSE_CHARGING_AUTO_ON = + "settings_key_reverse_charging_auto_turn_on"; + + /** * URI for "wired charging started" sound. * @hide */ diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.java b/core/java/android/security/keymaster/KeyAttestationApplicationId.java deleted file mode 100644 index 670f30e1b04b..000000000000 --- a/core/java/android/security/keymaster/KeyAttestationApplicationId.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open 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.security.keymaster; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * @hide - * The information aggregated by this class is used by keystore to identify a caller of the - * keystore API toward a remote party. It aggregates multiple PackageInfos because keystore - * can only determine a caller by uid granularity, and a uid can be shared by multiple packages. - * The remote party must decide if it trusts all of the packages enough to consider the - * confidentiality of the key material in question intact. - */ -public class KeyAttestationApplicationId implements Parcelable { - private final KeyAttestationPackageInfo[] mAttestationPackageInfos; - - /** - * @param mAttestationPackageInfos - */ - public KeyAttestationApplicationId(KeyAttestationPackageInfo[] mAttestationPackageInfos) { - super(); - this.mAttestationPackageInfos = mAttestationPackageInfos; - } - - /** - * @return the mAttestationPackageInfos - */ - public KeyAttestationPackageInfo[] getAttestationPackageInfos() { - return mAttestationPackageInfos; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeTypedArray(mAttestationPackageInfos, flags); - } - - public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationApplicationId> CREATOR - = new Parcelable.Creator<KeyAttestationApplicationId>() { - @Override - public KeyAttestationApplicationId createFromParcel(Parcel source) { - return new KeyAttestationApplicationId(source); - } - - @Override - public KeyAttestationApplicationId[] newArray(int size) { - return new KeyAttestationApplicationId[size]; - } - }; - - KeyAttestationApplicationId(Parcel source) { - mAttestationPackageInfos = source.createTypedArray(KeyAttestationPackageInfo.CREATOR); - } -} diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java deleted file mode 100644 index c0b8d8dfd4d9..000000000000 --- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open 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.security.keymaster; - -import android.content.pm.Signature; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * @hide - * This class constitutes and excerpt from the PackageManager's PackageInfo for the purpose of - * key attestation. It is part of the KeyAttestationApplicationId, which is used by - * keystore to identify the caller of the keystore API towards a remote party. - */ -public class KeyAttestationPackageInfo implements Parcelable { - private final String mPackageName; - private final long mPackageVersionCode; - private final Signature[] mPackageSignatures; - - /** - * @param mPackageName - * @param mPackageVersionCode - * @param mPackageSignatures - */ - public KeyAttestationPackageInfo( - String mPackageName, long mPackageVersionCode, Signature[] mPackageSignatures) { - super(); - this.mPackageName = mPackageName; - this.mPackageVersionCode = mPackageVersionCode; - this.mPackageSignatures = mPackageSignatures; - } - /** - * @return the mPackageName - */ - public String getPackageName() { - return mPackageName; - } - /** - * @return the mPackageVersionCode - */ - public long getPackageVersionCode() { - return mPackageVersionCode; - } - /** - * @return the mPackageSignatures - */ - public Signature[] getPackageSignatures() { - return mPackageSignatures; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mPackageName); - dest.writeLong(mPackageVersionCode); - dest.writeTypedArray(mPackageSignatures, flags); - } - - public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationPackageInfo> CREATOR - = new Parcelable.Creator<KeyAttestationPackageInfo>() { - @Override - public KeyAttestationPackageInfo createFromParcel(Parcel source) { - return new KeyAttestationPackageInfo(source); - } - - @Override - public KeyAttestationPackageInfo[] newArray(int size) { - return new KeyAttestationPackageInfo[size]; - } - }; - - private KeyAttestationPackageInfo(Parcel source) { - mPackageName = source.readString(); - mPackageVersionCode = source.readLong(); - mPackageSignatures = source.createTypedArray(Signature.CREATOR); - } -} diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 115894f586f2..a29bf7a06334 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -18,6 +18,7 @@ package android.service.autofill; import static android.view.autofill.Helper.sDebug; +import android.annotation.Hide; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,6 +27,7 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.ClipData; import android.content.IntentSender; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; @@ -187,6 +189,9 @@ public final class Dataset implements Parcelable { @Nullable private final InlinePresentation mInlinePresentation; @Nullable private final InlinePresentation mInlineTooltipPresentation; private final IntentSender mAuthentication; + + @Nullable private final Bundle mAuthenticationExtras; + @Nullable String mId; /** @@ -224,6 +229,7 @@ public final class Dataset implements Parcelable { mInlinePresentation = inlinePresentation; mInlineTooltipPresentation = inlineTooltipPresentation; mAuthentication = authentication; + mAuthenticationExtras = null; mId = id; } @@ -246,6 +252,7 @@ public final class Dataset implements Parcelable { mInlinePresentation = dataset.mInlinePresentation; mInlineTooltipPresentation = dataset.mInlineTooltipPresentation; mAuthentication = dataset.mAuthentication; + mAuthenticationExtras = dataset.mAuthenticationExtras; mId = dataset.mId; mAutofillDatatypes = dataset.mAutofillDatatypes; } @@ -264,6 +271,7 @@ public final class Dataset implements Parcelable { mInlinePresentation = builder.mInlinePresentation; mInlineTooltipPresentation = builder.mInlineTooltipPresentation; mAuthentication = builder.mAuthentication; + mAuthenticationExtras = builder.mAuthenticationExtras; mId = builder.mId; mAutofillDatatypes = builder.mAutofillDatatypes; } @@ -345,6 +353,12 @@ public final class Dataset implements Parcelable { } /** @hide */ + @Hide + public @Nullable Bundle getAuthenticationExtras() { + return mAuthenticationExtras; + } + + /** @hide */ @TestApi public boolean isEmpty() { return mFieldIds == null || mFieldIds.isEmpty(); @@ -401,6 +415,9 @@ public final class Dataset implements Parcelable { if (mAuthentication != null) { builder.append(", hasAuthentication"); } + if (mAuthenticationExtras != null) { + builder.append(", hasAuthenticationExtras"); + } if (mAutofillDatatypes != null) { builder.append(", autofillDatatypes=").append(mAutofillDatatypes); } @@ -454,6 +471,8 @@ public final class Dataset implements Parcelable { @Nullable private InlinePresentation mInlinePresentation; @Nullable private InlinePresentation mInlineTooltipPresentation; private IntentSender mAuthentication; + + private Bundle mAuthenticationExtras; private boolean mDestroyed; @Nullable private String mId; @@ -624,6 +643,25 @@ public final class Dataset implements Parcelable { } /** + * Sets extras to be associated with the {@code authentication} intent sender, to be + * set on the intent that is fired through the intent sender. + * + * Autofill providers can set any extras they wish to receive directly on the intent + * that is used to create the {@code authentication}. This is an internal API, to be + * used by the platform to associate data with a given dataset. These extras will be + * merged with the {@code clientState} and sent as part of the fill in intent when + * the {@code authentication} intentSender is invoked. + * + * @hide + */ + @Hide + public @NonNull Builder setAuthenticationExtras(@Nullable Bundle authenticationExtra) { + throwIfDestroyed(); + mAuthenticationExtras = authenticationExtra; + return this; + } + + /** * Sets the id for the dataset so its usage can be tracked. * * <p>Dataset usage can be tracked for 2 purposes: diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java index 3b757d6e3dd3..33978be9fb82 100644 --- a/core/java/android/service/controls/Control.java +++ b/core/java/android/service/controls/Control.java @@ -50,7 +50,7 @@ import java.lang.annotation.RetentionPolicy; * and zone. Some of these values are defined by the user and/or the {@link ControlsProviderService} * and will be used to display the control as well as group them for management. * <p> - * Each object will have an associated {@link DeviceTypes.DeviceType}. This will determine the icons and colors + * Each object will have an associated {@link DeviceTypes}. This will determine the icons and colors * used to display it. * <p> * An {@link Intent} linking to the provider Activity that expands on this {@link Control} and @@ -420,7 +420,7 @@ public final class Control implements Parcelable { * This fixes the values relating to state of the {@link Control} as required by * {@link ControlsProviderService#createPublisherForAllAvailable}: * <ul> - * <li> Status: {@link Status#STATUS_UNKNOWN} + * <li> Status: {@link #STATUS_UNKNOWN} * <li> Control template: {@link ControlTemplate#getNoTemplateObject} * <li> Status text: {@code ""} * <li> Auth Required: {@code true} @@ -620,7 +620,7 @@ public final class Control implements Parcelable { * <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN} * <li> Title: {@code ""} * <li> Subtitle: {@code ""} - * <li> Status: {@link Status#STATUS_UNKNOWN} + * <li> Status: {@link #STATUS_UNKNOWN} * <li> Control template: {@link ControlTemplate#getNoTemplateObject} * <li> Status text: {@code ""} * <li> Auth Required: {@code true} diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index fce87db2bbf0..0272bb9842d6 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -155,7 +155,7 @@ public abstract class ControlsProviderService extends Service { * The user has interacted with a Control. The action is dictated by the type of * {@link ControlAction} that was sent. A response can be sent via * {@link Consumer#accept}, with the Integer argument being one of the provided - * {@link ControlAction.ResponseResult}. The Integer should indicate whether the action + * {@link ControlAction} response results. The Integer should indicate whether the action * was received successfully, or if additional prompts should be presented to * the user. Any visual control updates should be sent via the Publisher. diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java index 10f526d6565c..4e382222547d 100644 --- a/core/java/android/service/controls/actions/ControlAction.java +++ b/core/java/android/service/controls/actions/ControlAction.java @@ -154,7 +154,7 @@ public abstract class ControlAction { public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5; /** - * The {@link ActionType} associated with this class. + * The action type associated with this class. */ public abstract @ActionType int getActionType(); diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java index 3902d6af69e7..0dd950d596f6 100644 --- a/core/java/android/service/controls/templates/ControlTemplate.java +++ b/core/java/android/service/controls/templates/ControlTemplate.java @@ -137,7 +137,7 @@ public abstract class ControlTemplate { } /** - * The {@link TemplateType} associated with this class. + * The template type associated with this class. */ public abstract @TemplateType int getTemplateType(); diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java index be7b72202b3f..a2b0a669c768 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -153,6 +153,18 @@ public abstract class CredentialProviderService extends Service { public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST"; + /** + * The key to autofillId associated with the requested credential option and the corresponding + * credential entry. The associated autofillId will be contained inside the candidate query + * bundle of {@link android.credentials.CredentialOption} if requested through the + * {@link com.android.credentialmanager.autofill.CredentialAutofillService}. The resulting + * credential entry will contain the autofillId inside its framework extras intent. + * + * @hide + */ + public static final String EXTRA_AUTOFILL_ID = + "android.service.credentials.extra.AUTOFILL_ID"; + private static final String TAG = "CredProviderService"; /** diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 07f8aac43bf5..1cfff14b2d7c 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -2007,6 +2007,20 @@ public abstract class NotificationListenerService extends Service { return mSmartActions == null ? Collections.emptyList() : mSmartActions; } + + /** + * Sets the smart {@link Notification.Action} objects. + * + * Should ONLY be used in cases where smartActions need to be removed from, then restored + * on, Ranking objects during Parceling, when they are transmitted between processes via + * Shared Memory. + * + * @hide + */ + public void setSmartActions(@Nullable ArrayList<Notification.Action> smartActions) { + mSmartActions = smartActions; + } + /** * Returns a list of smart replies that can be added by the * {@link NotificationAssistantService} @@ -2353,11 +2367,9 @@ public abstract class NotificationListenerService extends Service { /** * Get a reference to the actual Ranking object corresponding to the key. - * Used only by unit tests. * * @hide */ - @VisibleForTesting public Ranking getRawRankingObject(String key) { return mRankings.get(key); } diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index f3b4c6da4a01..79c8fb4a6620 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -17,7 +17,8 @@ package android.service.notification; import android.annotation.Nullable; import android.annotation.SuppressLint; -import android.annotation.TestApi; +import android.app.Notification; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.SharedMemory; @@ -29,13 +30,15 @@ import androidx.annotation.NonNull; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; /** * Represents an update to notification rankings. + * * @hide */ @SuppressLint({"ParcelNotFinal", "ParcelCreator"}) -@TestApi public class NotificationRankingUpdate implements Parcelable { private final NotificationListenerService.RankingMap mRankingMap; @@ -64,6 +67,7 @@ public class NotificationRankingUpdate implements Parcelable { // The ranking map should be stored in shared memory when it is parceled, so we // unwrap the SharedMemory object. mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class); + Bundle smartActionsBundle = in.readBundle(getClass().getClassLoader()); // In the case that the ranking map can't be read, readParcelable may return null. // In this case, we set mRankingMap to null; @@ -82,15 +86,20 @@ public class NotificationRankingUpdate implements Parcelable { mapParcel.unmarshall(payload, 0, payload.length); mapParcel.setDataPosition(0); - mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(), - android.service.notification.NotificationListenerService.RankingMap.class); + mRankingMap = + mapParcel.readParcelable( + getClass().getClassLoader(), + NotificationListenerService.RankingMap.class); + + addSmartActionsFromBundleToRankingMap(smartActionsBundle); + } catch (ErrnoException e) { // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to // avoid crashes; change to Log.wtf. throw new RuntimeException(e); } finally { mapParcel.recycle(); - if (buffer != null) { + if (buffer != null && mRankingMapFd != null) { mRankingMapFd.unmap(buffer); mRankingMapFd.close(); } @@ -102,10 +111,33 @@ public class NotificationRankingUpdate implements Parcelable { } /** + * For each key in the rankingMap, extracts lists of smart actions stored in the provided + * bundle and adds them to the corresponding Ranking object in the provided ranking + * map, then returns the rankingMap. + * + * @hide + */ + private void addSmartActionsFromBundleToRankingMap(Bundle smartActionsBundle) { + if (smartActionsBundle == null) { + return; + } + + String[] rankingMapKeys = mRankingMap.getOrderedKeys(); + for (int i = 0; i < rankingMapKeys.length; i++) { + String key = rankingMapKeys[i]; + ArrayList<Notification.Action> smartActions = + smartActionsBundle.getParcelableArrayList(key, Notification.Action.class); + // Get the ranking object from the ranking map. + NotificationListenerService.Ranking ranking = mRankingMap.getRawRankingObject(key); + ranking.setSmartActions(smartActions); + } + } + + /** * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing. + * * @hide */ - @TestApi public final boolean isFdNotNullAndClosed() { return mRankingMapFd != null && mRankingMapFd.getFd() == -1; } @@ -145,9 +177,45 @@ public class NotificationRankingUpdate implements Parcelable { if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { final Parcel mapParcel = Parcel.obtain(); + ArrayList<NotificationListenerService.Ranking> marshalableRankings = new ArrayList<>(); + Bundle smartActionsBundle = new Bundle(); + + // We need to separate the SmartActions from the RankingUpdate objects. + // SmartActions can contain PendingIntents, which cannot be marshalled, + // so we extract them to send separately. + String[] rankingMapKeys = mRankingMap.getOrderedKeys(); + for (int i = 0; i < rankingMapKeys.length; i++) { + String key = rankingMapKeys[i]; + NotificationListenerService.Ranking ranking = mRankingMap.getRawRankingObject(key); + + // Removes the SmartActions and stores them in a separate map. + // Note that getSmartActions returns a Collections.emptyList() if there are no + // smart actions, and we don't want to needlessly store an empty list object, so we + // check for null before storing. + List<Notification.Action> smartActions = ranking.getSmartActions(); + if (!smartActions.isEmpty()) { + smartActionsBundle.putParcelableList(key, smartActions); + } + + // Create a copy of the ranking object that doesn't have the smart actions. + NotificationListenerService.Ranking rankingCopy = + new NotificationListenerService.Ranking(); + rankingCopy.populate(ranking); + rankingCopy.setSmartActions(null); + marshalableRankings.add(rankingCopy); + } + + // Create a new marshalable RankingMap. + NotificationListenerService.RankingMap marshalableRankingMap = + new NotificationListenerService.RankingMap( + marshalableRankings.toArray( + new NotificationListenerService.Ranking[0] + ) + ); + try { // Parcels the ranking map and measures its size. - mapParcel.writeParcelable(mRankingMap, flags); + mapParcel.writeParcelable(marshalableRankingMap, flags); int mapSize = mapParcel.dataSize(); // Creates a new SharedMemory object with enough space to hold the ranking map. @@ -158,15 +226,14 @@ public class NotificationRankingUpdate implements Parcelable { // Gets a read/write buffer mapping the entire shared memory region. final ByteBuffer buffer = mRankingMapFd.mapReadWrite(); - // Puts the ranking map into the shared memory region buffer. buffer.put(mapParcel.marshall(), 0, mapSize); - // Protects the region from being written to, by setting it to be read-only. mRankingMapFd.setProtect(OsConstants.PROT_READ); - // Puts the SharedMemory object in the parcel. out.writeParcelable(mRankingMapFd, flags); + // Writes the Parceled smartActions separately. + out.writeBundle(smartActionsBundle); } catch (ErrnoException e) { // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to // avoid crashes; change to Log.wtf. @@ -180,8 +247,8 @@ public class NotificationRankingUpdate implements Parcelable { } /** - * @hide - */ + * @hide + */ public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR = new Parcelable.Creator<NotificationRankingUpdate>() { public NotificationRankingUpdate createFromParcel(Parcel parcel) { diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig new file mode 100644 index 000000000000..c414ef8a6826 --- /dev/null +++ b/core/java/android/service/voice/flags/flags.aconfig @@ -0,0 +1,8 @@ +package: "android.service.voice.flags" + +flag { + name: "allow_training_data_egress_from_hds" + namespace: "machine_learning" + description: "This flag allows the hotword detection service to egress training data to the default assistant." + bug: "296074924" +} diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index f6309f2c9860..cf1156db55e5 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -401,7 +401,7 @@ public class PhoneStateListener { /** * Listen for call disconnect causes which contains {@link DisconnectCause} and - * {@link PreciseDisconnectCause}. + * the precise disconnect cause. * * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} * or the calling app has carrier privileges @@ -851,8 +851,8 @@ public class PhoneStateListener { * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * - * @param disconnectCause {@link DisconnectCause}. - * @param preciseDisconnectCause {@link PreciseDisconnectCause}. + * @param disconnectCause the disconnect cause + * @param preciseDisconnectCause the precise disconnect cause * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead. */ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java index fb2d7714d402..7b48a16c2227 100644 --- a/core/java/android/telephony/SubscriptionPlan.java +++ b/core/java/android/telephony/SubscriptionPlan.java @@ -221,7 +221,7 @@ public final class SubscriptionPlan implements Parcelable { } /** - * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to. + * Return an array containing all network types this SubscriptionPlan applies to. * @see TelephonyManager for network types values */ public @NonNull @NetworkType int[] getNetworkTypes() { @@ -365,7 +365,7 @@ public final class SubscriptionPlan implements Parcelable { * Set the network types this SubscriptionPlan applies to. By default the plan will apply * to all network types. An empty array means this plan applies to no network types. * - * @param networkTypes an array of all {@link NetworkType}s that apply to this plan. + * @param networkTypes an array of all network types that apply to this plan. * @see TelephonyManager for network type values */ public @NonNull Builder setNetworkTypes(@NonNull @NetworkType int[] networkTypes) { diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index 7ada058e8e80..19bcf28d6b83 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -948,8 +948,8 @@ public class TelephonyCallback { * subscription ID. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * - * @param disconnectCause {@link DisconnectCause}. - * @param preciseDisconnectCause {@link PreciseDisconnectCause}. + * @param disconnectCause the disconnect cause + * @param preciseDisconnectCause the precise disconnect cause */ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause, diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index e2c5539141a4..0063d13cb3ab 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -238,7 +238,7 @@ public class TelephonyRegistryManager { } /** - * To check the SDK version for {@link #listenFromListener}. + * To check the SDK version for {@code #listenFromListener}. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P) diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 2b3a081ceff6..8862f1d74ab1 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -16,6 +16,10 @@ package android.text; +import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN; +import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; + +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; @@ -280,6 +284,7 @@ public class DynamicLayout extends Layout { * @see android.widget.TextView#setLineBreakWordStyle */ @NonNull + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) { mLineBreakConfig = lineBreakConfig; return this; @@ -303,6 +308,7 @@ public class DynamicLayout extends Layout { * @see Layout.Builder#setUseBoundsForWidth(boolean) */ @NonNull + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public Builder setUseBoundsForWidth(boolean useBoundsForWidth) { mUseBoundsForWidth = useBoundsForWidth; return this; diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index c5857347fd45..94c8eaf13ccb 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -29,6 +29,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontFamily.Builder.VariableFontFamilyType; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; +import android.icu.util.ULocale; import android.os.Build; import android.os.LocaleList; import android.os.Parcel; @@ -39,6 +40,7 @@ import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Objects; @@ -58,6 +60,7 @@ public final class FontConfig implements Parcelable { private final @NonNull List<FontFamily> mFamilies; private final @NonNull List<Alias> mAliases; private final @NonNull List<NamedFamilyList> mNamedFamilyLists; + private final @NonNull List<Customization.LocaleFallback> mLocaleFallbackCustomizations; private final long mLastModifiedTimeMillis; private final int mConfigVersion; @@ -71,10 +74,12 @@ public final class FontConfig implements Parcelable { */ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, @NonNull List<NamedFamilyList> namedFamilyLists, + @NonNull List<Customization.LocaleFallback> localeFallbackCustomizations, long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { mFamilies = families; mAliases = aliases; mNamedFamilyLists = namedFamilyLists; + mLocaleFallbackCustomizations = localeFallbackCustomizations; mLastModifiedTimeMillis = lastModifiedTimeMillis; mConfigVersion = configVersion; } @@ -84,7 +89,8 @@ public final class FontConfig implements Parcelable { */ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases, long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) { - this(families, aliases, Collections.emptyList(), lastModifiedTimeMillis, configVersion); + this(families, aliases, Collections.emptyList(), Collections.emptyList(), + lastModifiedTimeMillis, configVersion); } @@ -113,6 +119,18 @@ public final class FontConfig implements Parcelable { } /** + * Returns a locale fallback customizations. + * + * This field is used for creating the system fallback in the system server. This field is + * always empty in the application process. + * + * @hide + */ + public @NonNull List<Customization.LocaleFallback> getLocaleFallbackCustomizations() { + return mLocaleFallbackCustomizations; + } + + /** * Returns the last modified time in milliseconds. * * This is a value of {@link System#currentTimeMillis()} when the system font configuration was @@ -169,7 +187,9 @@ public final class FontConfig implements Parcelable { source.readTypedList(familyLists, NamedFamilyList.CREATOR); long lastModifiedDate = source.readLong(); int configVersion = source.readInt(); - return new FontConfig(families, aliases, familyLists, lastModifiedDate, configVersion); + return new FontConfig(families, aliases, familyLists, + Collections.emptyList(), // Don't need to pass customization to API caller. + lastModifiedDate, configVersion); } @Override @@ -813,4 +833,129 @@ public final class FontConfig implements Parcelable { + '}'; } } + + /** @hide */ + public static class Customization { + private Customization() {} // Singleton + + /** + * A class that represents customization of locale fallback + * + * This class represents a vendor customization of new-locale-family. + * + * <pre> + * <family customizationType="new-locale-family" operation="prepend" lang="ja-JP"> + * <font weight="400" style="normal">MyAlternativeFont.ttf + * <axis tag="wght" stylevalue="400"/> + * </font> + * </family> + * </pre> + * + * The operation can be one of prepend, replace or append. The operation prepend means that + * the new font family is inserted just before the original font family. The original font + * family is still in the fallback. The operation replace means that the original font + * family is replaced with new font family. The original font family is removed from the + * fallback. The operation append means that the new font family is inserted just after the + * original font family. The original font family is still in the fallback. + * + * The lang attribute is a BCP47 compliant language tag. The font fallback mainly uses ISO + * 15924 script code for matching. If the script code is missing, most likely script code + * will be used. + */ + public static class LocaleFallback { + private final Locale mLocale; + private final int mOperation; + private final FontFamily mFamily; + private final String mScript; + + public static final int OPERATION_PREPEND = 0; + public static final int OPERATION_APPEND = 1; + public static final int OPERATION_REPLACE = 2; + + /** @hide */ + @Retention(SOURCE) + @IntDef(prefix = { "OPERATION_" }, value = { + OPERATION_PREPEND, + OPERATION_APPEND, + OPERATION_REPLACE + }) + public @interface Operation {} + + + public LocaleFallback(@NonNull Locale locale, @Operation int operation, + @NonNull FontFamily family) { + mLocale = locale; + mOperation = operation; + mFamily = family; + mScript = resolveScript(locale); + } + + /** + * A customization target locale. + * @return a locale + */ + public @NonNull Locale getLocale() { + return mLocale; + } + + /** + * An operation to be applied to the original font family. + * + * The operation can be one of {@link #OPERATION_PREPEND}, {@link #OPERATION_REPLACE} or + * {@link #OPERATION_APPEND}. + * + * The operation prepend ({@link #OPERATION_PREPEND}) means that the new font family is + * inserted just before the original font family. The original font family is still in + * the fallback. + * + * The operation replace ({@link #OPERATION_REPLACE}) means that the original font + * family is replaced with new font family. The original font family is removed from the + * fallback. + * + * The operation append ({@link #OPERATION_APPEND}) means that the new font family is + * inserted just after the original font family. The original font family is still in + * the fallback. + * + * @return an operation. + */ + public @Operation int getOperation() { + return mOperation; + } + + /** + * Returns a family to be inserted or replaced to the fallback. + * + * @return a family + */ + public @NonNull FontFamily getFamily() { + return mFamily; + } + + /** + * Returns a script of the locale. If the script is missing in the given locale, the + * most likely locale is returned. + */ + public @NonNull String getScript() { + return mScript; + } + + @Override + public String toString() { + return "LocaleFallback{" + + "mLocale=" + mLocale + + ", mOperation=" + mOperation + + ", mFamily=" + mFamily + + '}'; + } + } + } + + /** @hide */ + public static String resolveScript(Locale locale) { + String script = locale.getScript(); + if (script != null && !script.isEmpty()) { + return script; + } + return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript(); + } } diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index c1d0e9b95b70..ac5eb3cbeeaa 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -16,6 +16,9 @@ package android.text; +import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN; + +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; @@ -416,10 +419,12 @@ public class MeasuredParagraph { * @hide */ @TestApi + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public interface StyleRunCallback { /** * Called when a single style run is identified. */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) void onAppendStyleRun(@NonNull Paint paint, @Nullable LineBreakConfig lineBreakConfig, @IntRange(from = 0) int length, boolean isRtl); @@ -427,6 +432,7 @@ public class MeasuredParagraph { /** * Called when a single replacement run is identified. */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) void onAppendReplacementRun(@NonNull Paint paint, @IntRange(from = 0) int length, @Px @FloatRange(from = 0) float width); } @@ -488,6 +494,7 @@ public class MeasuredParagraph { @SuppressLint("ExecutorRegistration") @TestApi @NonNull + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) public static MeasuredParagraph buildForStaticLayoutTest( @NonNull TextPaint paint, @Nullable LineBreakConfig lineBreakConfig, diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index e3c72c964d64..01279cea073f 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -16,6 +16,9 @@ package android.text; +import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; + +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; @@ -439,6 +442,7 @@ public class StaticLayout extends Layout { * @see Layout.Builder#setUseBoundsForWidth(boolean) */ @NonNull + @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH) public Builder setUseBoundsForWidth(boolean useBoundsForWidth) { mUseBoundsForWidth = useBoundsForWidth; return this; diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java index 536e3ccd98f6..9edf298b1fd2 100644 --- a/core/java/android/text/TextFlags.java +++ b/core/java/android/text/TextFlags.java @@ -62,6 +62,18 @@ public final class TextFlags { }; /** + * List of the default values of the text flags. + * + * The order must be the same to the TEXT_ACONFIG_FLAGS. + */ + public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = { + Flags.deprecateFontsXml(), + Flags.noBreakNoHyphenationSpan(), + Flags.phraseStrictFallback(), + Flags.useBoundsForWidth(), + }; + + /** * Get a key for the feature flag. */ public static String getKeyForFlag(@NonNull String flag) { diff --git a/core/java/android/text/flags/custom_locale_fallback.aconfig b/core/java/android/text/flags/custom_locale_fallback.aconfig new file mode 100644 index 000000000000..52fe8834f234 --- /dev/null +++ b/core/java/android/text/flags/custom_locale_fallback.aconfig @@ -0,0 +1,9 @@ +package: "com.android.text.flags" + +flag { + name: "custom_locale_fallback" + namespace: "text" + description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font." + is_fixed_read_only: true + bug: "278768958" +} diff --git a/core/java/android/text/flags/fix_line_height_for_locale.aconfig b/core/java/android/text/flags/fix_line_height_for_locale.aconfig new file mode 100644 index 000000000000..8696bfa61e5c --- /dev/null +++ b/core/java/android/text/flags/fix_line_height_for_locale.aconfig @@ -0,0 +1,8 @@ +package: "com.android.text.flags" + +flag { + name: "fix_line_height_for_locale" + namespace: "text" + description: "Feature flag that preserve the line height of the TextView and EditText even if the the locale is different from Latin" + bug: "303326708" +} diff --git a/core/java/android/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java index 25c1db4d9804..682ffa180c0b 100644 --- a/core/java/android/text/style/LineBreakConfigSpan.java +++ b/core/java/android/text/style/LineBreakConfigSpan.java @@ -71,6 +71,10 @@ public class LineBreakConfigSpan { .setHyphenation(LineBreakConfig.HYPHENATION_DISABLED) .build(); + private static final LineBreakConfig sNoBreakConfig = new LineBreakConfig.Builder() + .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_NO_BREAK) + .build(); + /** * A specialized {@link LineBreakConfigSpan} that used for preventing hyphenation. */ @@ -84,4 +88,24 @@ public class LineBreakConfigSpan { super(sNoHyphenationConfig); } } + + /** + * A specialized {@link LineBreakConfigSpan} that used for preventing line break. + * + * This is useful when you want to preserve some words in the same line. + * Note that even if this style is specified, the grapheme based line break is still performed + * for preventing clipping text. + * + * @see LineBreakConfigSpan + */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) + public static final class NoBreakSpan extends LineBreakConfigSpan { + /** + * Construct a new {@link NoBreakSpan}. + */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) + public NoBreakSpan() { + super(sNoBreakConfig); + } + } } diff --git a/core/java/android/util/Base64.java b/core/java/android/util/Base64.java index 92abd7c15f5f..33cc5e37da62 100644 --- a/core/java/android/util/Base64.java +++ b/core/java/android/util/Base64.java @@ -26,6 +26,7 @@ import java.io.UnsupportedEncodingException; * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>. */ +@android.ravenwood.annotations.RavenwoodWholeClassKeep public class Base64 { /** * Default values for encoder/decoder flags. diff --git a/core/java/android/view/IDecorViewGestureListener.aidl b/core/java/android/view/IDecorViewGestureListener.aidl new file mode 100644 index 000000000000..1022dbfb70eb --- /dev/null +++ b/core/java/android/view/IDecorViewGestureListener.aidl @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2019, The Android Open 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; + +/** + * Listener for changes to gesture interception detector running at DecorView. + * + * {@hide} + */ +oneway interface IDecorViewGestureListener { + /** + * Called when a DecorView has started intercepting gesture. + * + * @param windowToken Where did this gesture interception result comes from. + * @param intercepted Whether the gesture interception detector has started interception. + */ + void onInterceptionChanged(in IBinder windowToken, in boolean intercepted); +} diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index c4d307073d12..a150187bbc6c 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -23,6 +23,8 @@ import android.graphics.GraphicBuffer; import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; +import com.android.internal.os.IResultReceiver; + /** * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the * runner control certain aspects of the recents animation, and to notify window manager when the @@ -58,7 +60,7 @@ interface IRecentsAnimationController { * top resumed app, false otherwise. */ @UnsupportedAppUsage - void finish(boolean moveHomeToTop, boolean sendUserLeaveHint); + void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, in IResultReceiver finishCb); /** * Called by the handler to indicate that the recents animation input consumer should be diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index cccac95b9caa..c10fc9f9cb09 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -48,6 +48,7 @@ import android.view.IScrollCaptureResponseListener; import android.view.RemoteAnimationAdapter; import android.view.IRotationWatcher; import android.view.ISystemGestureExclusionListener; +import android.view.IDecorViewGestureListener; import android.view.IWallpaperVisibilityListener; import android.view.IWindow; import android.view.IWindowSession; @@ -1062,4 +1063,18 @@ interface IWindowManager @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.ACCESS_SURFACE_FLINGER)") boolean replaceContentOnDisplay(int displayId, in SurfaceControl sc); + + /** + * Registers a DecorView gesture listener for a given display. + */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MONITOR_INPUT)") + void registerDecorViewGestureListener(IDecorViewGestureListener listener, int displayId); + + /** + * Unregisters a DecorView gesture listener for a given display. + */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MONITOR_INPUT)") + void unregisterDecorViewGestureListener(IDecorViewGestureListener listener, int displayId); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 83de2a0fafbe..7acf2f8ce06d 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -284,6 +284,11 @@ interface IWindowSession { oneway void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects); /** + * Called when the DecorView gesture interception state has changed. + */ + oneway void reportDecorViewGestureInterceptionChanged(IWindow window, in boolean intercepted); + + /** * Called when the keep-clear areas for this window have changed. */ oneway void reportKeepClearAreasChanged(IWindow window, in List<Rect> restricted, diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 2761aaeb4a7d..45b3fdd7e5bc 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -16,6 +16,8 @@ package android.view; +import static com.android.window.flags.Flags.surfaceTrustedOverlay; + import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Matrix; @@ -35,7 +37,6 @@ import java.lang.ref.WeakReference; * @hide */ public final class InputWindowHandle { - /** * An internal annotation for all the {@link android.os.InputConfig} flags that can be * specified to {@link #inputConfig} to control the behavior of an input window. Only the @@ -59,7 +60,6 @@ public final class InputWindowHandle { InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER, InputConfig.IS_WALLPAPER, InputConfig.PAUSE_DISPATCHING, - InputConfig.TRUSTED_OVERLAY, InputConfig.WATCH_OUTSIDE_TOUCH, InputConfig.SLIPPERY, InputConfig.DISABLE_USER_ACTIVITY, @@ -272,4 +272,13 @@ public final class InputWindowHandle { } this.inputConfig &= ~inputConfig; } + + public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc, + boolean isTrusted) { + if (surfaceTrustedOverlay()) { + t.setTrustedOverlay(sc, isTrusted); + } else if (isTrusted) { + inputConfig |= InputConfig.TRUSTED_OVERLAY; + } + } } diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index a0a172d366cc..5a28d5f7fc01 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -199,12 +199,33 @@ public class ScaleGestureDetector { * @throws NullPointerException if {@code listener} is null. */ public ScaleGestureDetector(@NonNull Context context, @NonNull OnScaleGestureListener listener, - @Nullable Handler handler) { + @Nullable Handler handler) { + this(context, ViewConfiguration.get(context).getScaledTouchSlop() * 2, + ViewConfiguration.get(context).getScaledMinimumScalingSpan(), handler, listener); + } + + /** + * Creates a ScaleGestureDetector with span slop and min span. + * + * @param context the application's context. + * @param spanSlop the threshold for interpreting a touch movement as scaling. + * @param minSpan the minimum threshold of scaling span. The span could be + * overridden by other usages to specify a different scaling span, for instance, + * if you need pinch gestures to continue closer together than the default. + * @param listener the listener invoked for all the callbacks, this must not be null. + * @param handler the handler to use for running deferred listener events. + * + * @throws NullPointerException if {@code listener} is null. + * + * @hide + */ + public ScaleGestureDetector(@NonNull Context context, @NonNull int spanSlop, + @NonNull int minSpan, @Nullable Handler handler, + @NonNull OnScaleGestureListener listener) { mContext = context; mListener = listener; - final ViewConfiguration viewConfiguration = ViewConfiguration.get(context); - mSpanSlop = viewConfiguration.getScaledTouchSlop() * 2; - mMinSpan = viewConfiguration.getScaledMinimumScalingSpan(); + mSpanSlop = spanSlop; + mMinSpan = minSpan; mHandler = handler; // Quick scale is enabled by default after JB_MR2 final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index be6fb313b230..139c0bebedec 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -123,7 +123,8 @@ public final class SurfaceControl implements Parcelable { private static native long nativeMirrorSurface(long mirrorOfObject); private static native long nativeCreateTransaction(); private static native long nativeGetNativeTransactionFinalizer(); - private static native void nativeApplyTransaction(long transactionObj, boolean sync); + private static native void nativeApplyTransaction(long transactionObj, boolean sync, + boolean oneWay); private static native void nativeMergeTransaction(long transactionObj, long otherTransactionObj); private static native void nativeClearTransaction(long transactionObj); @@ -2785,10 +2786,22 @@ public final class SurfaceControl implements Parcelable { * as a new transaction. */ public void apply() { - apply(false); + apply(/*sync*/ false); } /** + * Applies the transaction as a one way binder call. This transaction will be applied out + * of order with other transactions that are applied synchronously. This method is not + * safe. It should only be used when the order does not matter. + * + * @hide + */ + public void applyAsyncUnsafe() { + apply(/*sync*/ false, /*oneWay*/ true); + } + + + /** * Clear the transaction object, without applying it. * * @hide @@ -2817,9 +2830,13 @@ public final class SurfaceControl implements Parcelable { * @hide */ public void apply(boolean sync) { + apply(sync, /*oneWay*/ false); + } + + private void apply(boolean sync, boolean oneWay) { applyResizedSurfaces(); notifyReparentedSurfaces(); - nativeApplyTransaction(mNativeObject, sync); + nativeApplyTransaction(mNativeObject, sync, oneWay); } /** @@ -4373,7 +4390,7 @@ public final class SurfaceControl implements Parcelable { void applyGlobalTransaction(boolean sync) { applyResizedSurfaces(); notifyReparentedSurfaces(); - nativeApplyTransaction(mNativeObject, sync); + nativeApplyTransaction(mNativeObject, sync, /*oneWay*/ false); } @Override diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index effc127dabd2..57b19a8ce12f 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -407,7 +407,7 @@ public class SurfaceControlViewHost { public @Nullable SurfacePackage getSurfacePackage() { if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), - mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface); + mAccessibilityEmbeddedConnection, getInputTransferToken(), mRemoteInterface); } else { return null; } @@ -526,10 +526,12 @@ public class SurfaceControlViewHost { } /** + * Returns an input token used which can be used to request focus on the embedded surface. + * * @hide */ - public IBinder getFocusGrantToken() { - return mWm.getFocusGrantToken(getWindowToken().asBinder()); + public IBinder getInputTransferToken() { + return mWm.getInputTransferToken(getWindowToken().asBinder()); } private void addWindowToken(WindowManager.LayoutParams attrs) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index afa3157e35a0..aa47f077226a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -84,6 +84,7 @@ import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_B import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW; import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; +import static android.view.accessibility.Flags.forceInvertColor; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER; @@ -154,6 +155,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.sysprop.DisplayProperties; import android.text.TextUtils; import android.util.AndroidRuntimeException; @@ -1744,8 +1746,23 @@ public final class ViewRootImpl implements ViewParent, return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; } - private void updateForceDarkMode() { - if (mAttachInfo.mThreadedRenderer == null) return; + /** Returns true if force dark should be enabled according to various settings */ + @VisibleForTesting + public boolean isForceDarkEnabled() { + if (forceInvertColor()) { + boolean isForceInvertEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* def= */ 0, + UserHandle.myUserId()) == 1; + // Force invert ignores all developer opt-outs. + // We also ignore dark theme, since the app developer can override the user's preference + // for dark mode in configuration.uiMode. Instead, we assume that the force invert + // setting will be enabled at the same time dark theme is in the Settings app. + if (isForceInvertEnabled) { + return true; + } + } boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES; @@ -1757,8 +1774,12 @@ public final class ViewRootImpl implements ViewParent, && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault); a.recycle(); } + return useAutoDark; + } - if (mAttachInfo.mThreadedRenderer.setForceDark(useAutoDark)) { + private void updateForceDarkMode() { + if (mAttachInfo.mThreadedRenderer == null) return; + if (mAttachInfo.mThreadedRenderer.setForceDark(isForceDarkEnabled())) { // TODO: Don't require regenerating all display lists to apply this setting invalidateWorld(mView); } @@ -5299,6 +5320,29 @@ public final class ViewRootImpl implements ViewParent, } /** + * Called from DecorView when gesture interception state has changed. + * + * @param intercepted If DecorView is intercepting touch events + */ + public void updateDecorViewGestureInterception(boolean intercepted) { + mHandler.sendMessage( + mHandler.obtainMessage( + MSG_DECOR_VIEW_GESTURE_INTERCEPTION, + /* arg1= */ intercepted ? 1 : 0, + /* arg2= */ 0)); + } + + void decorViewInterceptionChanged(boolean intercepted) { + if (mView != null) { + try { + mWindowSession.reportDecorViewGestureInterceptionChanged(mWindow, intercepted); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** * Set the root-level system gesture exclusion rects. These are added to those provided by * the root's view hierarchy. */ @@ -5921,6 +5965,7 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 35; private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36; private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37; + private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38; final class ViewRootHandler extends Handler { @Override @@ -6199,6 +6244,9 @@ public final class ViewRootImpl implements ViewParent, case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: { systemGestureExclusionChanged(); } break; + case MSG_DECOR_VIEW_GESTURE_INTERCEPTION: { + decorViewInterceptionChanged(/* intercepted= */ msg.arg1 == 1); + } break; case MSG_KEEP_CLEAR_RECTS_CHANGED: { keepClearRectsChanged(/* accessibilityFocusRectChanged= */ msg.arg1 == 1); } break; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e64274ec3892..08f9c8cdd2a8 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -82,6 +82,7 @@ import static android.view.WindowLayoutParamsProto.Y; import android.Manifest.permission; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -1850,6 +1851,8 @@ public interface WindowManager extends ViewManager { to = "PHONE"), @ViewDebug.IntToString(from = TYPE_SYSTEM_ALERT, to = "SYSTEM_ALERT"), + @ViewDebug.IntToString(from = TYPE_KEYGUARD, + to = "KEYGUARD"), @ViewDebug.IntToString(from = TYPE_TOAST, to = "TOAST"), @ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY, @@ -1898,6 +1901,8 @@ public interface WindowManager extends ViewManager { to = "PRIVATE_PRESENTATION"), @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "VOICE_INTERACTION"), + @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_OVERLAY, + to = "ACCESSIBILITY_OVERLAY"), @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "VOICE_INTERACTION_STARTING"), @ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, @@ -1907,7 +1912,13 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_SCREENSHOT, to = "SCREENSHOT"), @ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY, - to = "APPLICATION_OVERLAY") + to = "APPLICATION_OVERLAY"), + @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, + to = "ACCESSIBILITY_MAGNIFICATION_OVERLAY"), + @ViewDebug.IntToString(from = TYPE_NOTIFICATION_SHADE, + to = "NOTIFICATION_SHADE"), + @ViewDebug.IntToString(from = TYPE_STATUS_BAR_ADDITIONAL, + to = "STATUS_BAR_ADDITIONAL") }) @WindowType public int type; @@ -5811,6 +5822,7 @@ public interface WindowManager extends ViewManager { * * @hide */ + @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @TestApi @RequiresPermission(permission.ACCESS_SURFACE_FLINGER) default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) { @@ -5826,6 +5838,7 @@ public interface WindowManager extends ViewManager { * * @hide */ + @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @TestApi @RequiresPermission(permission.ACCESS_SURFACE_FLINGER) default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) { diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 7d3d283a45f2..7c3b6ae42fcf 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -58,7 +58,7 @@ public class WindowlessWindowManager implements IWindowSession { SurfaceControl mLeash; Rect mFrame; Rect mAttachedFrame; - IBinder mFocusGrantToken; + IBinder mInputTransferToken; State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client, SurfaceControl leash, Rect frame) { @@ -89,7 +89,7 @@ public class WindowlessWindowManager implements IWindowSession { private final Configuration mConfiguration; private final IWindowSession mRealWm; private final IBinder mHostInputToken; - private final IBinder mFocusGrantToken = new Binder(); + private final IBinder mInputTransferToken = new Binder(); private InsetsState mInsetsState; private final ClientWindowFrames mTmpFrames = new ClientWindowFrames(); private final MergedConfiguration mTmpConfig = new MergedConfiguration(); @@ -109,17 +109,17 @@ public class WindowlessWindowManager implements IWindowSession { mConfiguration.setTo(configuration); } - IBinder getFocusGrantToken(IBinder window) { + IBinder getInputTransferToken(IBinder window) { synchronized (this) { // This can only happen if someone requested the focusGrantToken before setView was // called for the SCVH. In that case, use the root focusGrantToken since this will be // the same token sent to WMS for the root window once setView is called. if (mStateForWindow.isEmpty()) { - return mFocusGrantToken; + return mInputTransferToken; } State state = mStateForWindow.get(window); if (state != null) { - return state.mFocusGrantToken; + return state.mInputTransferToken; } } @@ -207,9 +207,9 @@ public class WindowlessWindowManager implements IWindowSession { // Give the first window the mFocusGrantToken since that's the token the host can use // to give focus to the embedded. if (mStateForWindow.isEmpty()) { - state.mFocusGrantToken = mFocusGrantToken; + state.mInputTransferToken = mInputTransferToken; } else { - state.mFocusGrantToken = new Binder(); + state.mInputTransferToken = new Binder(); } mStateForWindow.put(window.asBinder(), state); @@ -230,12 +230,13 @@ public class WindowlessWindowManager implements IWindowSession { new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"), window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, - attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(), + attrs.token, state.mInputTransferToken, attrs.getTitle().toString(), outInputChannel); } else { mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token, - state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); + state.mInputTransferToken, attrs.getTitle().toString(), + outInputChannel); } state.mInputChannelToken = outInputChannel != null ? outInputChannel.getToken() : null; @@ -583,9 +584,13 @@ public class WindowlessWindowManager implements IWindowSession { } @Override - public void reportKeepClearAreasChanged(android.view.IWindow window, List<Rect> restrictedRects, - List<Rect> unrestrictedRects) { - } + public void reportDecorViewGestureInterceptionChanged(IWindow window, boolean intercepted) {} + + @Override + public void reportKeepClearAreasChanged( + android.view.IWindow window, + List<Rect> restrictedRects, + List<Rect> unrestrictedRects) {} @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig new file mode 100644 index 000000000000..e31ad82d3f55 --- /dev/null +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -0,0 +1,8 @@ +package: "android.view.accessibility" + +flag { + namespace: "accessibility" + name: "force_invert_color" + description: "Enable force force-dark for smart inversion and dark theme everywhere" + bug: "282821643" +}
\ No newline at end of file diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 89fa83e5c7aa..34e4c37de1b5 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -67,6 +67,7 @@ import android.os.SystemClock; import android.service.autofill.AutofillService; import android.service.autofill.FillCallback; import android.service.autofill.FillEventHistory; +import android.service.autofill.Flags; import android.service.autofill.IFillCallback; import android.service.autofill.UserData; import android.text.TextUtils; @@ -277,6 +278,12 @@ public final class AutofillManager { "android.view.autofill.extra.CLIENT_STATE"; /** + * @hide + */ + public static final String EXTRA_AUTH_STATE = + "android.view.autofill.extra.AUTH_STATE"; + + /** * Intent extra: the {@link android.view.inputmethod.InlineSuggestionsRequest} in the * autofill request. * @@ -428,6 +435,14 @@ public final class AutofillManager { public static final int STATE_UNKNOWN_FAILED = 6; /** + * Same as {@link #STATE_ACTIVE}, but when pending authentication after + * {@link AutofillManagerClient#authenticate(int, int, IntentSender, Intent, boolean)} + * + * @hide + */ + public static final int STATE_PENDING_AUTHENTICATION = 7; + + /** * Timeout in ms for calls to the field classification service. * @hide */ @@ -731,6 +746,10 @@ public final class AutofillManager { // Indicate whether WebView should always be included in the assist structure private boolean mShouldAlwaysIncludeWebviewInAssistStructure; + // Controls logic around apps changing some properties of their views when activity loses + // focus due to autofill showing biometric activity, password manager, or password breach check. + private boolean mRelayoutFix; + // Indicates whether called the showAutofillDialog() method. private boolean mShowAutofillDialogCalled = false; @@ -952,6 +971,8 @@ public final class AutofillManager { mShouldAlwaysIncludeWebviewInAssistStructure = AutofillFeatureFlags.shouldAlwaysIncludeWebviewInAssistStructure(); + + mRelayoutFix = Flags.relayout(); } /** @@ -1715,7 +1736,13 @@ public final class AutofillManager { synchronized (mLock) { if (mForAugmentedAutofillOnly) { if (sVerbose) { - Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode"); + Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode"); + } + return; + } + if (mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION) { + if (sVerbose) { + Log.v(TAG, "notifyViewVisibilityChanged(): ignoring in auth pending mode"); } return; } @@ -2342,6 +2369,7 @@ public final class AutofillManager { if (!isActiveLocked()) { return; } + mState = STATE_ACTIVE; // If authenticate activity closes itself during onCreate(), there is no onStop/onStart // of app activity. We enforce enter event to re-show fill ui in such case. // CTS example: @@ -2830,6 +2858,9 @@ public final class AutofillManager { Intent fillInIntent, boolean authenticateInline) { synchronized (mLock) { if (sessionId == mSessionId) { + if (mRelayoutFix) { + mState = STATE_PENDING_AUTHENTICATION; + } final AutofillClient client = getClient(); if (client != null) { // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill() @@ -3563,6 +3594,8 @@ public final class AutofillManager { return "UNKNOWN"; case STATE_ACTIVE: return "ACTIVE"; + case STATE_PENDING_AUTHENTICATION: + return "PENDING_AUTHENTICATION"; case STATE_FINISHED: return "FINISHED"; case STATE_SHOWING_SAVE_UI: @@ -3592,7 +3625,12 @@ public final class AutofillManager { @GuardedBy("mLock") private boolean isActiveLocked() { - return mState == STATE_ACTIVE; + return mState == STATE_ACTIVE || isPendingAuthenticationLocked(); + } + + @GuardedBy("mLock") + private boolean isPendingAuthenticationLocked() { + return mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION; } @GuardedBy("mLock") diff --git a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig index 13a6f8d52dc6..56b5fac1c64a 100644 --- a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig +++ b/core/java/android/view/flags/refresh_rate_flags.aconfig @@ -19,4 +19,11 @@ flag { namespace: "toolkit" description: "Feature flag for using expected presentation time of the Choreographer" bug: "278730197" +} + +flag { + name: "set_frame_rate_callback" + namespace: "core_graphics" + description: "Enable the `setFrameRate` callback" + bug: "299946220" }
\ No newline at end of file diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 26ceea6d1e4c..1fdd1a5a5a5f 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -80,6 +80,7 @@ import android.view.animation.LinearInterpolator; import android.view.autofill.AutofillId; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; +import android.view.flags.Flags; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -92,7 +93,6 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.SurroundingText; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty.EnumEntry; -import android.widget.flags.Flags; import android.widget.RemoteViews.InteractionHandler; import com.android.internal.R; @@ -4518,7 +4518,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final int overscrollMode = getOverScrollMode(); if (!trackMotionScroll(delta, delta)) { - if (Flags.platformWidgetHapticScrollFeedback()) { + if (Flags.scrollFeedbackApi()) { initHapticScrollFeedbackProviderIfNotExists(); mHapticScrollFeedbackProvider.onScrollProgress( event.getDeviceId(), event.getSource(), axis, delta); @@ -4534,7 +4534,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te float overscroll = (delta - (motionViewRealTop - motionViewPrevTop)) / ((float) getHeight()); boolean hitTopLimit = delta > 0; - if (Flags.platformWidgetHapticScrollFeedback()) { + if (Flags.scrollFeedbackApi()) { initHapticScrollFeedbackProviderIfNotExists(); mHapticScrollFeedbackProvider.onScrollLimit( event.getDeviceId(), event.getSource(), axis, diff --git a/core/java/android/widget/DifferentialMotionFlingHelper.java b/core/java/android/widget/DifferentialMotionFlingHelper.java index 95d24ec31209..ef01c3b79059 100644 --- a/core/java/android/widget/DifferentialMotionFlingHelper.java +++ b/core/java/android/widget/DifferentialMotionFlingHelper.java @@ -21,6 +21,8 @@ import android.content.Context; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; +import android.widget.flags.FeatureFlags; +import android.widget.flags.FeatureFlagsImpl; import com.android.internal.annotations.VisibleForTesting; @@ -50,6 +52,8 @@ public class DifferentialMotionFlingHelper { private final FlingVelocityThresholdCalculator mVelocityThresholdCalculator; private final DifferentialVelocityProvider mVelocityProvider; + private final FeatureFlags mWidgetFeatureFlags; + @Nullable private VelocityTracker mVelocityTracker; private float mLastFlingVelocity; @@ -134,7 +138,8 @@ public class DifferentialMotionFlingHelper { this(context, target, DifferentialMotionFlingHelper::calculateFlingVelocityThresholds, - DifferentialMotionFlingHelper::getCurrentVelocity); + DifferentialMotionFlingHelper::getCurrentVelocity, + /* widgetFeatureFlags= */ new FeatureFlagsImpl()); } @VisibleForTesting @@ -142,11 +147,13 @@ public class DifferentialMotionFlingHelper { Context context, DifferentialMotionFlingTarget target, FlingVelocityThresholdCalculator velocityThresholdCalculator, - DifferentialVelocityProvider velocityProvider) { + DifferentialVelocityProvider velocityProvider, + FeatureFlags widgetFeatureFlags) { mContext = context; mTarget = target; mVelocityThresholdCalculator = velocityThresholdCalculator; mVelocityProvider = velocityProvider; + mWidgetFeatureFlags = widgetFeatureFlags; } /** @@ -156,6 +163,9 @@ public class DifferentialMotionFlingHelper { * @param axis the axis being processed by the target View. */ public void onMotionEvent(MotionEvent event, int axis) { + if (!mWidgetFeatureFlags.enablePlatformWidgetDifferentialMotionFling()) { + return; + } boolean flingParamsChanged = calculateFlingVelocityThresholds(event, axis); if (mFlingVelocityThresholds[0] == Integer.MAX_VALUE) { // Integer.MAX_VALUE means that the device does not support fling for the current diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 79acfbb0b39c..103725da9af0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -638,9 +638,13 @@ public class RemoteViews implements Parcelable, Filter { * Base class for all actions that can be performed on an * inflated view. * - * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! + * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! */ private abstract static class Action implements Parcelable { + @IdRes + @UnsupportedAppUsage + int mViewId; + public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException; @@ -664,7 +668,7 @@ public class RemoteViews implements Parcelable, Filter { public abstract int getActionTag(); public String getUniqueKey() { - return (getActionTag() + "_" + viewId); + return (getActionTag() + "_" + mViewId); } /** @@ -684,16 +688,12 @@ public class RemoteViews implements Parcelable, Filter { public void visitUris(@NonNull Consumer<Uri> visitor) { // Nothing to visit by default } - - @IdRes - @UnsupportedAppUsage - int viewId; } /** * Action class used during async inflation of RemoteViews. Subclasses are not parcelable. */ - private static abstract class RuntimeAction extends Action { + private abstract static class RuntimeAction extends Action { @Override public final int getActionTag() { return 0; @@ -796,15 +796,16 @@ public class RemoteViews implements Parcelable, Filter { for (int i = 0; i < mActions.size(); i++) { Action action = mActions.get(i); if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction - && itemsAction.viewId == viewId + && itemsAction.mViewId == viewId && itemsAction.mServiceIntent != null) { - mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId, - itemsAction.mServiceIntent)); + mActions.set(i, + new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId, + itemsAction.mServiceIntent)); isActionReplaced = true; } else if (action instanceof SetRemoteViewsAdapterIntent intentAction - && intentAction.viewId == viewId) { + && intentAction.mViewId == viewId) { mActions.set(i, new SetRemoteCollectionItemListAdapterAction( - intentAction.viewId, intentAction.intent)); + intentAction.mViewId, intentAction.mIntent)); isActionReplaced = true; } else if (action instanceof ViewGroupActionAdd groupAction && groupAction.mNestedViews != null) { @@ -838,7 +839,7 @@ public class RemoteViews implements Parcelable, Filter { if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction && itemsAction.mServiceIntent != null) || (action instanceof SetRemoteViewsAdapterIntent intentAction - && intentAction.intent != null) + && intentAction.mIntent != null) || (action instanceof ViewGroupActionAdd groupAction && groupAction.mNestedViews != null && groupAction.mNestedViews.hasLegacyLists())) { @@ -856,11 +857,7 @@ public class RemoteViews implements Parcelable, Filter { if (mLandscape != null && mLandscape.hasLegacyLists()) { return true; } - if (mPortrait != null && mPortrait.hasLegacyLists()) { - return true; - } - - return false; + return mPortrait != null && mPortrait.hasLegacyLists(); } private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) { @@ -913,31 +910,31 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetEmptyView extends Action { - int emptyViewId; + int mEmptyViewId; SetEmptyView(@IdRes int viewId, @IdRes int emptyViewId) { - this.viewId = viewId; - this.emptyViewId = emptyViewId; + this.mViewId = viewId; + this.mEmptyViewId = emptyViewId; } SetEmptyView(Parcel in) { - this.viewId = in.readInt(); - this.emptyViewId = in.readInt(); + this.mViewId = in.readInt(); + this.mEmptyViewId = in.readInt(); } public void writeToParcel(Parcel out, int flags) { - out.writeInt(this.viewId); - out.writeInt(this.emptyViewId); + out.writeInt(this.mViewId); + out.writeInt(this.mEmptyViewId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (!(view instanceof AdapterView<?>)) return; AdapterView<?> adapterView = (AdapterView<?>) view; - final View emptyView = root.findViewById(emptyViewId); + final View emptyView = root.findViewById(mEmptyViewId); if (emptyView == null) return; adapterView.setEmptyView(emptyView); @@ -950,24 +947,27 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetPendingIntentTemplate extends Action { + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + PendingIntent mPendingIntentTemplate; + public SetPendingIntentTemplate(@IdRes int id, PendingIntent pendingIntentTemplate) { - this.viewId = id; - this.pendingIntentTemplate = pendingIntentTemplate; + this.mViewId = id; + this.mPendingIntentTemplate = pendingIntentTemplate; } public SetPendingIntentTemplate(Parcel parcel) { - viewId = parcel.readInt(); - pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel); + mViewId = parcel.readInt(); + mPendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest); + dest.writeInt(mViewId); + PendingIntent.writePendingIntentOrNullToParcel(mPendingIntentTemplate, dest); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense @@ -981,10 +981,10 @@ public class RemoteViews implements Parcelable, Filter { } }; av.setOnItemClickListener(listener); - av.setTag(pendingIntentTemplate); + av.setTag(mPendingIntentTemplate); } else { Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" + - "an AdapterView (id: " + viewId + ")"); + "an AdapterView (id: " + mViewId + ")"); return; } } @@ -1015,65 +1015,65 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_PENDING_INTENT_TEMPLATE_TAG; } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - PendingIntent pendingIntentTemplate; } private static class SetRemoteViewsAdapterList extends Action { + int mViewTypeCount; + ArrayList<RemoteViews> mList; + public SetRemoteViewsAdapterList(@IdRes int id, ArrayList<RemoteViews> list, int viewTypeCount) { - this.viewId = id; - this.list = list; - this.viewTypeCount = viewTypeCount; + this.mViewId = id; + this.mList = list; + this.mViewTypeCount = viewTypeCount; } public SetRemoteViewsAdapterList(Parcel parcel) { - viewId = parcel.readInt(); - viewTypeCount = parcel.readInt(); - list = parcel.createTypedArrayList(RemoteViews.CREATOR); + mViewId = parcel.readInt(); + mViewTypeCount = parcel.readInt(); + mList = parcel.createTypedArrayList(RemoteViews.CREATOR); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(viewTypeCount); - dest.writeTypedList(list, flags); + dest.writeInt(mViewId); + dest.writeInt(mViewTypeCount); + dest.writeTypedList(mList, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " + - "AppWidgets (root id: " + viewId + ")"); + "AppWidgets (root id: " + mViewId + ")"); return; } // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " + - "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); + "an AbsListView or AdapterViewAnimator (id: " + mViewId + ")"); return; } if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; Adapter a = v.getAdapter(); - if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { - ((RemoteViewsListAdapter) a).setViewsList(list); + if (a instanceof RemoteViewsListAdapter && mViewTypeCount <= a.getViewTypeCount()) { + ((RemoteViewsListAdapter) a).setViewsList(mList); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), mList, mViewTypeCount, params.colorResources)); } } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; Adapter a = v.getAdapter(); - if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { - ((RemoteViewsListAdapter) a).setViewsList(list); + if (a instanceof RemoteViewsListAdapter && mViewTypeCount <= a.getViewTypeCount()) { + ((RemoteViewsListAdapter) a).setViewsList(mList); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), mList, mViewTypeCount, params.colorResources)); } } @@ -1086,11 +1086,8 @@ public class RemoteViews implements Parcelable, Filter { @Override public String getUniqueKey() { - return (SET_REMOTE_ADAPTER_TAG + "_" + viewId); + return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId); } - - int viewTypeCount; - ArrayList<RemoteViews> list; } /** @@ -1135,19 +1132,20 @@ public class RemoteViews implements Parcelable, Filter { } private class SetRemoteCollectionItemListAdapterAction extends Action { - private @NonNull CompletableFuture<RemoteCollectionItems> mItemsFuture; + @NonNull + private CompletableFuture<RemoteCollectionItems> mItemsFuture; final Intent mServiceIntent; SetRemoteCollectionItemListAdapterAction(@IdRes int id, @NonNull RemoteCollectionItems items) { - viewId = id; + mViewId = id; items.setHierarchyRootData(getHierarchyRootData()); mItemsFuture = CompletableFuture.completedFuture(items); mServiceIntent = null; } SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) { - viewId = id; + mViewId = id; mItemsFuture = getItemsFutureFromIntentWithTimeout(intent); setHierarchyRootData(getHierarchyRootData()); mServiceIntent = intent; @@ -1196,7 +1194,7 @@ public class RemoteViews implements Parcelable, Filter { } SetRemoteCollectionItemListAdapterAction(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mItemsFuture = CompletableFuture.completedFuture( new RemoteCollectionItems(parcel, getHierarchyRootData())); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); @@ -1226,7 +1224,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); items.writeToParcel(dest, flags, /* attached= */ true); dest.writeTypedObject(mServiceIntent, flags); @@ -1235,7 +1233,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - View target = root.findViewById(viewId); + View target = root.findViewById(mViewId); if (target == null) return; RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); @@ -1243,13 +1241,13 @@ public class RemoteViews implements Parcelable, Filter { // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "setRemoteAdapter can only be used for " - + "AppWidgets (root id: " + viewId + ")"); + + "AppWidgets (root id: " + mViewId + ")"); return; } if (!(target instanceof AdapterView)) { Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not " - + "an AdapterView (id: " + viewId + ")"); + + "an AdapterView (id: " + mViewId + ")"); return; } @@ -1289,59 +1287,62 @@ public class RemoteViews implements Parcelable, Filter { @Override public String getUniqueKey() { - return (SET_REMOTE_ADAPTER_TAG + "_" + viewId); + return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId); } } private class SetRemoteViewsAdapterIntent extends Action { + Intent mIntent; + boolean mIsAsync = false; + public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) { - this.viewId = id; - this.intent = intent; + this.mViewId = id; + this.mIntent = intent; } public SetRemoteViewsAdapterIntent(Parcel parcel) { - viewId = parcel.readInt(); - intent = parcel.readTypedObject(Intent.CREATOR); + mViewId = parcel.readInt(); + mIntent = parcel.readTypedObject(Intent.CREATOR); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeTypedObject(intent, flags); + dest.writeInt(mViewId); + dest.writeTypedObject(mIntent, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "setRemoteAdapter can only be used for " - + "AppWidgets (root id: " + viewId + ")"); + + "AppWidgets (root id: " + mViewId + ")"); return; } // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { Log.e(LOG_TAG, "Cannot setRemoteAdapter on a view which is not " - + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); + + "an AbsListView or AdapterViewAnimator (id: " + mViewId + ")"); return; } // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent // RemoteViewsService AppWidgetHostView host = (AppWidgetHostView) rootParent; - intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()) + mIntent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()) .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)); if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; - v.setRemoteViewsAdapter(intent, isAsync); + v.setRemoteViewsAdapter(mIntent, mIsAsync); v.setRemoteViewsInteractionHandler(params.handler); } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; - v.setRemoteViewsAdapter(intent, isAsync); + v.setRemoteViewsAdapter(mIntent, mIsAsync); v.setRemoteViewsOnClickHandler(params.handler); } } @@ -1349,8 +1350,8 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, ActionApplyParams params) { - SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent); - copy.isAsync = true; + SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(mViewId, mIntent); + copy.mIsAsync = true; return copy; } @@ -1358,9 +1359,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG; } - - Intent intent; - boolean isAsync = false; } /** @@ -1369,26 +1367,27 @@ public class RemoteViews implements Parcelable, Filter { * to launch the provided {@link PendingIntent}. */ private class SetOnClickResponse extends Action { + final RemoteResponse mResponse; SetOnClickResponse(@IdRes int id, RemoteResponse response) { - this.viewId = id; + this.mViewId = id; this.mResponse = response; } SetOnClickResponse(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mResponse = new RemoteResponse(); mResponse.readFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); mResponse.writeToParcel(dest, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (mResponse.mPendingIntent != null) { @@ -1397,7 +1396,7 @@ public class RemoteViews implements Parcelable, Filter { // AdapterView's children? if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item " - + "(id: " + viewId + ")"); + + "(id: " + mViewId + ")"); ApplicationInfo appInfo = root.getContext().getApplicationInfo(); // We let this slide for HC and ICS so as to not break compatibility. It should @@ -1435,8 +1434,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_CLICK_RESPONSE_TAG; } - - final RemoteResponse mResponse; } /** @@ -1446,32 +1443,31 @@ public class RemoteViews implements Parcelable, Filter { * to launch the provided {@link PendingIntent}. */ private class SetOnCheckedChangeResponse extends Action { - private final RemoteResponse mResponse; SetOnCheckedChangeResponse(@IdRes int id, RemoteResponse response) { - this.viewId = id; + this.mViewId = id; this.mResponse = response; } SetOnCheckedChangeResponse(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mResponse = new RemoteResponse(); mResponse.readFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); mResponse.writeToParcel(dest, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (!(target instanceof CompoundButton)) { Log.w(LOG_TAG, "setOnCheckedChange methods cannot be used on " - + "non-CompoundButton child (id: " + viewId + ")"); + + "non-CompoundButton child (id: " + mViewId + ")"); return; } CompoundButton button = (CompoundButton) target; @@ -1481,7 +1477,7 @@ public class RemoteViews implements Parcelable, Filter { // must use setOnCheckedChangeFillInIntent instead. if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.w(LOG_TAG, "Cannot setOnCheckedChangePendingIntent for collection item " - + "(id: " + viewId + ")"); + + "(id: " + mViewId + ")"); return; } target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent); @@ -1648,36 +1644,40 @@ public class RemoteViews implements Parcelable, Filter { * <p> */ private static class SetDrawableTint extends Action { + boolean mTargetBackground; + @ColorInt int mColorFilter; + PorterDuff.Mode mFilterMode; + SetDrawableTint(@IdRes int id, boolean targetBackground, @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) { - this.viewId = id; - this.targetBackground = targetBackground; - this.colorFilter = colorFilter; - this.filterMode = mode; + this.mViewId = id; + this.mTargetBackground = targetBackground; + this.mColorFilter = colorFilter; + this.mFilterMode = mode; } SetDrawableTint(Parcel parcel) { - viewId = parcel.readInt(); - targetBackground = parcel.readInt() != 0; - colorFilter = parcel.readInt(); - filterMode = PorterDuff.intToMode(parcel.readInt()); + mViewId = parcel.readInt(); + mTargetBackground = parcel.readInt() != 0; + mColorFilter = parcel.readInt(); + mFilterMode = PorterDuff.intToMode(parcel.readInt()); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(targetBackground ? 1 : 0); - dest.writeInt(colorFilter); - dest.writeInt(PorterDuff.modeToInt(filterMode)); + dest.writeInt(mViewId); + dest.writeInt(mTargetBackground ? 1 : 0); + dest.writeInt(mColorFilter); + dest.writeInt(PorterDuff.modeToInt(mFilterMode)); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Pick the correct drawable to modify for this view Drawable targetDrawable = null; - if (targetBackground) { + if (mTargetBackground) { targetDrawable = target.getBackground(); } else if (target instanceof ImageView) { ImageView imageView = (ImageView) target; @@ -1685,7 +1685,7 @@ public class RemoteViews implements Parcelable, Filter { } if (targetDrawable != null) { - targetDrawable.mutate().setColorFilter(colorFilter, filterMode); + targetDrawable.mutate().setColorFilter(mColorFilter, mFilterMode); } } @@ -1693,10 +1693,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_DRAWABLE_TINT_TAG; } - - boolean targetBackground; - @ColorInt int colorFilter; - PorterDuff.Mode filterMode; } /** @@ -1709,27 +1705,26 @@ public class RemoteViews implements Parcelable, Filter { * <p> */ private class SetRippleDrawableColor extends Action { - ColorStateList mColorStateList; SetRippleDrawableColor(@IdRes int id, ColorStateList colorStateList) { - this.viewId = id; + this.mViewId = id; this.mColorStateList = colorStateList; } SetRippleDrawableColor(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mColorStateList = parcel.readParcelable(null, android.content.res.ColorStateList.class); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeParcelable(mColorStateList, 0); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Pick the correct drawable to modify for this view @@ -1756,23 +1751,23 @@ public class RemoteViews implements Parcelable, Filter { final boolean mNext; ViewContentNavigation(@IdRes int viewId, boolean next) { - this.viewId = viewId; + this.mViewId = viewId; this.mNext = next; } ViewContentNavigation(Parcel in) { - this.viewId = in.readInt(); + this.mViewId = in.readInt(); this.mNext = in.readBoolean(); } public void writeToParcel(Parcel out, int flags) { - out.writeInt(this.viewId); + out.writeInt(this.mViewId); out.writeBoolean(this.mNext); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (view == null) return; try { @@ -1794,7 +1789,6 @@ public class RemoteViews implements Parcelable, Filter { } private static class BitmapCache { - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) ArrayList<Bitmap> mBitmaps; SparseIntArray mBitmapHashes; @@ -1861,45 +1855,45 @@ public class RemoteViews implements Parcelable, Filter { } private class BitmapReflectionAction extends Action { - int bitmapId; + int mBitmapId; @UnsupportedAppUsage - Bitmap bitmap; + Bitmap mBitmap; @UnsupportedAppUsage - String methodName; + String mMethodName; BitmapReflectionAction(@IdRes int viewId, String methodName, Bitmap bitmap) { - this.bitmap = bitmap; - this.viewId = viewId; - this.methodName = methodName; - bitmapId = mBitmapCache.getBitmapId(bitmap); + this.mBitmap = bitmap; + this.mViewId = viewId; + this.mMethodName = methodName; + mBitmapId = mBitmapCache.getBitmapId(bitmap); } BitmapReflectionAction(Parcel in) { - viewId = in.readInt(); - methodName = in.readString8(); - bitmapId = in.readInt(); - bitmap = mBitmapCache.getBitmapForId(bitmapId); + mViewId = in.readInt(); + mMethodName = in.readString8(); + mBitmapId = in.readInt(); + mBitmap = mBitmapCache.getBitmapForId(mBitmapId); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeString8(methodName); - dest.writeInt(bitmapId); + dest.writeInt(mViewId); + dest.writeString8(mMethodName); + dest.writeInt(mBitmapId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - ReflectionAction ra = new ReflectionAction(viewId, methodName, + ReflectionAction ra = new ReflectionAction(mViewId, mMethodName, BaseReflectionAction.BITMAP, - bitmap); + mBitmap); ra.apply(root, rootParent, params); } @Override public void setHierarchyRootData(HierarchyRootData rootData) { - bitmapId = rootData.mBitmapCache.getBitmapId(bitmap); + mBitmapId = rootData.mBitmapCache.getBitmapId(mBitmap); } @Override @@ -1933,30 +1927,30 @@ public class RemoteViews implements Parcelable, Filter { static final int BLEND_MODE = 17; @UnsupportedAppUsage - String methodName; - int type; + String mMethodName; + int mType; BaseReflectionAction(@IdRes int viewId, String methodName, int type) { - this.viewId = viewId; - this.methodName = methodName; - this.type = type; + this.mViewId = viewId; + this.mMethodName = methodName; + this.mType = type; } BaseReflectionAction(Parcel in) { - this.viewId = in.readInt(); - this.methodName = in.readString8(); - this.type = in.readInt(); + this.mViewId = in.readInt(); + this.mMethodName = in.readString8(); + this.mType = in.readInt(); //noinspection ConstantIfStatement if (false) { - Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId) - + " methodName=" + this.methodName + " type=" + this.type); + Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.mViewId) + + " methodName=" + this.mMethodName + " type=" + this.mType); } } public void writeToParcel(Parcel out, int flags) { - out.writeInt(this.viewId); - out.writeString8(this.methodName); - out.writeInt(this.type); + out.writeInt(this.mViewId); + out.writeString8(this.mMethodName); + out.writeInt(this.mType); } /** @@ -1971,16 +1965,16 @@ public class RemoteViews implements Parcelable, Filter { @Override public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (view == null) return; - Class<?> param = getParameterType(this.type); + Class<?> param = getParameterType(this.mType); if (param == null) { - throw new ActionException("bad type: " + this.type); + throw new ActionException("bad type: " + this.mType); } Object value = getParameterValue(view); try { - getMethod(view, this.methodName, param, false /* async */).invoke(view, value); + getMethod(view, this.mMethodName, param, false /* async */).invoke(view, value); } catch (Throwable ex) { throw new ActionException(ex); } @@ -1989,17 +1983,17 @@ public class RemoteViews implements Parcelable, Filter { @Override public final Action initActionAsync(ViewTree root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (view == null) return ACTION_NOOP; - Class<?> param = getParameterType(this.type); + Class<?> param = getParameterType(this.mType); if (param == null) { - throw new ActionException("bad type: " + this.type); + throw new ActionException("bad type: " + this.mType); } Object value = getParameterValue(view); try { - MethodHandle method = getMethod(view, this.methodName, param, true /* async */); + MethodHandle method = getMethod(view, this.mMethodName, param, true /* async */); // Upload the bitmap to GPU if the parameter is of type Bitmap or Icon. // Since bitmaps in framework are seldomly modified, this is supposed to accelerate // the operations. @@ -2025,7 +2019,7 @@ public class RemoteViews implements Parcelable, Filter { if (endAction instanceof ViewStub.ViewReplaceRunnable) { root.createTree(); // Replace child tree - root.findViewTreeById(viewId).replaceView( + root.findViewTreeById(mViewId).replaceView( ((ViewStub.ViewReplaceRunnable) endAction).view); } return new RunnableAction(endAction); @@ -2039,7 +2033,7 @@ public class RemoteViews implements Parcelable, Filter { public final int mergeBehavior() { // smoothScrollBy is cumulative, everything else overwites. - if (methodName.equals("smoothScrollBy")) { + if (mMethodName.equals("smoothScrollBy")) { return MERGE_APPEND; } else { return MERGE_REPLACE; @@ -2050,17 +2044,17 @@ public class RemoteViews implements Parcelable, Filter { public final String getUniqueKey() { // Each type of reflection action corresponds to a setter, so each should be seen as // unique from the standpoint of merging. - return super.getUniqueKey() + this.methodName + this.type; + return super.getUniqueKey() + this.mMethodName + this.mType; } @Override public final boolean prefersAsyncApply() { - return this.type == URI || this.type == ICON; + return this.mType == URI || this.mType == ICON; } @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - switch (this.type) { + switch (this.mType) { case URI: final Uri uri = (Uri) getParameterValue(null); if (uri != null) visitor.accept(uri); @@ -2076,53 +2070,53 @@ public class RemoteViews implements Parcelable, Filter { /** Class for the reflection actions. */ private static final class ReflectionAction extends BaseReflectionAction { @UnsupportedAppUsage - Object value; + Object mValue; ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) { super(viewId, methodName, type); - this.value = value; + this.mValue = value; } ReflectionAction(Parcel in) { super(in); // For some values that may have been null, we first check a flag to see if they were // written to the parcel. - switch (this.type) { + switch (this.mType) { case BOOLEAN: - this.value = in.readBoolean(); + this.mValue = in.readBoolean(); break; case BYTE: - this.value = in.readByte(); + this.mValue = in.readByte(); break; case SHORT: - this.value = (short) in.readInt(); + this.mValue = (short) in.readInt(); break; case INT: - this.value = in.readInt(); + this.mValue = in.readInt(); break; case LONG: - this.value = in.readLong(); + this.mValue = in.readLong(); break; case FLOAT: - this.value = in.readFloat(); + this.mValue = in.readFloat(); break; case DOUBLE: - this.value = in.readDouble(); + this.mValue = in.readDouble(); break; case CHAR: - this.value = (char) in.readInt(); + this.mValue = (char) in.readInt(); break; case STRING: - this.value = in.readString8(); + this.mValue = in.readString8(); break; case CHAR_SEQUENCE: - this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + this.mValue = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); break; case URI: - this.value = in.readTypedObject(Uri.CREATOR); + this.mValue = in.readTypedObject(Uri.CREATOR); break; case BITMAP: - this.value = in.readTypedObject(Bitmap.CREATOR); + this.mValue = in.readTypedObject(Bitmap.CREATOR); break; case BUNDLE: // Because we use Parcel.allowSquashing() when writing, and that affects @@ -2131,24 +2125,24 @@ public class RemoteViews implements Parcelable, Filter { // just happens to have that effect on Bundle.readFromParcel(). // TODO(b/212731590): build this state tracking into Bundle if (in.hasReadWriteHelper()) { - this.value = in.readBundle(); + this.mValue = in.readBundle(); } else { in.setReadWriteHelper(ALTERNATIVE_DEFAULT); - this.value = in.readBundle(); + this.mValue = in.readBundle(); in.setReadWriteHelper(null); } break; case INTENT: - this.value = in.readTypedObject(Intent.CREATOR); + this.mValue = in.readTypedObject(Intent.CREATOR); break; case COLOR_STATE_LIST: - this.value = in.readTypedObject(ColorStateList.CREATOR); + this.mValue = in.readTypedObject(ColorStateList.CREATOR); break; case ICON: - this.value = in.readTypedObject(Icon.CREATOR); + this.mValue = in.readTypedObject(Icon.CREATOR); break; case BLEND_MODE: - this.value = BlendMode.fromValue(in.readInt()); + this.mValue = BlendMode.fromValue(in.readInt()); break; default: break; @@ -2159,49 +2153,49 @@ public class RemoteViews implements Parcelable, Filter { super.writeToParcel(out, flags); // For some values which are null, we record an integer flag to indicate whether // we have written a valid value to the parcel. - switch (this.type) { + switch (this.mType) { case BOOLEAN: - out.writeBoolean((Boolean) this.value); + out.writeBoolean((Boolean) this.mValue); break; case BYTE: - out.writeByte((Byte) this.value); + out.writeByte((Byte) this.mValue); break; case SHORT: - out.writeInt((Short) this.value); + out.writeInt((Short) this.mValue); break; case INT: - out.writeInt((Integer) this.value); + out.writeInt((Integer) this.mValue); break; case LONG: - out.writeLong((Long) this.value); + out.writeLong((Long) this.mValue); break; case FLOAT: - out.writeFloat((Float) this.value); + out.writeFloat((Float) this.mValue); break; case DOUBLE: - out.writeDouble((Double) this.value); + out.writeDouble((Double) this.mValue); break; case CHAR: - out.writeInt((int) ((Character) this.value).charValue()); + out.writeInt((int) ((Character) this.mValue).charValue()); break; case STRING: - out.writeString8((String) this.value); + out.writeString8((String) this.mValue); break; case CHAR_SEQUENCE: - TextUtils.writeToParcel((CharSequence) this.value, out, flags); + TextUtils.writeToParcel((CharSequence) this.mValue, out, flags); break; case BUNDLE: - out.writeBundle((Bundle) this.value); + out.writeBundle((Bundle) this.mValue); break; case BLEND_MODE: - out.writeInt(BlendMode.toValue((BlendMode) this.value)); + out.writeInt(BlendMode.toValue((BlendMode) this.mValue)); break; case URI: case BITMAP: case INTENT: case COLOR_STATE_LIST: case ICON: - out.writeTypedObject((Parcelable) this.value, flags); + out.writeTypedObject((Parcelable) this.mValue, flags); break; default: break; @@ -2211,7 +2205,7 @@ public class RemoteViews implements Parcelable, Filter { @Nullable @Override protected Object getParameterValue(@Nullable View view) throws ActionException { - return this.value; + return this.mValue; } @Override @@ -2221,7 +2215,6 @@ public class RemoteViews implements Parcelable, Filter { } private static final class ResourceReflectionAction extends BaseReflectionAction { - static final int DIMEN_RESOURCE = 1; static final int COLOR_RESOURCE = 2; static final int STRING_RESOURCE = 3; @@ -2258,7 +2251,7 @@ public class RemoteViews implements Parcelable, Filter { try { switch (this.mResourceType) { case DIMEN_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return mResId == 0 ? 0 : resources.getDimensionPixelSize(mResId); case BaseReflectionAction.FLOAT: @@ -2266,10 +2259,10 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException( "dimen resources must be used as INT or FLOAT, " - + "not " + this.type); + + "not " + this.mType); } case COLOR_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return mResId == 0 ? 0 : view.getContext().getColor(mResId); case BaseReflectionAction.COLOR_STATE_LIST: @@ -2278,10 +2271,10 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException( "color resources must be used as INT or COLOR_STATE_LIST," - + " not " + this.type); + + " not " + this.mType); } case STRING_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.CHAR_SEQUENCE: return mResId == 0 ? null : resources.getText(mResId); case BaseReflectionAction.STRING: @@ -2289,7 +2282,7 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException( "string resources must be used as STRING or CHAR_SEQUENCE," - + " not " + this.type); + + " not " + this.mType); } default: throw new ActionException("unknown resource type: " + this.mResourceType); @@ -2308,7 +2301,6 @@ public class RemoteViews implements Parcelable, Filter { } private static final class AttributeReflectionAction extends BaseReflectionAction { - static final int DIMEN_RESOURCE = 1; static final int COLOR_RESOURCE = 2; static final int STRING_RESOURCE = 3; @@ -2347,7 +2339,7 @@ public class RemoteViews implements Parcelable, Filter { } switch (this.mResourceType) { case DIMEN_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return typedArray.getDimensionPixelSize(0, 0); case BaseReflectionAction.FLOAT: @@ -2356,10 +2348,10 @@ public class RemoteViews implements Parcelable, Filter { throw new ActionException( "dimen attribute 0x" + Integer.toHexString(this.mAttrId) + " must be used as INT or FLOAT," - + " not " + this.type); + + " not " + this.mType); } case COLOR_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return typedArray.getColor(0, 0); case BaseReflectionAction.COLOR_STATE_LIST: @@ -2368,10 +2360,10 @@ public class RemoteViews implements Parcelable, Filter { throw new ActionException( "color attribute 0x" + Integer.toHexString(this.mAttrId) + " must be used as INT or COLOR_STATE_LIST," - + " not " + this.type); + + " not " + this.mType); } case STRING_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.CHAR_SEQUENCE: return typedArray.getText(0); case BaseReflectionAction.STRING: @@ -2380,7 +2372,7 @@ public class RemoteViews implements Parcelable, Filter { throw new ActionException( "string attribute 0x" + Integer.toHexString(this.mAttrId) + " must be used as STRING or CHAR_SEQUENCE," - + " not " + this.type); + + " not " + this.mType); } default: // Note: This can only be an implementation error. @@ -2402,7 +2394,6 @@ public class RemoteViews implements Parcelable, Filter { } } private static final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction { - private final float mValue; @ComplexDimensionUnit private final int mUnit; @@ -2435,14 +2426,14 @@ public class RemoteViews implements Parcelable, Filter { DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics(); try { int data = TypedValue.createComplexDimension(this.mValue, this.mUnit); - switch (this.type) { + switch (this.mType) { case ReflectionAction.INT: return TypedValue.complexToDimensionPixelSize(data, dm); case ReflectionAction.FLOAT: return TypedValue.complexToDimension(data, dm); default: throw new ActionException( - "parameter type must be INT or FLOAT, not " + this.type); + "parameter type must be INT or FLOAT, not " + this.mType); } } catch (ActionException ex) { throw ex; @@ -2458,7 +2449,6 @@ public class RemoteViews implements Parcelable, Filter { } private static final class NightModeReflectionAction extends BaseReflectionAction { - private final Object mLightValue; private final Object mDarkValue; @@ -2475,7 +2465,7 @@ public class RemoteViews implements Parcelable, Filter { NightModeReflectionAction(Parcel in) { super(in); - switch (this.type) { + switch (this.mType) { case ICON: mLightValue = in.readTypedObject(Icon.CREATOR); mDarkValue = in.readTypedObject(Icon.CREATOR); @@ -2489,14 +2479,14 @@ public class RemoteViews implements Parcelable, Filter { mDarkValue = in.readInt(); break; default: - throw new ActionException("Unexpected night mode action type: " + this.type); + throw new ActionException("Unexpected night mode action type: " + this.mType); } } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); - switch (this.type) { + switch (this.mType) { case ICON: case COLOR_STATE_LIST: out.writeTypedObject((Parcelable) mLightValue, flags); @@ -2525,7 +2515,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - if (this.type == ICON) { + if (this.mType == ICON) { visitIconUri((Icon) mDarkValue, visitor); visitIconUri((Icon) mLightValue, visitor); } @@ -2617,7 +2607,7 @@ public class RemoteViews implements Parcelable, Filter { } ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) { - this.viewId = viewId; + this.mViewId = viewId; mNestedViews = nestedViews; mIndex = index; mStableId = stableId; @@ -2625,7 +2615,7 @@ public class RemoteViews implements Parcelable, Filter { } ViewGroupActionAdd(Parcel parcel, ApplicationInfo info, int depth) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mIndex = parcel.readInt(); mStableId = parcel.readInt(); mNestedViews = new RemoteViews(parcel, getHierarchyRootData(), info, depth); @@ -2633,7 +2623,7 @@ public class RemoteViews implements Parcelable, Filter { } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mIndex); dest.writeInt(mStableId); mNestedViews.writeToParcel(dest, flags); @@ -2658,7 +2648,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { final Context context = root.getContext(); - final ViewGroup target = root.findViewById(viewId); + final ViewGroup target = root.findViewById(mViewId); if (target == null) { return; @@ -2713,7 +2703,7 @@ public class RemoteViews implements Parcelable, Filter { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); - ViewTree target = root.findViewTreeById(viewId); + ViewTree target = root.findViewTreeById(mViewId); if ((target == null) || !(target.mRoot instanceof ViewGroup)) { return ACTION_NOOP; } @@ -2845,23 +2835,23 @@ public class RemoteViews implements Parcelable, Filter { } ViewGroupActionRemove(@IdRes int viewId, @IdRes int viewIdToKeep) { - this.viewId = viewId; + this.mViewId = viewId; mViewIdToKeep = viewIdToKeep; } ViewGroupActionRemove(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mViewIdToKeep = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mViewIdToKeep); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final ViewGroup target = root.findViewById(viewId); + final ViewGroup target = root.findViewById(mViewId); if (target == null) { return; @@ -2888,7 +2878,7 @@ public class RemoteViews implements Parcelable, Filter { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); - ViewTree target = root.findViewTreeById(viewId); + ViewTree target = root.findViewTreeById(mViewId); if ((target == null) || !(target.mRoot instanceof ViewGroup)) { return ACTION_NOOP; @@ -2953,22 +2943,21 @@ public class RemoteViews implements Parcelable, Filter { * Action to remove a view from its parent. */ private static class RemoveFromParentAction extends Action { - RemoveFromParentAction(@IdRes int viewId) { - this.viewId = viewId; + this.mViewId = viewId; } RemoveFromParentAction(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null || target == root) { return; @@ -2986,7 +2975,7 @@ public class RemoteViews implements Parcelable, Filter { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the correct view. root.createTree(); - ViewTree target = root.findViewTreeById(viewId); + ViewTree target = root.findViewTreeById(mViewId); if (target == null || target == root) { return ACTION_NOOP; @@ -3023,88 +3012,96 @@ public class RemoteViews implements Parcelable, Filter { * (s/t/e/b) or cardinal (l/t/r/b) arrangement. */ private static class TextViewDrawableAction extends Action { + boolean mIsRelative = false; + boolean mUseIcons = false; + int mD1, mD2, mD3, mD4; + Icon mI1, mI2, mI3, mI4; + + boolean mDrawablesLoaded = false; + Drawable mId1, mId2, mId3, mId4; + public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, @DrawableRes int d1, @DrawableRes int d2, @DrawableRes int d3, @DrawableRes int d4) { - this.viewId = viewId; - this.isRelative = isRelative; - this.useIcons = false; - this.d1 = d1; - this.d2 = d2; - this.d3 = d3; - this.d4 = d4; + this.mViewId = viewId; + this.mIsRelative = isRelative; + this.mUseIcons = false; + this.mD1 = d1; + this.mD2 = d2; + this.mD3 = d3; + this.mD4 = d4; } public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, Icon i1, Icon i2, Icon i3, Icon i4) { - this.viewId = viewId; - this.isRelative = isRelative; - this.useIcons = true; - this.i1 = i1; - this.i2 = i2; - this.i3 = i3; - this.i4 = i4; + this.mViewId = viewId; + this.mIsRelative = isRelative; + this.mUseIcons = true; + this.mI1 = i1; + this.mI2 = i2; + this.mI3 = i3; + this.mI4 = i4; } public TextViewDrawableAction(Parcel parcel) { - viewId = parcel.readInt(); - isRelative = (parcel.readInt() != 0); - useIcons = (parcel.readInt() != 0); - if (useIcons) { - i1 = parcel.readTypedObject(Icon.CREATOR); - i2 = parcel.readTypedObject(Icon.CREATOR); - i3 = parcel.readTypedObject(Icon.CREATOR); - i4 = parcel.readTypedObject(Icon.CREATOR); + mViewId = parcel.readInt(); + mIsRelative = (parcel.readInt() != 0); + mUseIcons = (parcel.readInt() != 0); + if (mUseIcons) { + mI1 = parcel.readTypedObject(Icon.CREATOR); + mI2 = parcel.readTypedObject(Icon.CREATOR); + mI3 = parcel.readTypedObject(Icon.CREATOR); + mI4 = parcel.readTypedObject(Icon.CREATOR); } else { - d1 = parcel.readInt(); - d2 = parcel.readInt(); - d3 = parcel.readInt(); - d4 = parcel.readInt(); + mD1 = parcel.readInt(); + mD2 = parcel.readInt(); + mD3 = parcel.readInt(); + mD4 = parcel.readInt(); } } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(isRelative ? 1 : 0); - dest.writeInt(useIcons ? 1 : 0); - if (useIcons) { - dest.writeTypedObject(i1, 0); - dest.writeTypedObject(i2, 0); - dest.writeTypedObject(i3, 0); - dest.writeTypedObject(i4, 0); + dest.writeInt(mViewId); + dest.writeInt(mIsRelative ? 1 : 0); + dest.writeInt(mUseIcons ? 1 : 0); + if (mUseIcons) { + dest.writeTypedObject(mI1, 0); + dest.writeTypedObject(mI2, 0); + dest.writeTypedObject(mI3, 0); + dest.writeTypedObject(mI4, 0); } else { - dest.writeInt(d1); - dest.writeInt(d2); - dest.writeInt(d3); - dest.writeInt(d4); + dest.writeInt(mD1); + dest.writeInt(mD2); + dest.writeInt(mD3); + dest.writeInt(mD4); } } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final TextView target = root.findViewById(viewId); + final TextView target = root.findViewById(mViewId); if (target == null) return; - if (drawablesLoaded) { - if (isRelative) { - target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4); + if (mDrawablesLoaded) { + if (mIsRelative) { + target.setCompoundDrawablesRelativeWithIntrinsicBounds(mId1, mId2, mId3, mId4); } else { - target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4); + target.setCompoundDrawablesWithIntrinsicBounds(mId1, mId2, mId3, mId4); } - } else if (useIcons) { + } else if (mUseIcons) { final Context ctx = target.getContext(); - final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx); - final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx); - final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx); - final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx); - if (isRelative) { + final Drawable id1 = mI1 == null ? null : mI1.loadDrawable(ctx); + final Drawable id2 = mI2 == null ? null : mI2.loadDrawable(ctx); + final Drawable id3 = mI3 == null ? null : mI3.loadDrawable(ctx); + final Drawable id4 = mI4 == null ? null : mI4.loadDrawable(ctx); + if (mIsRelative) { target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4); } else { target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4); } } else { - if (isRelative) { - target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4); + if (mIsRelative) { + target.setCompoundDrawablesRelativeWithIntrinsicBounds(mD1, mD2, mD3, mD4); } else { - target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4); + target.setCompoundDrawablesWithIntrinsicBounds(mD1, mD2, mD3, mD4); } } } @@ -3112,34 +3109,34 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, ActionApplyParams params) { - final TextView target = root.findViewById(viewId); + final TextView target = root.findViewById(mViewId); if (target == null) return ACTION_NOOP; - TextViewDrawableAction copy = useIcons ? - new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) : - new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4); + TextViewDrawableAction copy = mUseIcons + ? new TextViewDrawableAction(mViewId, mIsRelative, mI1, mI2, mI3, mI4) + : new TextViewDrawableAction(mViewId, mIsRelative, mD1, mD2, mD3, mD4); // Load the drawables on the background thread. - copy.drawablesLoaded = true; + copy.mDrawablesLoaded = true; final Context ctx = target.getContext(); - if (useIcons) { - copy.id1 = i1 == null ? null : i1.loadDrawable(ctx); - copy.id2 = i2 == null ? null : i2.loadDrawable(ctx); - copy.id3 = i3 == null ? null : i3.loadDrawable(ctx); - copy.id4 = i4 == null ? null : i4.loadDrawable(ctx); + if (mUseIcons) { + copy.mId1 = mI1 == null ? null : mI1.loadDrawable(ctx); + copy.mId2 = mI2 == null ? null : mI2.loadDrawable(ctx); + copy.mId3 = mI3 == null ? null : mI3.loadDrawable(ctx); + copy.mId4 = mI4 == null ? null : mI4.loadDrawable(ctx); } else { - copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1); - copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2); - copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3); - copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4); + copy.mId1 = mD1 == 0 ? null : ctx.getDrawable(mD1); + copy.mId2 = mD2 == 0 ? null : ctx.getDrawable(mD2); + copy.mId3 = mD3 == 0 ? null : ctx.getDrawable(mD3); + copy.mId4 = mD4 == 0 ? null : ctx.getDrawable(mD4); } return copy; } @Override public boolean prefersAsyncApply() { - return useIcons; + return mUseIcons; } @Override @@ -3149,110 +3146,101 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - if (useIcons) { - visitIconUri(i1, visitor); - visitIconUri(i2, visitor); - visitIconUri(i3, visitor); - visitIconUri(i4, visitor); + if (mUseIcons) { + visitIconUri(mI1, visitor); + visitIconUri(mI2, visitor); + visitIconUri(mI3, visitor); + visitIconUri(mI4, visitor); } } - - boolean isRelative = false; - boolean useIcons = false; - int d1, d2, d3, d4; - Icon i1, i2, i3, i4; - - boolean drawablesLoaded = false; - Drawable id1, id2, id3, id4; } /** * Helper action to set text size on a TextView in any supported units. */ private static class TextViewSizeAction extends Action { + int mUnits; + float mSize; + TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) { - this.viewId = viewId; - this.units = units; - this.size = size; + this.mViewId = viewId; + this.mUnits = units; + this.mSize = size; } TextViewSizeAction(Parcel parcel) { - viewId = parcel.readInt(); - units = parcel.readInt(); - size = parcel.readFloat(); + mViewId = parcel.readInt(); + mUnits = parcel.readInt(); + mSize = parcel.readFloat(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(units); - dest.writeFloat(size); + dest.writeInt(mViewId); + dest.writeInt(mUnits); + dest.writeFloat(mSize); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final TextView target = root.findViewById(viewId); + final TextView target = root.findViewById(mViewId); if (target == null) return; - target.setTextSize(units, size); + target.setTextSize(mUnits, mSize); } @Override public int getActionTag() { return TEXT_VIEW_SIZE_ACTION_TAG; } - - int units; - float size; } /** * Helper action to set padding on a View. */ private static class ViewPaddingAction extends Action { + @Px int mLeft, mTop, mRight, mBottom; + public ViewPaddingAction(@IdRes int viewId, @Px int left, @Px int top, @Px int right, @Px int bottom) { - this.viewId = viewId; - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; + this.mViewId = viewId; + this.mLeft = left; + this.mTop = top; + this.mRight = right; + this.mBottom = bottom; } public ViewPaddingAction(Parcel parcel) { - viewId = parcel.readInt(); - left = parcel.readInt(); - top = parcel.readInt(); - right = parcel.readInt(); - bottom = parcel.readInt(); + mViewId = parcel.readInt(); + mLeft = parcel.readInt(); + mTop = parcel.readInt(); + mRight = parcel.readInt(); + mBottom = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(left); - dest.writeInt(top); - dest.writeInt(right); - dest.writeInt(bottom); + dest.writeInt(mViewId); + dest.writeInt(mLeft); + dest.writeInt(mTop); + dest.writeInt(mRight); + dest.writeInt(mBottom); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; - target.setPadding(left, top, right, bottom); + target.setPadding(mLeft, mTop, mRight, mBottom); } @Override public int getActionTag() { return VIEW_PADDING_ACTION_TAG; } - - @Px int left, top, right, bottom; } /** * Helper action to set layout params on a View. */ private static class LayoutParamAction extends Action { - static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT; static final int LAYOUT_MARGIN_TOP = MARGIN_TOP; static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT; @@ -3274,7 +3262,7 @@ public class RemoteViews implements Parcelable, Filter { */ LayoutParamAction(@IdRes int viewId, int property, float value, @ComplexDimensionUnit int units) { - this.viewId = viewId; + this.mViewId = viewId; this.mProperty = property; this.mValueType = VALUE_TYPE_COMPLEX_UNIT; this.mValue = TypedValue.createComplexDimension(value, units); @@ -3289,21 +3277,21 @@ public class RemoteViews implements Parcelable, Filter { * {@link #VALUE_TYPE_RAW}. */ LayoutParamAction(@IdRes int viewId, int property, int value, @ValueType int valueType) { - this.viewId = viewId; + this.mViewId = viewId; this.mProperty = property; this.mValueType = valueType; this.mValue = value; } public LayoutParamAction(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mProperty = parcel.readInt(); mValueType = parcel.readInt(); mValue = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mProperty); dest.writeInt(mValueType); dest.writeInt(mValue); @@ -3311,7 +3299,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) { return; } @@ -3438,55 +3426,53 @@ public class RemoteViews implements Parcelable, Filter { * Helper action to add a view tag with RemoteInputs. */ private static class SetRemoteInputsAction extends Action { + final Parcelable[] mRemoteInputs; public SetRemoteInputsAction(@IdRes int viewId, RemoteInput[] remoteInputs) { - this.viewId = viewId; - this.remoteInputs = remoteInputs; + this.mViewId = viewId; + this.mRemoteInputs = remoteInputs; } public SetRemoteInputsAction(Parcel parcel) { - viewId = parcel.readInt(); - remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR); + mViewId = parcel.readInt(); + mRemoteInputs = parcel.createTypedArray(RemoteInput.CREATOR); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeTypedArray(remoteInputs, flags); + dest.writeInt(mViewId); + dest.writeTypedArray(mRemoteInputs, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; - target.setTagInternal(R.id.remote_input_tag, remoteInputs); + target.setTagInternal(R.id.remote_input_tag, mRemoteInputs); } @Override public int getActionTag() { return SET_REMOTE_INPUTS_ACTION_TAG; } - - final Parcelable[] remoteInputs; } /** * Helper action to override all textViewColors */ private static class OverrideTextColorsAction extends Action { - - private final int textColor; + private final int mTextColor; public OverrideTextColorsAction(int textColor) { - this.textColor = textColor; + this.mTextColor = textColor; } public OverrideTextColorsAction(Parcel parcel) { - textColor = parcel.readInt(); + mTextColor = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(textColor); + dest.writeInt(mTextColor); } @Override @@ -3499,7 +3485,7 @@ public class RemoteViews implements Parcelable, Filter { if (v instanceof TextView) { TextView textView = (TextView) v; textView.setText(ContrastColorUtil.clearColorSpans(textView.getText())); - textView.setTextColor(textColor); + textView.setTextColor(mTextColor); } if (v instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) v; @@ -3554,34 +3540,33 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetCompoundButtonCheckedAction extends Action { - private final boolean mChecked; SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) { - this.viewId = viewId; + this.mViewId = viewId; mChecked = checked; } SetCompoundButtonCheckedAction(Parcel in) { - viewId = in.readInt(); + mViewId = in.readInt(); mChecked = in.readBoolean(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeBoolean(mChecked); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (!(target instanceof CompoundButton)) { Log.w(LOG_TAG, "Cannot set checked to view " - + viewId + " because it is not a CompoundButton"); + + mViewId + " because it is not a CompoundButton"); return; } @@ -3605,33 +3590,32 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetRadioGroupCheckedAction extends Action { - @IdRes private final int mCheckedId; SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) { - this.viewId = viewId; + this.mViewId = viewId; mCheckedId = checkedId; } SetRadioGroupCheckedAction(Parcel in) { - viewId = in.readInt(); + mViewId = in.readInt(); mCheckedId = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mCheckedId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (!(target instanceof RadioGroup)) { - Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup"); + Log.w(LOG_TAG, "Cannot check " + mViewId + " because it's not a RadioGroup"); return; } @@ -3670,35 +3654,34 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetViewOutlinePreferredRadiusAction extends Action { - @ValueType private final int mValueType; private final int mValue; SetViewOutlinePreferredRadiusAction(@IdRes int viewId, int value, @ValueType int valueType) { - this.viewId = viewId; + this.mViewId = viewId; this.mValueType = valueType; this.mValue = value; } SetViewOutlinePreferredRadiusAction( @IdRes int viewId, float radius, @ComplexDimensionUnit int units) { - this.viewId = viewId; + this.mViewId = viewId; this.mValueType = VALUE_TYPE_COMPLEX_UNIT; this.mValue = TypedValue.createComplexDimension(radius, units); } SetViewOutlinePreferredRadiusAction(Parcel in) { - viewId = in.readInt(); + mViewId = in.readInt(); mValueType = in.readInt(); mValue = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mValueType); dest.writeInt(mValue); } @@ -3706,7 +3689,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; try { @@ -3748,7 +3731,6 @@ public class RemoteViews implements Parcelable, Filter { * {@link #setViewOutlinePreferredRadius(int, float, int)}. */ public static final class RemoteViewOutlineProvider extends ViewOutlineProvider { - private final float mRadius; public RemoteViewOutlineProvider(float radius) { @@ -3821,7 +3803,8 @@ public class RemoteViews implements Parcelable, Filter { return mSizedRemoteViews != null; } - private @Nullable SizeF getIdealSize() { + @Nullable + private SizeF getIdealSize() { return mIdealSize; } @@ -4174,7 +4157,7 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException("Tag " + tag + " not found"); } - }; + } /** * Returns a deep copy of the RemoteViews object. The RemoteView may not be @@ -5907,7 +5890,7 @@ public class RemoteViews implements Parcelable, Filter { * Callback when the RemoteView has finished inflating, * but no actions have been applied yet. */ - default void onViewInflated(View v) {}; + default void onViewInflated(View v) {} void onViewApplied(View v); @@ -6288,7 +6271,6 @@ public class RemoteViews implements Parcelable, Filter { * @hide */ public class ActionApplyParams { - public InteractionHandler handler; public ColorResources colorResources; public Executor executor; @@ -6575,15 +6557,17 @@ public class RemoteViews implements Parcelable, Filter { /** * Parcelable.Creator that instantiates RemoteViews objects */ - public static final @android.annotation.NonNull Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() { - public RemoteViews createFromParcel(Parcel parcel) { - return new RemoteViews(parcel); - } + @NonNull + public static final Parcelable.Creator<RemoteViews> CREATOR = + new Parcelable.Creator<RemoteViews>() { + public RemoteViews createFromParcel(Parcel parcel) { + return new RemoteViews(parcel); + } - public RemoteViews[] newArray(int size) { - return new RemoteViews[size]; - } - }; + public RemoteViews[] newArray(int size) { + return new RemoteViews[size]; + } + }; /** * A representation of the view hierarchy. Only views which have a valid ID are added @@ -7284,7 +7268,8 @@ public class RemoteViews implements Parcelable, Filter { * Get the ID of the top-level view of the XML layout, if set using * {@link RemoteViews#RemoteViews(String, int, int)}. */ - public @IdRes int getViewId() { + @IdRes + public int getViewId() { return mViewId; } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index e0e72ba1b9db..a1ebde76e98e 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -47,8 +47,8 @@ import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; +import android.view.flags.Flags; import android.view.inspector.InspectableProperty; -import android.widget.flags.Flags; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -1011,14 +1011,14 @@ public class ScrollView extends FrameLayout { if (newScrollY != oldScrollY) { super.scrollTo(mScrollX, newScrollY); if (hitLimit) { - if (Flags.platformWidgetHapticScrollFeedback()) { + if (Flags.scrollFeedbackApi()) { initHapticScrollFeedbackProviderIfNotExists(); mHapticScrollFeedbackProvider.onScrollLimit( event.getDeviceId(), event.getSource(), axis, /* isStart= */ newScrollY == 0); } } else { - if (Flags.platformWidgetHapticScrollFeedback()) { + if (Flags.scrollFeedbackApi()) { initHapticScrollFeedbackProviderIfNotExists(); mHapticScrollFeedbackProvider.onScrollProgress( event.getDeviceId(), event.getSource(), axis, delta); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2c413300ef17..e8281eac5928 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -567,6 +567,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mShadowDy; private int mShadowColor; + private int mLastOrientation; + private boolean mPreDrawRegistered; private boolean mPreDrawListenerDetached; @@ -1193,6 +1195,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; mJustificationMode = Layout.JUSTIFICATION_MODE_NONE; + mLastOrientation = getResources().getConfiguration().orientation; final Resources.Theme theme = context.getTheme(); @@ -4591,6 +4594,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mFontWeightAdjustment = newConfig.fontWeightAdjustment; setTypeface(getTypeface()); } + + InputMethodManager imm = getInputMethodManager(); + // if orientation changed and this TextView is currently served. + if (mLastOrientation != newConfig.orientation + && imm != null && imm.hasActiveInputConnection(this)) { + // EditorInfo.internalImeOptions are out of date. + imm.restartInput(this); + } + mLastOrientation = newConfig.orientation; } /** diff --git a/core/java/android/widget/flags/differential_motion_fling_flags.aconfig b/core/java/android/widget/flags/differential_motion_fling_flags.aconfig new file mode 100644 index 000000000000..79cfe566ac05 --- /dev/null +++ b/core/java/android/widget/flags/differential_motion_fling_flags.aconfig @@ -0,0 +1,8 @@ +package: "android.widget.flags" + +flag { + namespace: "toolkit" + name: "enable_platform_widget_differential_motion_fling" + description: "Enables differential motion fling in platform widgets" + bug: "293332089" +}
\ No newline at end of file diff --git a/core/java/android/widget/flags/scroll_view_flags.aconfig b/core/java/android/widget/flags/scroll_view_flags.aconfig deleted file mode 100644 index f93ade28750b..000000000000 --- a/core/java/android/widget/flags/scroll_view_flags.aconfig +++ /dev/null @@ -1,8 +0,0 @@ -package: "android.widget.flags" - -flag { - namespace: "widget" - name: "platform_widget_haptic_scroll_feedback" - description: "Enables haptic scroll feedback in platform widgets" - bug: "287914819" -}
\ No newline at end of file diff --git a/core/java/android/window/IRemoteTransition.aidl b/core/java/android/window/IRemoteTransition.aidl index 2efb68a33889..ec8b66d8b2f2 100644 --- a/core/java/android/window/IRemoteTransition.aidl +++ b/core/java/android/window/IRemoteTransition.aidl @@ -59,4 +59,12 @@ oneway interface IRemoteTransition { void mergeAnimation(in IBinder transition, in TransitionInfo info, in SurfaceControl.Transaction t, in IBinder mergeTarget, in IRemoteTransitionFinishedCallback finishCallback); + + /** + * Called when a different handler has consumed the transition + * + * @param transition An identifier for the transition that was consumed. + * @param aborted Whether the transition is aborted or not. + */ + void onTransitionConsumed(in IBinder transition, in boolean aborted); } diff --git a/core/java/android/window/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java new file mode 100644 index 000000000000..07ac2922304e --- /dev/null +++ b/core/java/android/window/SystemPerformanceHinter.java @@ -0,0 +1,332 @@ +/* + * 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.window; + +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT; +import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH; +import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN; +import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.PerformanceHintManager; +import android.os.Trace; +import android.util.Log; +import android.view.SurfaceControl; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Random; +import java.util.function.Supplier; + +/** + * A helper class to manage performance related hints for a process. This helper is used for both + * long-lived and transient hints. + * + * @hide + */ +public class SystemPerformanceHinter { + private static final String TAG = "SystemPerformanceHinter"; + + // Change app and SF wakeup times to allow sf more time to composite a frame + public static final int HINT_SF_EARLY_WAKEUP = 1 << 0; + // Force max refresh rate + public static final int HINT_SF_FRAME_RATE = 1 << 1; + // Boost CPU & GPU clocks + public static final int HINT_ADPF = 1 << 2; + // Convenience constant for SF only flags + public static final int HINT_SF = HINT_SF_EARLY_WAKEUP | HINT_SF_FRAME_RATE; + // Convenience constant for all the flags + public static final int HINT_ALL = HINT_SF_EARLY_WAKEUP | HINT_SF_FRAME_RATE | HINT_ADPF; + + // Hints that are applied per-display and require a display root surface + private static final int HINT_PER_DISPLAY = HINT_SF_FRAME_RATE; + // Hints that are global (not per-display) + private static final int HINT_GLOBAL = HINT_SF_EARLY_WAKEUP | HINT_ADPF; + + @IntDef(prefix = {"HINT_"}, value = { + HINT_SF_EARLY_WAKEUP, + HINT_SF_FRAME_RATE, + HINT_ADPF, + }) + private @interface HintFlags {} + + /** + * A provider for the root to apply SurfaceControl hints which will be inherited by all children + * of that root. + * @hide + */ + public interface DisplayRootProvider { + /** + * @return the SurfaceControl to apply hints for the given displayId. + */ + @Nullable SurfaceControl getRootForDisplay(int displayId); + } + + /** + * A session where high performance is needed. + * @hide + */ + public class HighPerfSession implements AutoCloseable { + private final @HintFlags int hintFlags; + private final String reason; + private final int displayId; + private final int traceCookie; + + protected HighPerfSession(@HintFlags int hintFlags, int displayId, @NonNull String reason) { + this.hintFlags = hintFlags; + this.reason = reason; + this.displayId = displayId; + this.traceCookie = new Random().nextInt(); + if (hintFlags != 0) { + startSession(this); + } + } + + /** + * Closes this session. + */ + public void close() { + if (hintFlags != 0) { + endSession(this); + } + } + + public void finalize() { + close(); + } + } + + /** + * A no-op implementation of a session. + */ + private class NoOpHighPerfSession extends HighPerfSession { + public NoOpHighPerfSession() { + super(0 /* hintFlags */, -1 /* displayId */, ""); + } + + public void close() { + // Do nothing + } + } + + // The active sessions + private final ArrayList<HighPerfSession> mActiveSessions = new ArrayList<>(); + private final SurfaceControl.Transaction mTransaction; + private final PerformanceHintManager mPerfHintManager; + private @Nullable PerformanceHintManager.Session mAdpfSession; + private @Nullable DisplayRootProvider mDisplayRootProvider; + + + /** + * Constructor for the hinter. + * @hide + */ + public SystemPerformanceHinter(@NonNull Context context, + @Nullable DisplayRootProvider displayRootProvider) { + this(context, displayRootProvider, null /* transactionSupplier */); + } + + /** + * Constructor for the hinter. + * @hide + */ + public SystemPerformanceHinter(@NonNull Context context, + @Nullable DisplayRootProvider displayRootProvider, + @Nullable Supplier<SurfaceControl.Transaction> transactionSupplier) { + mDisplayRootProvider = displayRootProvider; + mPerfHintManager = context.getSystemService(PerformanceHintManager.class); + mTransaction = transactionSupplier != null + ? transactionSupplier.get() + : new SurfaceControl.Transaction(); + } + + /** + * Sets the current ADPF session, required if you are using HINT_ADPF. It is the responsibility + * of the caller to manage up the ADPF session. + * @hide + */ + public void setAdpfSession(PerformanceHintManager.Session adpfSession) { + mAdpfSession = adpfSession; + } + + /** + * Starts a session that requires high performance. + * @hide + */ + public HighPerfSession startSession(@HintFlags int hintFlags, int displayId, + @NonNull String reason) { + if (mDisplayRootProvider == null && (hintFlags & HINT_SF_FRAME_RATE) != 0) { + throw new IllegalArgumentException( + "Using SF frame rate hints requires a valid display root provider"); + } + if (mAdpfSession == null && (hintFlags & HINT_ADPF) != 0) { + throw new IllegalArgumentException("Using ADPF hints requires an ADPF session"); + } + if ((hintFlags & HINT_PER_DISPLAY) != 0) { + if (mDisplayRootProvider.getRootForDisplay(displayId) == null) { + // Just log an error and return early if there is no root as there could be races + // between when a display root is removed and when a hint session is requested + Log.v(TAG, "No display root for displayId=" + displayId); + Trace.instant(TRACE_TAG_WINDOW_MANAGER, "PerfHint-NoDisplayRoot: " + displayId); + return new NoOpHighPerfSession(); + } + } + return new HighPerfSession(hintFlags, displayId, reason); + } + + /** + * Starts a session that requires high performance. + */ + private void startSession(HighPerfSession session) { + int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL); + int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY, + session.displayId); + mActiveSessions.add(session); + int newGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL); + int newPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY, + session.displayId); + + boolean transactionChanged = false; + // Per-display flags + if (nowEnabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) { + SurfaceControl displaySurfaceControl = mDisplayRootProvider.getRootForDisplay( + session.displayId); + mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl, + FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); + mTransaction.setFrameRateCategory(displaySurfaceControl, FRAME_RATE_CATEGORY_HIGH); + transactionChanged = true; + Trace.beginAsyncSection("PerfHint-framerate-" + session.displayId + "-" + + session.reason, session.traceCookie); + } + + // Global flags + if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) { + mTransaction.setEarlyWakeupStart(); + transactionChanged = true; + Trace.beginAsyncSection("PerfHint-early_wakeup-" + session.reason, session.traceCookie); + } + if (nowEnabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) { + mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_UP); + Trace.beginAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie); + } + if (transactionChanged) { + mTransaction.applyAsyncUnsafe(); + } + } + + /** + * Ends a session that requires high performance. + */ + private void endSession(HighPerfSession session) { + int oldGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL); + int oldPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY, + session.displayId); + mActiveSessions.remove(session); + int newGlobalFlags = calculateActiveHintFlags(HINT_GLOBAL); + int newPerDisplayFlags = calculateActiveHintFlagsForDisplay(HINT_PER_DISPLAY, + session.displayId); + + boolean transactionChanged = false; + // Per-display flags + if (nowDisabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) { + SurfaceControl displaySurfaceControl = mDisplayRootProvider.getRootForDisplay( + session.displayId); + mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl, + FRAME_RATE_SELECTION_STRATEGY_SELF); + mTransaction.setFrameRateCategory(displaySurfaceControl, FRAME_RATE_CATEGORY_DEFAULT); + transactionChanged = true; + Trace.endAsyncSection("PerfHint-framerate-" + session.displayId + "-" + session.reason, + session.traceCookie); + } + + // Global flags + if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) { + mTransaction.setEarlyWakeupEnd(); + transactionChanged = true; + Trace.endAsyncSection("PerfHint-early_wakeup-" + session.reason, session.traceCookie); + } + if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) { + mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET); + Trace.endAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie); + } + if (transactionChanged) { + mTransaction.applyAsyncUnsafe(); + } + } + + /** + * Checks if checkFlags was previously not set and is now set. + */ + private boolean nowEnabled(@HintFlags int oldFlags, @HintFlags int newFlags, + @HintFlags int checkFlags) { + return (oldFlags & checkFlags) == 0 && (newFlags & checkFlags) != 0; + } + + /** + * Checks if checkFlags was previously set and is now not set. + */ + private boolean nowDisabled(@HintFlags int oldFlags, @HintFlags int newFlags, + @HintFlags int checkFlags) { + return (oldFlags & checkFlags) != 0 && (newFlags & checkFlags) == 0; + } + + /** + * @return the combined hint flags for all active sessions, filtered by {@param filterFlags}. + */ + private @HintFlags int calculateActiveHintFlags(@HintFlags int filterFlags) { + int flags = 0; + for (int i = 0; i < mActiveSessions.size(); i++) { + flags |= mActiveSessions.get(i).hintFlags & filterFlags; + } + return flags; + } + + /** + * @return the combined hint flags for all active sessions for a given display, filtered by + * {@param filterFlags}. + */ + private @HintFlags int calculateActiveHintFlagsForDisplay(@HintFlags int filterFlags, + int displayId) { + int flags = 0; + for (int i = 0; i < mActiveSessions.size(); i++) { + final HighPerfSession session = mActiveSessions.get(i); + if (session.displayId == displayId) { + flags |= mActiveSessions.get(i).hintFlags & filterFlags; + } + } + return flags; + } + + /** + * Dumps the existing sessions. + */ + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG + ":"); + pw.println(innerPrefix + "Active sessions (" + mActiveSessions.size() + "):"); + for (int i = 0; i < mActiveSessions.size(); i++) { + final HighPerfSession s = mActiveSessions.get(i); + pw.println(innerPrefix + " reason=" + s.reason + + " flags=" + s.hintFlags + + " display=" + s.displayId); + } + } +} diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 3323ae576f33..c2b519619690 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -897,6 +897,23 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Moves the PiP activity of a parent task to a pinned root task. + * @param parentToken the parent task of the PiP activity + * @param bounds the entry bounds + * @hide + */ + @NonNull + public WindowContainerTransaction movePipActivityToPinnedRootTask( + @NonNull WindowContainerToken parentToken, @NonNull Rect bounds) { + mHierarchyOps.add(new HierarchyOp + .Builder(HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK) + .setContainer(parentToken.asBinder()) + .setBounds(bounds) + .build()); + return this; + } + + /** * Merges another WCT into this one. * @param transfer When true, this will transfer everything from other potentially leaving * other in an unusable state. When false, other is left alone, but @@ -1327,6 +1344,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS = 15; public static final int HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH = 16; public static final int HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION = 17; + public static final int HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK = 18; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1379,6 +1397,9 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private ShortcutInfo mShortcutInfo; + @Nullable + private Rect mBounds; + private boolean mAlwaysOnTop; private boolean mReparentLeafTaskIfRelaunch; @@ -1482,6 +1503,7 @@ public final class WindowContainerTransaction implements Parcelable { public HierarchyOp(@NonNull HierarchyOp copy) { mType = copy.mType; mContainer = copy.mContainer; + mBounds = copy.mBounds; mReparent = copy.mReparent; mInsetsFrameProvider = copy.mInsetsFrameProvider; mInsetsFrameOwner = copy.mInsetsFrameOwner; @@ -1501,6 +1523,7 @@ public final class WindowContainerTransaction implements Parcelable { protected HierarchyOp(Parcel in) { mType = in.readInt(); mContainer = in.readStrongBinder(); + mBounds = in.readTypedObject(Rect.CREATOR); mReparent = in.readStrongBinder(); mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR); mInsetsFrameOwner = in.readStrongBinder(); @@ -1599,6 +1622,11 @@ public final class WindowContainerTransaction implements Parcelable { return mShortcutInfo; } + @NonNull + public Rect getBounds() { + return mBounds; + } + /** Gets a string representation of a hierarchy-op type. */ public static String hopToString(int type) { switch (type) { @@ -1709,6 +1737,7 @@ public final class WindowContainerTransaction implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); dest.writeStrongBinder(mContainer); + dest.writeTypedObject(mBounds, flags); dest.writeStrongBinder(mReparent); dest.writeTypedObject(mInsetsFrameProvider, flags); dest.writeStrongBinder(mInsetsFrameOwner); @@ -1783,6 +1812,9 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private ShortcutInfo mShortcutInfo; + @Nullable + private Rect mBounds; + private boolean mAlwaysOnTop; private boolean mReparentLeafTaskIfRelaunch; @@ -1867,6 +1899,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setBounds(@NonNull Rect bounds) { + mBounds = bounds; + return this; + } + HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; @@ -1887,6 +1924,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mAlwaysOnTop = mAlwaysOnTop; hierarchyOp.mTaskFragmentOperation = mTaskFragmentOperation; hierarchyOp.mShortcutInfo = mShortcutInfo; + hierarchyOp.mBounds = mBounds; hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch; return hierarchyOp; diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig new file mode 100644 index 000000000000..1b98806a0f01 --- /dev/null +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -0,0 +1,11 @@ +package: "com.android.window.flags" + +# Project link: https://gantry.corp.google.com/projects/android_platform_window_surfaces/changes + +flag { + namespace: "window_surfaces" + name: "surface_trusted_overlay" + description: "Whether to add trusted overlay flag on the SurfaceControl or the InputWindow" + is_fixed_read_only: true + bug: "292032926" +} diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index cb2d93474971..b1d22e069d9d 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -86,6 +86,28 @@ public class SystemUiSystemPropertiesFlags { public static final Flag ENABLE_ATTENTION_HELPER_REFACTOR = devFlag( "persist.debug.sysui.notification.enable_attention_helper_refactor"); + // TODO b/291899544: for released flags, use resource config values + /** Value used by polite notif. feature */ + public static final Flag NOTIF_COOLDOWN_T1 = devFlag( + "persist.debug.sysui.notification.notif_cooldown_t1", 5000); + /** Value used by polite notif. feature */ + public static final Flag NOTIF_COOLDOWN_T2 = devFlag( + "persist.debug.sysui.notification.notif_cooldown_t2", 3000); + /** Value used by polite notif. feature */ + public static final Flag NOTIF_VOLUME1 = devFlag( + "persist.debug.sysui.notification.notif_volume1", 30); + public static final Flag NOTIF_VOLUME2 = devFlag( + "persist.debug.sysui.notification.notif_volume2", 0); + /** Value used by polite notif. feature. -1 to ignore the counter */ + public static final Flag NOTIF_COOLDOWN_COUNTER_RESET = devFlag( + "persist.debug.sysui.notification.notif_cooldown_counter_reset", 10); + /** + * Value used by polite notif. feature: cooldown behavior/strategy. Valid values: rule1, + * rule2 + */ + public static final Flag NOTIF_COOLDOWN_RULE = devFlag( + "persist.debug.sysui.notification.notif_cooldown_rule", "rule1"); + /** b/301242692: Visit extra URIs used in notifications to prevent security issues. */ public static final Flag VISIT_RISKY_URIS = devFlag( "persist.sysui.notification.visit_risky_uris"); @@ -97,6 +119,10 @@ public class SystemUiSystemPropertiesFlags { public interface FlagResolver { /** Is the flag enabled? */ boolean isEnabled(Flag flag); + /** Get the flag value (integer) */ + int getIntValue(Flag flag); + /** Get the flag value (string) */ + String getStringValue(Flag flag); } /** The primary, immutable resolver returned by getResolver() */ @@ -134,6 +160,22 @@ public class SystemUiSystemPropertiesFlags { } /** + * Creates a flag that with a default integer value in debuggable builds. + */ + @VisibleForTesting + public static Flag devFlag(String name, int defaultValue) { + return new Flag(name, defaultValue, null); + } + + /** + * Creates a flag that with a default string value in debuggable builds. + */ + @VisibleForTesting + public static Flag devFlag(String name, String defaultValue) { + return new Flag(name, defaultValue, null); + } + + /** * Creates a flag that is disabled by default in debuggable builds. * It can be enabled or force-disabled by setting this flag's SystemProperty to 1 or 0. * If this flag's SystemProperty is not set, the flag can be enabled by setting the @@ -161,6 +203,8 @@ public class SystemUiSystemPropertiesFlags { public static final class Flag { public final String mSysPropKey; public final boolean mDefaultValue; + public final int mDefaultIntValue; + public final String mDefaultStringValue; @Nullable public final Flag mDebugDefault; @@ -170,6 +214,24 @@ public class SystemUiSystemPropertiesFlags { mSysPropKey = sysPropKey; mDefaultValue = defaultValue; mDebugDefault = debugDefault; + mDefaultIntValue = 0; + mDefaultStringValue = null; + } + + public Flag(@NonNull String sysPropKey, int defaultValue, @Nullable Flag debugDefault) { + mSysPropKey = sysPropKey; + mDefaultIntValue = defaultValue; + mDebugDefault = debugDefault; + mDefaultValue = false; + mDefaultStringValue = null; + } + + public Flag(@NonNull String sysPropKey, String defaultValue, @Nullable Flag debugDefault) { + mSysPropKey = sysPropKey; + mDefaultStringValue = defaultValue; + mDebugDefault = debugDefault; + mDefaultValue = false; + mDefaultIntValue = 0; } } @@ -181,6 +243,16 @@ public class SystemUiSystemPropertiesFlags { public boolean isEnabled(Flag flag) { return flag.mDefaultValue; } + + @Override + public int getIntValue(Flag flag) { + return flag.mDefaultIntValue; + } + + @Override + public String getStringValue(Flag flag) { + return flag.mDefaultStringValue; + } } /** Implementation of the interface used in debuggable builds. */ @@ -199,5 +271,23 @@ public class SystemUiSystemPropertiesFlags { public boolean getBoolean(String key, boolean defaultValue) { return SystemProperties.getBoolean(key, defaultValue); } + + /** Look up the value; overridable for tests to avoid needing to set SystemProperties */ + @VisibleForTesting + public int getIntValue(Flag flag) { + if (flag.mDebugDefault == null) { + return SystemProperties.getInt(flag.mSysPropKey, flag.mDefaultIntValue); + } + return SystemProperties.getInt(flag.mSysPropKey, getIntValue(flag.mDebugDefault)); + } + + /** Look up the value; overridable for tests to avoid needing to set SystemProperties */ + @VisibleForTesting + public String getStringValue(Flag flag) { + if (flag.mDebugDefault == null) { + return SystemProperties.get(flag.mSysPropKey, flag.mDefaultStringValue); + } + return SystemProperties.get(flag.mSysPropKey, getStringValue(flag.mDebugDefault)); + } } } diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java new file mode 100644 index 000000000000..e55c64199f45 --- /dev/null +++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java @@ -0,0 +1,56 @@ +/* + * 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.display; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.util.Log; +import android.view.Display; + +/** + * Constants and utility methods for refresh rate settings. + */ +public class RefreshRateSettingsUtils { + + private static final String TAG = "RefreshRateSettingsUtils"; + + public static final float DEFAULT_REFRESH_RATE = 60f; + + /** + * Find the highest refresh rate among all the modes of the default display. + * + * @param context The context + * @return The highest refresh rate + */ + public static float findHighestRefreshRateForDefaultDisplay(Context context) { + final DisplayManager dm = context.getSystemService(DisplayManager.class); + final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY); + + if (display == null) { + Log.w(TAG, "No valid default display device"); + return DEFAULT_REFRESH_RATE; + } + + float maxRefreshRate = DEFAULT_REFRESH_RATE; + for (Display.Mode mode : display.getSupportedModes()) { + if (mode.getRefreshRate() > maxRefreshRate) { + maxRefreshRate = mode.getRefreshRate(); + } + } + return maxRefreshRate; + } +} diff --git a/core/java/com/android/internal/foldables/Android.bp b/core/java/com/android/internal/foldables/Android.bp new file mode 100644 index 000000000000..f1d06da98186 --- /dev/null +++ b/core/java/com/android/internal/foldables/Android.bp @@ -0,0 +1,7 @@ +aconfig_declarations { + name: "fold_lock_setting_flags", + package: "com.android.internal.foldables.flags", + srcs: [ + "fold_lock_setting_flags.aconfig", + ], +} diff --git a/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java b/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java index 4e3888a9e92d..a115ecff4ac3 100644 --- a/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java +++ b/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java @@ -17,16 +17,23 @@ package com.android.internal.foldables; import android.content.res.Resources; +import android.os.Build; import android.sysprop.FoldLockBehaviorProperties; +import android.util.Slog; import com.android.internal.R; +import com.android.internal.foldables.flags.Flags; + +import java.util.function.Supplier; /** * Wrapper class to access {@link FoldLockBehaviorProperties} and also assists with testing */ public class FoldLockSettingAvailabilityProvider { - boolean mFoldLockBehaviorResourceValue; + private static final String TAG = "FoldLockSettingAvailabilityProvider"; + private final boolean mFoldLockBehaviorResourceValue; + private final Supplier<Boolean> mFoldLockSettingEnabled = Flags::foldLockSettingEnabled; public FoldLockSettingAvailabilityProvider(Resources resources) { mFoldLockBehaviorResourceValue = resources.getBoolean( @@ -35,6 +42,22 @@ public class FoldLockSettingAvailabilityProvider { public boolean isFoldLockBehaviorAvailable() { return mFoldLockBehaviorResourceValue - && FoldLockBehaviorProperties.fold_lock_setting_enabled().orElse(false); + && flagOrSystemProperty(); + } + + private boolean flagOrSystemProperty() { + if ((Build.IS_ENG || Build.IS_USERDEBUG) + && FoldLockBehaviorProperties.fold_lock_setting_enabled().orElse(false)) { + return true; + } + try { + return mFoldLockSettingEnabled.get(); + } catch (Throwable ex) { + Slog.i(TAG, + "Flags not ready yet. Return false for " + + Flags.FLAG_FOLD_LOCK_SETTING_ENABLED, + ex); + return false; + } } } diff --git a/core/java/com/android/internal/foldables/OWNERS b/core/java/com/android/internal/foldables/OWNERS new file mode 100644 index 000000000000..6ce1ee4d3de2 --- /dev/null +++ b/core/java/com/android/internal/foldables/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/display/OWNERS diff --git a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig new file mode 100644 index 000000000000..44f436eaaa19 --- /dev/null +++ b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.internal.foldables.flags" + +flag { + name: "fold_lock_setting_enabled" + namespace: "display_manager" + description: "Feature flag for Fold Lock Setting" + bug: "274447767" + is_fixed_read_only: true +} diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 1bfb51cc4b82..6e836e077e44 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -837,25 +837,41 @@ public class InteractionJankMonitor { @WorkerThread private void updateProperties(DeviceConfig.Properties properties) { - mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, - DEFAULT_SAMPLING_INTERVAL); - mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY, - DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); - mTraceThresholdFrameTimeMillis = properties.getInt( - SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY, - DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); - // Never allow the debug overlay to be used on user builds - boolean debugOverlayEnabled = Build.IS_DEBUGGABLE && properties.getBoolean( - SETTINGS_DEBUG_OVERLAY_ENABLED_KEY, - DEFAULT_DEBUG_OVERLAY_ENABLED); - if (debugOverlayEnabled && mDebugOverlay == null) { - mDebugOverlay = new InteractionMonitorDebugOverlay(mLock, mDebugBgColor, mDebugYOffset); - } else if (!debugOverlayEnabled && mDebugOverlay != null) { - mDebugOverlay.dispose(); - mDebugOverlay = null; + for (String property : properties.getKeyset()) { + switch (property) { + case SETTINGS_SAMPLING_INTERVAL_KEY: + mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL); + break; + case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY: + mTraceThresholdMissedFrames = + properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); + break; + case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY: + mTraceThresholdFrameTimeMillis = + properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); + break; + case SETTINGS_ENABLED_KEY: + mEnabled = properties.getBoolean(property, DEFAULT_ENABLED); + break; + case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY: + // Never allow the debug overlay to be used on user builds + boolean debugOverlayEnabled = Build.IS_DEBUGGABLE + && properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED); + if (debugOverlayEnabled && mDebugOverlay == null) { + mDebugOverlay = new InteractionMonitorDebugOverlay( + mLock, mDebugBgColor, mDebugYOffset); + } else if (!debugOverlayEnabled && mDebugOverlay != null) { + mDebugOverlay.dispose(); + mDebugOverlay = null; + } + break; + default: + if (DEBUG) { + Log.d(TAG, "Got a change event for an unknown property: " + + property + " => " + properties.getString(property, "")); + } + } } - // The memory visibility is powered by the volatile field, mEnabled. - mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); } @VisibleForTesting diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 1be916f44f5b..85662634c22d 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -290,11 +290,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind }; private Consumer<Boolean> mCrossWindowBlurEnabledListener; + private final WearGestureInterceptionDetector mWearGestureInterceptionDetector; + DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { super(context); mFeatureId = featureId; - mShowInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); mHideInterpolator = AnimationUtils.loadInterpolator(context, @@ -314,6 +315,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind updateLogTag(params); mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); + + mWearGestureInterceptionDetector = + WearGestureInterceptionDetector.isEnabled(context) + ? new WearGestureInterceptionDetector(context, this) + : null; } void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { @@ -544,6 +550,18 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl != null && mWearGestureInterceptionDetector != null) { + boolean wasIntercepting = mWearGestureInterceptionDetector.isIntercepting(); + boolean intercepting = mWearGestureInterceptionDetector.onInterceptTouchEvent(event); + if (wasIntercepting != intercepting) { + viewRootImpl.updateDecorViewGestureInterception(intercepting); + } + if (intercepting) { + return true; + } + } + if (!SWEEP_OPEN_MENU) { return false; } diff --git a/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java new file mode 100644 index 000000000000..6fd50180e78b --- /dev/null +++ b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.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.internal.policy; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.TypedArray; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; + +/** + * Wear-specific gesture interception detector to be installed at DecorView, for compatibility of + * apps depending on legacy SwipeDismissLayout behavior. + * + * <p>Results of the detector will be used by {@code DecorView} to intercept motion events. The + * interception state will also be sent to {@code android.view.ViewRootImpl} and {@code + * com.android.server.wm.DisplayContent} through {@code android.view.IWindowSession}. + * + * <p>SystemUI can register {@code android.view.IDecorViewGestureListener} to listen for the result + * of the detector. The result will be valid for between a pair of touch down/up events. + */ +public class WearGestureInterceptionDetector { + private static final boolean DEBUG = false; + private static final String TAG = "WearGestureInterceptionDetector"; + + private final DecorView mInstalledDecorView; + private final float mTouchSlop; + private final float mSwipingStartThreshold; + private boolean mSwiping; + + private float mDownX; + private float mDownY; + private int mActivePointerId; + private boolean mDiscardIntercept; + + WearGestureInterceptionDetector(Context context, DecorView installedDecorView) { + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mInstalledDecorView = installedDecorView; + mSwipingStartThreshold = mTouchSlop * 2; + } + + /** Check if this gesture interception detector should be enabled. */ + public static boolean isEnabled(Context context) { + PackageManager pm = context.getPackageManager(); + if (!pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + return false; + } + + // Compatibility check for flag that disables legacy SwipeDismissLayout. + TypedArray windowAttr = + context.obtainStyledAttributes(new int[] {android.R.attr.windowSwipeToDismiss}); + boolean windowSwipeToDismiss = true; + if (windowAttr.getIndexCount() > 0) { + windowSwipeToDismiss = windowAttr.getBoolean(0, true); + } + windowAttr.recycle(); + return windowSwipeToDismiss; + } + + private boolean isPointerIndexValid(MotionEvent ev) { + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == -1) { + if (DEBUG) { + Log.e(TAG, "Invalid pointer index: ignoring."); + } + mDiscardIntercept = true; + return false; + } + return true; + } + + private void updateSwiping(MotionEvent ev) { + if (mSwiping) { + return; + } + float deltaX = ev.getRawX() - mDownX; + float deltaY = ev.getRawY() - mDownY; + // Check if we have left the touch slop area. + if ((deltaX * deltaX) + (deltaY * deltaY) > (mTouchSlop * mTouchSlop)) { + mSwiping = deltaX > mSwipingStartThreshold && Math.abs(deltaY) < Math.abs(deltaX); + } + } + + private void updateDiscardIntercept(MotionEvent ev) { + if (!mSwiping) { + // Don't look at canScroll until we have passed the touch slop + return; + } + if (mDiscardIntercept) { + return; + } + final boolean checkLeft = mDownX < ev.getRawX(); + final float x = ev.getX(mActivePointerId); + final float y = ev.getY(mActivePointerId); + if (canScroll(mInstalledDecorView, false, checkLeft, x, y)) { + mDiscardIntercept = true; + } + } + + /** Resets internal members when canceling. */ + private void resetMembers() { + mDownX = 0; + mDownY = 0; + mSwiping = false; + mDiscardIntercept = false; + } + + /** Should we intercept the MotionEvent for system gesture? */ + public boolean isIntercepting() { + return !mDiscardIntercept && mSwiping; + } + + /** Tests if the MotionEvent should be intercepted */ + public boolean onInterceptTouchEvent(MotionEvent ev) { + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + resetMembers(); + mDownX = ev.getRawX(); + mDownY = ev.getRawY(); + mActivePointerId = ev.getPointerId(0); + break; + case MotionEvent.ACTION_POINTER_DOWN: + mActivePointerId = ev.getPointerId(ev.getActionIndex()); + break; + case MotionEvent.ACTION_POINTER_UP: + int associatedPointerIndex = ev.getActionIndex(); + if (ev.getPointerId(associatedPointerIndex) == mActivePointerId) { + // This was our active pointer going up. + // Choose the first available pointer index. + int newActionIndex = associatedPointerIndex == 0 ? 1 : 0; + mActivePointerId = ev.getPointerId(newActionIndex); + } + break; + case MotionEvent.ACTION_MOVE: + if (mDiscardIntercept) { + break; + } + if (!isPointerIndexValid(ev)) { + break; + } + updateSwiping(ev); + updateDiscardIntercept(ev); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + resetMembers(); + break; + } + return isIntercepting(); + } + + /** + * Tests scroll-ability within child views of v in the direction of dx. + * + * @param v View to test for horizontal scroll-ability + * @param checkSelf Whether the view v passed should itself be checked for scroll-ability + * (true), or just its children (false). + * @param checkLeft Which direction to check? Left = true, right = false. + * @param x X coordinate of the active touch point + * @param y Y coordinate of the active touch point + * @return true if child views of v can be scrolled by delta of dx. + */ + private boolean canScroll(View v, boolean checkSelf, boolean checkLeft, float x, float y) { + if (v instanceof ViewGroup) { + final ViewGroup group = (ViewGroup) v; + final int scrollX = v.getScrollX(); + final int scrollY = v.getScrollY(); + final int count = group.getChildCount(); + for (int i = count - 1; i >= 0; i--) { + final View child = group.getChildAt(i); + + if (x + scrollX < child.getLeft() + || x + scrollX >= child.getRight() + || y + scrollY < child.getTop() + || y + scrollY >= child.getBottom()) { + // This child is out of bound, don't bother checking. + continue; + } + + // Recursively check until finding the first scrollable or none is scrollable. + if (canScroll( + /* view= */ child, + /* checkSelf= */ true, + /* checkLeft= */ checkLeft, + /* x= */ x + scrollX - child.getLeft(), + /* y= */ y + scrollY - child.getTop())) { + return true; + } + } + } + + return checkSelf && v.canScrollHorizontally(checkLeft ? -1 : 1); + } +} diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java index 06e69f2d9859..fd435d0595c2 100644 --- a/core/java/com/android/internal/view/FloatingActionMode.java +++ b/core/java/com/android/internal/view/FloatingActionMode.java @@ -222,6 +222,7 @@ public final class FloatingActionMode extends ActionMode { private boolean isContentRectWithinBounds() { mContext.getDisplayNoVerify().getRealSize(mDisplaySize); mScreenRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); + mScreenRect.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]); return intersectsClosed(mContentRectOnScreen, mScreenRect) && intersectsClosed(mContentRectOnScreen, mViewRectOnScreen); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 9384f41e26f5..f79dbe761e07 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -469,9 +469,10 @@ static void nativeSetDefaultBufferSize(JNIEnv* env, jclass clazz, jlong nativeOb } } -static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) { +static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync, + jboolean oneWay) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - transaction->apply(sync); + transaction->apply(sync, oneWay); } static void nativeMergeTransaction(JNIEnv* env, jclass clazz, @@ -2119,7 +2120,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDefaultBufferSize}, {"nativeCreateTransaction", "()J", (void*)nativeCreateTransaction }, - {"nativeApplyTransaction", "(JZ)V", + {"nativeApplyTransaction", "(JZZ)V", (void*)nativeApplyTransaction }, {"nativeGetNativeTransactionFinalizer", "()J", (void*)nativeGetNativeTransactionFinalizer }, diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 6c936801de18..473270229bdb 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -98,6 +98,7 @@ message SecureSettingsProto { // Settings for font scaling optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_magnification_gesture = 53 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Accessibility accessibility = 2; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e34e42317c0c..b0ecc60de597 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -48,6 +48,7 @@ <protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" /> <protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" /> + <protected-broadcast android:name="android.intent.action.PACKAGE_UNSTOPPED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" /> <protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION" /> @@ -7232,6 +7233,15 @@ android:description="@string/permdesc_fullScreenIntent" android:protectionLevel="normal|appop" /> + <!-- @SystemApi Required for the privileged assistant apps targeting + {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} + that receive voice trigger from the trusted hotword detection service. + <p>Protection level: signature|privileged|appop + @FlaggedApi("android.permission.flags.voice_activation_permission_apis") + @hide --> + <permission android:name="android.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO" + android:protectionLevel="signature|privileged|appop" /> + <!-- @SystemApi Allows requesting the framework broadcast the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent. @hide --> diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml index 8b6c90141bb7..27f8138ac5e3 100644 --- a/core/res/res/layout/autofill_save.xml +++ b/core/res/res/layout/autofill_save.xml @@ -60,10 +60,11 @@ android:gravity="center" android:textAppearance="@style/AutofillSaveUiTitle"> </TextView> - <LinearLayout + <FrameLayout android:id="@+id/autofill_save_custom_subtitle" android:layout_width="match_parent" android:layout_height="wrap_content" + android:minHeight="0dp" android:visibility="gone"/> </LinearLayout> diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml index 63fe47110c8b..2c6e0a7d3ef4 100644 --- a/core/res/res/layout/notification_expand_button.xml +++ b/core/res/res/layout/notification_expand_button.xml @@ -52,7 +52,7 @@ android:id="@+id/expand_button_icon" android:layout_width="@dimen/notification_expand_button_pill_height" android:layout_height="@dimen/notification_expand_button_pill_height" - android:padding="2dp" + android:padding="@dimen/notification_expand_button_icon_padding" android:scaleType="fitCenter" android:importantForAccessibility="no" /> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index a49e5477aabd..6f879e42b534 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -674,7 +674,7 @@ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ডিভাইস আনলক করুন"</string> <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"অন্য কোনওভাবে আনলক করার চেষ্টা করুন"</string> <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"আপনার \'ফিঙ্গারপ্রিন্ট\' শনাক্ত করা না গেলে \'ফেস আনলক\' ব্যবহার করুন, যেমন যখন আপনার আঙুল ভিজে থাকে"</string> - <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"আপনার মুখ শনাক্ত করা না গেলে \'ফিঙ্গারপ্রিন্ট আনলক\' ব্যবহার করুন, যেমন যখন পর্যাপ্ত আলো নেই"</string> + <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"পর্যাপ্ত আলো না থাকার পরিস্থিতিতে, আপনার মুখ শনাক্ত করা না গেলে \'ফিঙ্গারপ্রিন্ট আনলক\' ব্যবহার করুন"</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ফেস আনলক"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"\'ফেস আনলক\' ফিচার ব্যবহার করার ক্ষেত্রে হওয়া সমস্যা"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপনার ফেস মডেল মুছে দেওয়ার জন্য ট্যাপ করুন এবং তারপরে আবার ফেস যোগ করুন"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index a897170d0e63..2150324d2fb1 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -476,7 +476,7 @@ <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"atzitu kokapen-hornitzaileen komando gehigarriak"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Kokapen-hornitzailearen agindu gehigarriak erabiltzeko baimena ematen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string> <string name="permlab_accessFineLocation" msgid="6426318438195622966">"lortu kokapen zehatza aurreko planoan bakarrik"</string> - <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Abian denean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Bateria-erabilera areagotzen du horrek."</string> + <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Abian denean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Agian bateria gehiago erabiliko du."</string> <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"atzitu gutxi gorabeherako kokapena aurreko planoan bakarrik"</string> <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Abian denean, aplikazioak gutxi gorabeherako kokapena lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan."</string> <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"atzitu kokapena atzeko planoan"</string> @@ -2137,7 +2137,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Aplikazioak ez du grabatzeko baimenik, baina baliteke audioa grabatzea USB bidezko gailu horren bidez."</string> <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Pantaila nagusia"</string> <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Atzera"</string> - <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Erabilitako azken aplikazioak"</string> + <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Azkenaldian erabilitako aplikazioak"</string> <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Jakinarazpenak"</string> <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Ezarpen bizkorrak"</string> <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Piztu edo itzaltzeko leihoa"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 286751628ea5..d7d29a4c5ef7 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -299,8 +299,8 @@ <string name="android_system_label" msgid="5974767339591067210">"Système Android"</string> <string name="user_owner_label" msgid="8628726904184471211">"Passer au profil personnel"</string> <string name="managed_profile_label" msgid="7316778766973512382">"Passer au profil pro"</string> - <string name="user_owner_app_label" msgid="1553595155465750298">"Passer au <xliff:g id="APP_NAME">%1$s</xliff:g> personnel"</string> - <string name="managed_profile_app_label" msgid="367401088383965725">"Passer au <xliff:g id="APP_NAME">%1$s</xliff:g> professionnel"</string> + <string name="user_owner_app_label" msgid="1553595155465750298">"Passer à l\'application <xliff:g id="APP_NAME">%1$s</xliff:g> personnelle"</string> + <string name="managed_profile_app_label" msgid="367401088383965725">"Passer à l\'application <xliff:g id="APP_NAME">%1$s</xliff:g> professionnelle"</string> <string name="permgrouplab_contacts" msgid="4254143639307316920">"Contacts"</string> <string name="permgroupdesc_contacts" msgid="9163927941244182567">"accéder à vos contacts"</string> <string name="permgrouplab_location" msgid="1858277002233964394">"Position"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 5a98ef8d6885..d2b6dc8663aa 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -674,7 +674,7 @@ <string name="device_unlock_notification_name" msgid="2632928999862915709">"기기 잠금 해제"</string> <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"다른 잠금 해제 방법 사용"</string> <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"손가락에 물기가 있는 등 지문이 인식되지 않을 때는 얼굴 인식 잠금 해제를 사용하세요."</string> - <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"충분히 밝지 않은 경우 등 얼굴이 인식되지 않을 때는 지문 잠금 해제를 사용하세요."</string> + <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"주변이 어두운 경우 등 얼굴이 인식되지 않을 때는 지문 잠금 해제를 사용해 보세요."</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"얼굴 인식 잠금 해제"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"얼굴 인식 잠금 해제 문제"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"탭하여 얼굴 모델을 삭제한 후 다시 얼굴을 추가하세요"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 5f3b3159aae9..485e9b614d01 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -674,7 +674,7 @@ <string name="device_unlock_notification_name" msgid="2632928999862915709">"डिव्हाइस अनलॉक"</string> <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"अनलॉक करण्याची दुसरी पद्धत वापरून पहा"</string> <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"तुमचे फिंगरप्रिंट ओळखले जात नाही, तेव्हा फेस अनलॉक वापरा, जसे की तुमची बोटे ओली असताना"</string> - <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"तुमचा चेहरा ओळखला जात नाही, तेव्हा फिंगरप्रिंट अनलॉक वापरा, जसे की पुरेसा प्रकाश नसताना"</string> + <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"पुरेसा प्रकाश नसणे इत्यादिमुळे तुमचा चेहरा ओळखला जात नाही, अशावेळी फिंगरप्रिंट अनलॉक वापरा"</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फेस अनलॉक"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"फेस अनलॉकसंबंधित समस्या"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"फेस मॉडेल हटवण्यासाठी टॅप करा, त्यानंतर तुमचा चेहरा पुन्हा जोडा"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 9583c0e51b95..628627d53e8a 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -675,7 +675,7 @@ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueio do dispositivo"</string> <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Tente desbloquear de outra maneira"</string> <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use o Desbloqueio facial quando sua impressão digital não for reconhecida, como quando seus dedos estiverem molhados"</string> - <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, como quando não houver luz suficiente"</string> + <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, se estiver escuro, por exemplo"</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o Desbloqueio facial"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 9583c0e51b95..628627d53e8a 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -675,7 +675,7 @@ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueio do dispositivo"</string> <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Tente desbloquear de outra maneira"</string> <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use o Desbloqueio facial quando sua impressão digital não for reconhecida, como quando seus dedos estiverem molhados"</string> - <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, como quando não houver luz suficiente"</string> + <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, se estiver escuro, por exemplo"</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o Desbloqueio facial"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 8573c20c8021..aa22aeafe3b0 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -673,8 +673,8 @@ <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"వేలిముద్ర చిహ్నం"</string> <string name="device_unlock_notification_name" msgid="2632928999862915709">"పరికర అన్లాక్"</string> <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"అన్లాక్ చేయడానికి మరొక మార్గాన్ని ట్రై చేయండి"</string> - <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"మీ వేలిముద్ర గుర్తించబడనప్పుడు, మీ వేళ్లు తడిగా ఉన్నప్పుడు ఫేస్ అన్లాక్ను ఉపయోగించండి"</string> - <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"మీ ఫేస్ గుర్తించబడనప్పుడు, తగినంత వెలుతురు లేనప్పుడు వేలిముద్ర అన్లాక్ను ఉపయోగించండి"</string> + <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"మీ వేళ్లు తడిగా ఉండటం లేక ఇతరత్రా కారణాల వల్ల మీ వేలిముద్రను గుర్తించకపోతే, ఫేస్ అన్లాక్ను ఉపయోగించండి"</string> + <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"తగినంత వెలుతురు లేకపోవడం లేక ఇతరత్రా కారణాల వల్ల మీ ఫేస్ గుర్తించబడనప్పుడు, వేలిముద్ర అన్లాక్ను ఉపయోగించండి"</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ఫేస్ అన్లాక్"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ఫేస్ అన్లాక్తో సమస్య"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ఫేస్ మోడల్ను తొలగించడానికి నొక్కండి, ఆపై మీ ముఖాన్ని మళ్లీ జోడించండి"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 936c978bea47..d34e9769dcd3 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -675,8 +675,8 @@ <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок відбитка пальця"</string> <string name="device_unlock_notification_name" msgid="2632928999862915709">"Розблокування пристрою"</string> <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Спробуйте інший спосіб розблокування"</string> - <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Розблоковуйте пристрій за допомогою фейс-контролю, коли не вдається розпізнати ваш відбиток пальця (наприклад, коли у вас мокрі пальці)"</string> - <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Розблоковуйте пристрій відбитком пальця, коли не вдається розпізнати ваше обличчя (наприклад, коли недостатньо світла)"</string> + <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Якщо пристрій не розпізнає ваш відбиток пальця (наприклад, коли у вас мокрі руки), використовуйте фейс-контроль"</string> + <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Якщо пристрій не розпізнає ваше обличчя (наприклад, коли освітлення погане), використовуйте відбиток пальця"</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Фейс-контроль"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Сталася помилка з фейсконтролем"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Натисніть, щоб видалити свою модель обличчя, а потім знову додайте її"</string> diff --git a/core/res/res/values-watch/dimens_material.xml b/core/res/res/values-watch/dimens_material.xml index 2ab2d91058e2..8becb0880f97 100644 --- a/core/res/res/values-watch/dimens_material.xml +++ b/core/res/res/values-watch/dimens_material.xml @@ -47,11 +47,12 @@ <dimen name="progress_bar_height">24dp</dimen> <!-- Progress bar message dimens --> - <dimen name="message_progress_dialog_text_size">18sp</dimen> + <dimen name="message_progress_dialog_text_size">14sp</dimen> <dimen name="message_progress_dialog_bottom_padding">80px</dimen> <dimen name="message_progress_dialog_top_padding">0dp</dimen> <dimen name="message_progress_dialog_start_padding">0dp</dimen> <dimen name="message_progress_dialog_end_padding">0dp</dimen> + <item name="message_progress_dialog_letter_spacing" format="float" type="dimen">0.021</item> <!-- fallback for screen percentage widths --> <dimen name="screen_percentage_05">0dp</dimen> diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml index 8698e86ba5f9..f3e412dc948e 100644 --- a/core/res/res/values-watch/styles_material.xml +++ b/core/res/res/values-watch/styles_material.xml @@ -100,7 +100,9 @@ please see styles_device_defaults.xml. <style name="ProgressDialogMessage"> <item name="android:textAlignment">center</item> - <item name="textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> + <item name="android:fontFamily">google-sans-text</item> + <item name="android:letterSpacing">@dimen/message_progress_dialog_letter_spacing</item> + <item name="textColor">?attr/textColorPrimary</item> <item name="textSize">@dimen/message_progress_dialog_text_size</item> <item name="paddingBottom">@dimen/message_progress_dialog_bottom_padding</item> <item name="paddingEnd">@dimen/message_progress_dialog_end_padding</item> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index ccdd945b8732..06ec1dd42267 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1891,7 +1891,7 @@ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{1 小時}other{# 小時}}"</string> <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{1 小時}other{# 小時}}"</string> <string name="zen_mode_until_next_day" msgid="1403042784161725038">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> - <string name="zen_mode_until" msgid="2250286190237669079">"完成時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> + <string name="zen_mode_until" msgid="2250286190237669079">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> <string name="zen_mode_alarm" msgid="7046911727540499275">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (下一次響鬧)"</string> <string name="zen_mode_forever" msgid="740585666364912448">"直至你關閉為止"</string> <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"直至你關閉「請勿騷擾」功能"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3c296de780a2..b211ac2fd316 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1026,6 +1026,12 @@ <!-- Duration, in milliseconds, of the display white balance animated transitions. --> <integer name="config_displayWhiteBalanceTransitionTime">3000</integer> + <!-- Duration, in milliseconds, of the display white balance animated transitions when increasing cct. --> + <integer name="config_displayWhiteBalanceTransitionTimeIncrease">1000</integer> + + <!-- Duration, in milliseconds, of the display white balance animated transitions when decreasing cct. --> + <integer name="config_displayWhiteBalanceTransitionTimeDecrease">40000</integer> + <!-- Device states where the sensor based rotation values should be reversed around the Z axis for the default display. TODO(b/265312193): Remove this workaround when this bug is fixed.--> @@ -1151,6 +1157,14 @@ <!-- Allows activities to be launched on a long press on power during device setup. --> <bool name="config_allowStartActivityForLongPressOnPowerInSetup">false</bool> + <!-- Control the behavior when the user short presses the settings button. + 0 - Nothing + 1 - Launch notification panel + This needs to match the constants in + com/android/server/policy/PhoneWindowManager.java + --> + <integer name="config_shortPressOnSettingsBehavior">0</integer> + <!-- Control the behavior when the user short presses the power button. 0 - Nothing 1 - Go to sleep (doze) @@ -5394,6 +5408,7 @@ <item>1,1,1.0,.4,1</item> <item>1,1,1.0,.15,15</item> <item>0,0,0.7,0,1</item> + <item>0,0,0.83333,0,1</item> </string-array> <!-- The integer index of the selected option in config_udfps_touch_detection_options --> diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml index 98a5ff9c4a79..bc9ca3decec3 100644 --- a/core/res/res/values/config_device_idle.xml +++ b/core/res/res/values/config_device_idle.xml @@ -42,6 +42,15 @@ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FACTOR --> <item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item> + <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_INCREASE_LINEARLY --> + <bool name="device_idle_light_idle_increase_linearly">false</bool> + + <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS --> + <integer name="device_idle_light_idle_linear_increase_factor_ms">300000</integer> + + <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS --> + <integer name="device_idle_light_idle_flex_linear_increase_factor_ms">60000</integer> + <!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT --> <integer name="device_idle_light_max_idle_to_ms">900000</integer> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 71d696ea4554..3ba150bcdd44 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -73,7 +73,7 @@ CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY. If 0, the device always switch to the higher score SIM. If < 0, the network type and signal strength based auto switch is disabled. --> - <integer name="auto_data_switch_score_tolerance">-1</integer> + <integer name="auto_data_switch_score_tolerance">4000</integer> <java-symbol type="integer" name="auto_data_switch_score_tolerance" /> <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec @@ -219,4 +219,8 @@ <bool name="telephony_analytics_switch">true</bool> <java-symbol type="bool" name="telephony_analytics_switch" /> + <!-- Whether to enable modem on boot if behavior is not defined --> + <bool name="config_enable_cellular_on_boot_default">true</bool> + <java-symbol type="bool" name="config_enable_cellular_on_boot_default" /> + </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 49295fd235ae..96c4bf432c05 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -349,6 +349,9 @@ <!-- the height of the expand button pill --> <dimen name="notification_expand_button_pill_height">24dp</dimen> + <!-- the padding of the expand icon in the notification header --> + <dimen name="notification_expand_button_icon_padding">2dp</dimen> + <!-- Vertical margin for the headerless notification content, when content has 1 line --> <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) --> <dimen name="notification_headerless_margin_oneline">16dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 193f3adeedea..49a5a7224850 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1816,6 +1816,7 @@ <java-symbol type="integer" name="config_lidNavigationAccessibility" /> <java-symbol type="integer" name="config_lidOpenRotation" /> <java-symbol type="integer" name="config_longPressOnHomeBehavior" /> + <java-symbol type="integer" name="config_shortPressOnSettingsBehavior" /> <java-symbol type="layout" name="global_actions" /> <java-symbol type="layout" name="global_actions_item" /> <java-symbol type="layout" name="global_actions_silent_mode" /> @@ -3485,6 +3486,8 @@ <java-symbol type="array" name="config_displayWhiteBalanceDisplaySteps" /> <java-symbol type="bool" name="config_displayWhiteBalanceLightModeAllowed" /> <java-symbol type="integer" name="config_displayWhiteBalanceTransitionTime" /> + <java-symbol type="integer" name="config_displayWhiteBalanceTransitionTimeIncrease" /> + <java-symbol type="integer" name="config_displayWhiteBalanceTransitionTimeDecrease" /> <!-- Device states where the sensor based rotation values should be reversed around the Z axis for the default display. @@ -4537,7 +4540,10 @@ <java-symbol type="integer" name="device_idle_light_idle_to_init_flex_ms" /> <java-symbol type="integer" name="device_idle_light_idle_to_max_flex_ms" /> <java-symbol type="integer" name="device_idle_light_idle_factor" /> + <java-symbol type="bool" name="device_idle_light_idle_increase_linearly" /> <java-symbol type="integer" name="device_idle_light_max_idle_to_ms" /> + <java-symbol type="integer" name="device_idle_light_idle_linear_increase_factor_ms" /> + <java-symbol type="integer" name="device_idle_light_idle_flex_linear_increase_factor_ms" /> <java-symbol type="integer" name="device_idle_light_idle_maintenance_min_budget_ms" /> <java-symbol type="integer" name="device_idle_light_idle_maintenance_max_budget_ms" /> <java-symbol type="integer" name="device_idle_min_light_maintenance_time_ms" /> diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp index 85d54e02d318..054d10c336e6 100644 --- a/core/tests/BroadcastRadioTests/Android.bp +++ b/core/tests/BroadcastRadioTests/Android.bp @@ -38,7 +38,7 @@ android_test { static_libs: [ "services.core", "androidx.test.rules", - "truth-prebuilt", + "truth", "testng", "mockito-target-extended", ], diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index a1952282dd0b..824f5910f96c 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -16,20 +16,20 @@ package com.android.server.broadcastradio.aidl; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.after; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.compat.CompatChanges; import android.graphics.Bitmap; @@ -81,8 +81,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { private static final int USER_ID_1 = 11; private static final int USER_ID_2 = 12; - private static final VerificationWithTimeout CALLBACK_TIMEOUT = - timeout(/* millis= */ 200); + private static final int CALLBACK_TIMEOUT_MS = 200; + private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS); private static final int SIGNAL_QUALITY = 90; private static final long AM_FM_FREQUENCY_SPACING = 500; private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100}; @@ -166,12 +166,12 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { @Before public void setup() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1); doReturn(true).when(() -> CompatChanges.isChangeEnabled( eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier(); + doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser()); - doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); mRadioModule = new RadioModule(mBroadcastRadioMock, AidlTestUtils.makeDefaultModuleProperties()); @@ -222,7 +222,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { return Result.OK; }).when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean()); - when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null); + doReturn(null).when(mBroadcastRadioMock).getImage(anyInt()); doAnswer(invocation -> { int configFlag = (int) invocation.getArguments()[0]; @@ -275,7 +275,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setConfiguration(FM_BAND_CONFIG); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onConfigurationChanged(FM_BAND_CONFIG); } @@ -446,26 +446,11 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].tune(initialSel); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(tuneInfo); } @Test - public void tune_forSystemUser() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM); - doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); - doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); - ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); - RadioManager.ProgramInfo tuneInfo = - AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); - openAidlClients(/* numClients= */ 1); - - mTunerSessions[0].tune(initialSel); - - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); - } - - @Test public void tune_withUnknownErrorFromHal_fails() throws Exception { openAidlClients(/* numClients= */ 1); ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); @@ -525,7 +510,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } @@ -604,7 +589,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(seekUpInfo); } @@ -638,6 +623,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { openAidlClients(/* numClients= */ 1); ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any()); doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); mTunerSessions[0].cancel(); @@ -686,8 +672,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { public void getImage_whenHalThrowsException_fails() throws Exception { openAidlClients(/* numClients= */ 1); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.getImage(anyInt())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock) + .getImage(anyInt()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getImage(/* id= */ 1); @@ -713,7 +699,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].startBackgroundScan(); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete(); + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) + .onBackgroundScanComplete(); } @Test @@ -905,7 +892,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>())); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) + .onProgramListUpdated(any()); } @Test @@ -1160,8 +1148,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1", "mockParam2", "mockValue2"); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.setParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock) + .setParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].setParameters(parametersSet); @@ -1186,8 +1174,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { openAidlClients(/* numClients= */ 1); List<String> parameterKeys = List.of("mockKey1", "mockKey2"); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.getParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock) + .getParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getParameters(parameterKeys); @@ -1198,7 +1186,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test - public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback() + public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback() throws Exception { openAidlClients(1); doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser()); @@ -1206,7 +1194,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo( AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY)); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java index fac9eaafe94c..3ec44d14b409 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java @@ -16,22 +16,22 @@ package com.android.server.broadcastradio.hal2; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.after; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.graphics.Bitmap; import android.hardware.broadcastradio.V2_0.Constants; @@ -78,8 +78,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { private static final int USER_ID_1 = 11; private static final int USER_ID_2 = 12; - private static final VerificationWithTimeout CALLBACK_TIMEOUT = - timeout(/* millis= */ 200); + private static final int CALLBACK_TIMEOUT_MS = 200; + private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS); private static final int SIGNAL_QUALITY = 1; private static final long AM_FM_FREQUENCY_SPACING = 500; private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100}; @@ -113,7 +113,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { @Before public void setup() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1); + doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier(); doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser()); @@ -170,7 +170,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { return Result.OK; }).when(mHalTunerSessionMock).scan(anyBoolean(), anyBoolean()); - when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0)); + doReturn(new ArrayList<Byte>(0)).when(mBroadcastRadioMock).getImage(anyInt()); doAnswer(invocation -> { int configFlag = (int) invocation.getArguments()[0]; @@ -227,7 +227,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setConfiguration(FM_BAND_CONFIG); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onConfigurationChanged(FM_BAND_CONFIG); } @@ -379,7 +379,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].tune(initialSel); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(tuneInfo); } @@ -398,20 +398,6 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void tune_forSystemUser() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM); - doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); - doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); - ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); - RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); - openAidlClients(/* numClients= */ 1); - - mTunerSessions[0].tune(initialSel); - - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); - } - - @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq); @@ -455,7 +441,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } @@ -533,7 +519,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(seekUpInfo); } @@ -563,22 +549,11 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void cancel_forNonCurrentUser() throws Exception { - openAidlClients(/* numClients= */ 1); - ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); - mTunerSessions[0].tune(initialSel); - doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); - - mTunerSessions[0].cancel(); - - verify(mHalTunerSessionMock, never()).cancel(); - } - - @Test public void cancel_forNonCurrentUser_doesNotCancel() throws Exception { openAidlClients(/* numClients= */ 1); ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any()); doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); mTunerSessions[0].cancel(); @@ -627,8 +602,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { public void getImage_whenHalThrowsException_fails() throws Exception { openAidlClients(/* numClients= */ 1); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.getImage(anyInt())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock).getImage(anyInt()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getImage(/* id= */ 1); @@ -654,7 +628,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].startBackgroundScan(); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete(); + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) + .onBackgroundScanComplete(); } @Test @@ -845,8 +820,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1", "mockParam2", "mockValue2"); String exceptionMessage = "HAL service died."; - when(mHalTunerSessionMock.setParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock) + .setParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].setParameters(parametersSet); @@ -871,8 +846,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { openAidlClients(/* numClients= */ 1); List<String> parameterKeys = List.of("mockKey1", "mockKey2"); String exceptionMessage = "HAL service died."; - when(mHalTunerSessionMock.getParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock) + .getParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getParameters(parameterKeys); @@ -883,7 +858,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback() + public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback() throws Exception { openAidlClients(1); doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser()); @@ -891,7 +866,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mHalTunerCallback.onCurrentProgramInfoChanged(TestUtils.makeHalProgramInfo( TestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY)); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp index 8c5d6d5a76a1..0e3bc6562edb 100644 --- a/core/tests/GameManagerTests/Android.bp +++ b/core/tests/GameManagerTests/Android.bp @@ -30,7 +30,7 @@ android_test { "frameworks-base-testutils", "junit", "platform-test-annotations", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp index 6f2366e32574..b631df1fcf57 100644 --- a/core/tests/PackageInstallerSessions/Android.bp +++ b/core/tests/PackageInstallerSessions/Android.bp @@ -35,7 +35,7 @@ android_test { "frameworks-base-testutils", "platform-test-annotations", "testng", - "truth-prebuilt", + "truth", ], libs: [ diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp index 52608351775c..1fb5f2c0789b 100644 --- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp +++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp @@ -18,7 +18,7 @@ android_test { "platform-test-annotations", "platformprotosnano", "statsdprotolite", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp index 2b34ee2646f3..7c1ac487bfdb 100644 --- a/core/tests/bugreports/Android.bp +++ b/core/tests/bugreports/Android.bp @@ -32,7 +32,7 @@ android_test { static_libs: [ "androidx.test.rules", "androidx.test.uiautomator_uiautomator", - "truth-prebuilt", + "truth", ], test_suites: ["general-tests"], sdk_version: "test_current", diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 9cde296b2cab..2993a0e63228 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -44,24 +44,27 @@ android_test { "frameworks-core-util-lib", "mockwebserver", "guava", + "android.view.accessibility.flags-aconfig-java", "androidx.core_core", "androidx.core_core-ktx", "androidx.test.espresso.core", "androidx.test.ext.junit", "androidx.test.runner", "androidx.test.rules", + "flag-junit", "junit-params", "kotlin-test", "mockito-target-minus-junit4", "androidx.test.uiautomator_uiautomator", "platform-test-annotations", "platform-compat-test-rules", - "truth-prebuilt", + "truth", "print-test-util-lib", "testng", "servicestests-utils", "device-time-shell-utils", "testables", + "com.android.text.flags-aconfig-java", ], libs: [ @@ -147,7 +150,7 @@ android_library { "androidx.test.runner", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: [ diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index a8a5059eea20..3f78396e3a70 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -16,6 +16,8 @@ package android.graphics; +import static com.android.text.flags.Flags.FLAG_CUSTOM_LOCALE_FALLBACK; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -30,6 +32,9 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.graphics.text.PositionedGlyphs; import android.graphics.text.TextRunShaper; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.text.FontConfig; import android.util.ArrayMap; @@ -39,6 +44,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParserException; @@ -107,6 +113,10 @@ public class TypefaceSystemFallbackTest { GLYPH_2EM_WIDTH = paint.measureText("a"); } + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + @Before public void setUp() { final AssetManager am = @@ -877,6 +887,130 @@ public class TypefaceSystemFallbackTest { assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); } + private static void assertA3emFontIsUsed(Typeface typeface) { + final Paint paint = new Paint(); + assertNotNull(typeface); + paint.setTypeface(typeface); + assertTrue("a3em font must be used", GLYPH_3EM_WIDTH == paint.measureText("a") + && GLYPH_1EM_WIDTH == paint.measureText("b") + && GLYPH_1EM_WIDTH == paint.measureText("c")); + } + + private static void assertB3emFontIsUsed(Typeface typeface) { + final Paint paint = new Paint(); + assertNotNull(typeface); + paint.setTypeface(typeface); + assertTrue("b3em font must be used", GLYPH_1EM_WIDTH == paint.measureText("a") + && GLYPH_3EM_WIDTH == paint.measureText("b") + && GLYPH_1EM_WIDTH == paint.measureText("c")); + } + + private static String getBaseXml(String font, String lang) { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='named-family'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family lang='%s'>" + + " <font weight='400' style='normal'>%s</font>" + + " </family>" + + "</familyset>"; + return String.format(xml, lang, font); + } + + private static String getCustomizationXml(String font, String op, String lang) { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<fonts-modification version='1'>" + + " <family customizationType='new-locale-family' operation='%s' lang='%s'>" + + " <font weight='400' style='normal' fallbackFor='named-family'>%s</font>" + + " </family>" + + "</fonts-modification>"; + return String.format(xml, op, lang, font); + } + + @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) + @Test + public void testBuildSystemFallback__Customization_locale_prepend() { + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback( + getBaseXml("a3em.ttf", "ja-JP"), + getCustomizationXml("b3em.ttf", "prepend", "ja-JP"), + fontMap, fallbackMap); + Typeface typeface = fontMap.get("named-family"); + + // operation "prepend" places font before the original font, thus b3em is used. + assertB3emFontIsUsed(typeface); + } + + @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) + @Test + public void testBuildSystemFallback__Customization_locale_replace() { + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback( + getBaseXml("a3em.ttf", "ja-JP"), + getCustomizationXml("b3em.ttf", "replace", "ja-JP"), + fontMap, fallbackMap); + Typeface typeface = fontMap.get("named-family"); + + // operation "replace" removes the original font, thus b3em font is used. + assertB3emFontIsUsed(typeface); + } + + @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) + @Test + public void testBuildSystemFallback__Customization_locale_append() { + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback( + getBaseXml("a3em.ttf", "ja-JP"), + getCustomizationXml("b3em.ttf", "append", "ja-JP"), + fontMap, fallbackMap); + Typeface typeface = fontMap.get("named-family"); + + // operation "append" comes next to the original font, so the original "a3em" is used. + assertA3emFontIsUsed(typeface); + } + + @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) + @Test + public void testBuildSystemFallback__Customization_locale_ScriptMismatch() { + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback( + getBaseXml("a3em.ttf", "ja-JP"), + getCustomizationXml("b3em.ttf", "replace", "ko-KR"), + fontMap, fallbackMap); + Typeface typeface = fontMap.get("named-family"); + + // Since the script doesn't match, the customization is ignored. + assertA3emFontIsUsed(typeface); + } + + @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK) + @Test + public void testBuildSystemFallback__Customization_locale_SubscriptMatch() { + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback( + getBaseXml("a3em.ttf", "ja-JP"), + getCustomizationXml("b3em.ttf", "replace", "ko-Hani-KR"), + fontMap, fallbackMap); + Typeface typeface = fontMap.get("named-family"); + + // Hani script is supported by Japanese, Jpan. + assertB3emFontIsUsed(typeface); + } + @Test(expected = IllegalArgumentException.class) public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() { final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" @@ -902,7 +1036,6 @@ public class TypefaceSystemFallbackTest { readFontCustomization(oemXml); } - @Test public void testBuildSystemFallback_UpdatableFont() { final String xml = "<?xml version='1.0' encoding='UTF-8'?>" diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java index 55ded9c8813d..517aeae53784 100644 --- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java +++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java @@ -16,6 +16,10 @@ package android.service.notification; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE; + import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM; import static junit.framework.Assert.assertEquals; @@ -24,20 +28,40 @@ import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.Mockito.spy; + +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.os.Bundle; import android.os.Parcel; +import android.os.SharedMemory; +import android.testing.TestableContext; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Flag; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver; import org.junit.After; +import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import java.util.ArrayList; +import java.util.List; + @SmallTest @RunWith(Parameterized.class) public class NotificationRankingUpdateTest { @@ -56,16 +80,368 @@ public class NotificationRankingUpdateTest { @Parameterized.Parameter public boolean mRankingUpdateAshmem; + @Rule + public TestableContext mContext = + spy(new TestableContext(InstrumentationRegistry.getContext(), null)); + + protected TestableContext getContext() { + return mContext; + } + + public static String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"}; + + /** + * Creates a NotificationRankingUpdate with prepopulated Ranking entries + * @param context A testable context, used for PendingIntent creation + * @return The NotificationRankingUpdate to be used as test data + */ + public static NotificationRankingUpdate generateUpdate(TestableContext context) { + NotificationListenerService.Ranking[] rankings = + new NotificationListenerService.Ranking[mKeys.length]; + for (int i = 0; i < mKeys.length; i++) { + final String key = mKeys[i]; + NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); + ranking.populate( + key, + i, + !isIntercepted(i), + getVisibilityOverride(i), + getSuppressedVisualEffects(i), + getImportance(i), + getExplanation(key), + getOverrideGroupKey(key), + getChannel(key, i), + getPeople(key, i), + getSnoozeCriteria(key, i), + getShowBadge(i), + getUserSentiment(i), + getHidden(i), + lastAudiblyAlerted(i), + getNoisy(i), + getSmartActions(key, i, context), + getSmartReplies(key, i), + canBubble(i), + isTextChanged(i), + isConversation(i), + getShortcutInfo(i), + getRankingAdjustment(i), + isBubble(i), + getProposedImportance(i), + hasSensitiveContent(i) + ); + rankings[i] = ranking; + } + return new NotificationRankingUpdate(rankings); + } + + /** + * Produces a visibility override value based on the provided index. + */ + public static int getVisibilityOverride(int index) { + return index * 9; + } + + /** + * Produces a group key based on the provided key. + */ + public static String getOverrideGroupKey(String key) { + return key + key; + } + + /** + * Produces a boolean that can be used to represent isIntercepted, based on the provided index. + */ + public static boolean isIntercepted(int index) { + return index % 2 == 0; + } + + /** + * Produces a suppressed visual effects value based on the provided index + */ + public static int getSuppressedVisualEffects(int index) { + return index * 2; + } + + /** + * Produces an importance value, based on the provided index + */ + public static int getImportance(int index) { + return index; + } + + /** + * Produces an explanation value, based on the provided key + */ + public static String getExplanation(String key) { + return key + "explain"; + } + + /** + * Produces a notification channel, based on the provided key and index + */ + public static NotificationChannel getChannel(String key, int index) { + return new NotificationChannel(key, key, getImportance(index)); + } + + /** + * Produces a boolean that can be used to represent showBadge, based on the provided index + */ + public static boolean getShowBadge(int index) { + return index % 3 == 0; + } + + /** + * Produces a user sentiment value, based on the provided index + */ + public static int getUserSentiment(int index) { + switch(index % 3) { + case 0: + return USER_SENTIMENT_NEGATIVE; + case 1: + return USER_SENTIMENT_NEUTRAL; + case 2: + return USER_SENTIMENT_POSITIVE; + } + return USER_SENTIMENT_NEUTRAL; + } + + /** + * Produces a boolean that can be used to represent "hidden," based on the provided index. + */ + public static boolean getHidden(int index) { + return index % 2 == 0; + } + + /** + * Produces a long to represent lastAudiblyAlerted based on the provided index. + */ + public static long lastAudiblyAlerted(int index) { + return index * 2000L; + } + + /** + * Produces a boolean that can be used to represent "noisy," based on the provided index. + */ + public static boolean getNoisy(int index) { + return index < 1; + } + + /** + * Produces strings that can be used to represent people, based on the provided key and index. + */ + public static ArrayList<String> getPeople(String key, int index) { + ArrayList<String> people = new ArrayList<>(); + for (int i = 0; i < index; i++) { + people.add(i + key); + } + return people; + } + + /** + * Produces a number of snoozeCriteria, based on the provided key and index. + */ + public static ArrayList<SnoozeCriterion> getSnoozeCriteria(String key, int index) { + ArrayList<SnoozeCriterion> snooze = new ArrayList<>(); + for (int i = 0; i < index; i++) { + snooze.add(new SnoozeCriterion(key + i, getExplanation(key), key)); + } + return snooze; + } + + /** + * Produces a list of Actions which can be used to represent smartActions. + * These actions are built from pending intents with intent titles based on the provided + * key, and ids based on the provided index. + */ + public static ArrayList<Notification.Action> getSmartActions(String key, + int index, + TestableContext context) { + ArrayList<Notification.Action> actions = new ArrayList<>(); + for (int i = 0; i < index; i++) { + PendingIntent intent = PendingIntent.getBroadcast( + context, + index /*requestCode*/, + new Intent("ACTION_" + key), + PendingIntent.FLAG_IMMUTABLE /*flags*/); + actions.add(new Notification.Action.Builder(null /*icon*/, key, intent).build()); + } + return actions; + } + + /** + * Produces index number of "smart replies," all based on the provided key and index + */ + public static ArrayList<CharSequence> getSmartReplies(String key, int index) { + ArrayList<CharSequence> choices = new ArrayList<>(); + for (int i = 0; i < index; i++) { + choices.add("choice_" + key + "_" + i); + } + return choices; + } + + /** + * Produces a boolean that can be used to represent canBubble, based on the provided index + */ + public static boolean canBubble(int index) { + return index % 4 == 0; + } + + /** + * Produces a boolean that can be used to represent isTextChanged, based on the provided index. + */ + public static boolean isTextChanged(int index) { + return index % 4 == 0; + } + + /** + * Produces a boolean that can be used to represent isConversation, based on the provided index. + */ + public static boolean isConversation(int index) { + return index % 4 == 0; + } + + /** + * Produces a ShortcutInfo value based on the provided index. + */ + public static ShortcutInfo getShortcutInfo(int index) { + ShortcutInfo si = new ShortcutInfo( + index, String.valueOf(index), "packageName", new ComponentName("1", "1"), null, + "title", 0, "titleResName", "text", 0, "textResName", + "disabledMessage", 0, "disabledMessageResName", + null, null, 0, null, 0, 0, + 0, "iconResName", "bitmapPath", null, 0, + null, null, null, null); + return si; + } + + /** + * Produces a rankingAdjustment value, based on the provided index. + */ + public static int getRankingAdjustment(int index) { + return index % 3 - 1; + } + + /** + * Produces a proposedImportance, based on the provided index. + */ + public static int getProposedImportance(int index) { + return index % 5 - 1; + } + + /** + * Produces a boolean that can be used to represent hasSensitiveContent, based on the provided + * index. + */ + public static boolean hasSensitiveContent(int index) { + return index % 3 == 0; + } + + /** + * Produces a boolean that can be used to represent isBubble, based on the provided index. + */ + public static boolean isBubble(int index) { + return index % 4 == 0; + } + + /** + * Checks that each of the pairs of actions in the two provided lists has identical titles, + * and that the lists have the same number of elements. + */ + public void assertActionsEqual( + List<Notification.Action> expecteds, List<Notification.Action> actuals) { + Assert.assertEquals(expecteds.size(), actuals.size()); + for (int i = 0; i < expecteds.size(); i++) { + Notification.Action expected = expecteds.get(i); + Notification.Action actual = actuals.get(i); + Assert.assertEquals(expected.title.toString(), actual.title.toString()); + } + } + + /** + * Checks that all subelements of the provided NotificationRankingUpdates are equal. + */ + public void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) { + detailedAssertEquals(a.getRankingMap(), b.getRankingMap()); + } + + /** + * Checks that all subelements of the provided Ranking objects are equal. + */ + public void detailedAssertEquals(String comment, NotificationListenerService.Ranking a, + NotificationListenerService.Ranking b) { + Assert.assertEquals(comment, a.getKey(), b.getKey()); + Assert.assertEquals(comment, a.getRank(), b.getRank()); + Assert.assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter()); + Assert.assertEquals(comment, a.getLockscreenVisibilityOverride(), + b.getLockscreenVisibilityOverride()); + Assert.assertEquals(comment, a.getSuppressedVisualEffects(), + b.getSuppressedVisualEffects()); + Assert.assertEquals(comment, a.getImportance(), b.getImportance()); + Assert.assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation()); + Assert.assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey()); + Assert.assertEquals(comment, a.getChannel().toString(), b.getChannel().toString()); + Assert.assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople()); + Assert.assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria()); + Assert.assertEquals(comment, a.canShowBadge(), b.canShowBadge()); + Assert.assertEquals(comment, a.getUserSentiment(), b.getUserSentiment()); + Assert.assertEquals(comment, a.isSuspended(), b.isSuspended()); + Assert.assertEquals(comment, a.getLastAudiblyAlertedMillis(), + b.getLastAudiblyAlertedMillis()); + Assert.assertEquals(comment, a.isNoisy(), b.isNoisy()); + Assert.assertEquals(comment, a.getSmartReplies(), b.getSmartReplies()); + Assert.assertEquals(comment, a.canBubble(), b.canBubble()); + Assert.assertEquals(comment, a.isConversation(), b.isConversation()); + if (a.getConversationShortcutInfo() != null && b.getConversationShortcutInfo() != null) { + Assert.assertEquals(comment, a.getConversationShortcutInfo().getId(), + b.getConversationShortcutInfo().getId()); + } else { + // One or both must be null, so we can check for equality. + Assert.assertEquals(a.getConversationShortcutInfo(), b.getConversationShortcutInfo()); + } + assertActionsEqual(a.getSmartActions(), b.getSmartActions()); + Assert.assertEquals(a.getProposedImportance(), b.getProposedImportance()); + Assert.assertEquals(a.hasSensitiveContent(), b.hasSensitiveContent()); + } + + /** + * Checks that the two RankingMaps have identical keys, and that each Ranking object for + * each of those keys is identical. + */ + public void detailedAssertEquals(NotificationListenerService.RankingMap a, + NotificationListenerService.RankingMap b) { + NotificationListenerService.Ranking arank = new NotificationListenerService.Ranking(); + NotificationListenerService.Ranking brank = new NotificationListenerService.Ranking(); + assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys()); + for (String key : a.getOrderedKeys()) { + a.getRanking(key, arank); + b.getRanking(key, brank); + detailedAssertEquals("ranking for key <" + key + ">", arank, brank); + } + } + @Before public void setUp() { mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT); - SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> { - if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) { - return mRankingUpdateAshmem; + SystemUiSystemPropertiesFlags.TEST_RESOLVER = new FlagResolver() { + @Override + public boolean isEnabled(Flag flag) { + if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) { + return mRankingUpdateAshmem; + } + return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag); + } + + @Override + public int getIntValue(Flag flag) { + return 0; + } + + @Override + public String getStringValue(Flag flag) { + return null; } - return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag); }; } @@ -74,8 +450,13 @@ public class NotificationRankingUpdateTest { SystemUiSystemPropertiesFlags.TEST_RESOLVER = null; } - public NotificationListenerService.Ranking createTestRanking(String key, int rank) { + /** + * Creates a mostly empty Test Ranking object with the specified key, rank, and smartActions. + */ + public NotificationListenerService.Ranking createEmptyTestRanking( + String key, int rank, ArrayList<Notification.Action> actions) { NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); + ranking.populate( /* key= */ key, /* rank= */ rank, @@ -93,7 +474,7 @@ public class NotificationRankingUpdateTest { /* hidden= */ false, /* lastAudiblyAlertedMs= */ -1, /* noisy= */ false, - /* smartActions= */ null, + /* smartActions= */ actions, /* smartReplies= */ null, /* canBubble= */ false, /* isTextChanged= */ false, @@ -107,54 +488,111 @@ public class NotificationRankingUpdateTest { return ranking; } + // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking. @Test - public void testRankingUpdate_rankingConstructor() { - NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); - NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( - new NotificationListenerService.Ranking[]{ranking}); + public void testRankingUpdate_parcel() { + NotificationRankingUpdate nru = generateUpdate(getContext()); + Parcel parcel = Parcel.obtain(); + nru.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel); + // The rankingUpdate file descriptor is only non-null in the new path. + if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( + SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { + assertTrue(nru1.isFdNotNullAndClosed()); + } + detailedAssertEquals(nru, nru1); + parcel.recycle(); + } - NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap(); - NotificationListenerService.Ranking retrievedRanking = - new NotificationListenerService.Ranking(); - assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); - assertEquals(123, retrievedRanking.getRank()); + // Tests parceling of RankingMap and RankingMap.equals + @Test + public void testRankingMap_parcel() { + NotificationListenerService.RankingMap rmap = generateUpdate(getContext()).getRankingMap(); + Parcel parcel = Parcel.obtain(); + rmap.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + NotificationListenerService.RankingMap rmap1 = + NotificationListenerService.RankingMap.CREATOR.createFromParcel(parcel); + + detailedAssertEquals(rmap, rmap1); + Assert.assertEquals(rmap, rmap1); + parcel.recycle(); } + // Tests parceling of Ranking and Ranking.equals @Test - public void testRankingUpdate_parcelConstructor() { - NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); - NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( - new NotificationListenerService.Ranking[]{ranking}); + public void testRanking_parcel() { + NotificationListenerService.Ranking ranking = + generateUpdate(getContext()).getRankingMap().getRawRankingObject(mKeys[0]); + Parcel parcel = Parcel.obtain(); + ranking.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + NotificationListenerService.Ranking ranking1 = + new NotificationListenerService.Ranking(parcel); + detailedAssertEquals("rankings differ: ", ranking, ranking1); + Assert.assertEquals(ranking, ranking1); + parcel.recycle(); + } - Parcel parceledRankingUpdate = Parcel.obtain(); - rankingUpdate.writeToParcel(parceledRankingUpdate, 0); - parceledRankingUpdate.setDataPosition(0); + // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking. + @Test + public void testRankingUpdate_equals_legacy() { + NotificationRankingUpdate nru = generateUpdate(getContext()); + NotificationRankingUpdate nru2 = generateUpdate(getContext()); + detailedAssertEquals(nru, nru2); + Assert.assertEquals(nru, nru2); + NotificationListenerService.Ranking tweak = + nru2.getRankingMap().getRawRankingObject(mKeys[0]); + tweak.populate( + tweak.getKey(), + tweak.getRank(), + !tweak.matchesInterruptionFilter(), // note the inversion here! + tweak.getLockscreenVisibilityOverride(), + tweak.getSuppressedVisualEffects(), + tweak.getImportance(), + tweak.getImportanceExplanation(), + tweak.getOverrideGroupKey(), + tweak.getChannel(), + (ArrayList) tweak.getAdditionalPeople(), + (ArrayList) tweak.getSnoozeCriteria(), + tweak.canShowBadge(), + tweak.getUserSentiment(), + tweak.isSuspended(), + tweak.getLastAudiblyAlertedMillis(), + tweak.isNoisy(), + (ArrayList) tweak.getSmartActions(), + (ArrayList) tweak.getSmartReplies(), + tweak.canBubble(), + tweak.isTextChanged(), + tweak.isConversation(), + tweak.getConversationShortcutInfo(), + tweak.getRankingAdjustment(), + tweak.isBubble(), + tweak.getProposedImportance(), + tweak.hasSensitiveContent() + ); + assertNotEquals(nru, nru2); + } - NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( - parceledRankingUpdate); + @Test + public void testRankingUpdate_rankingConstructor() { + NotificationRankingUpdate nru = generateUpdate(getContext()); + NotificationRankingUpdate constructedNru = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ + nru.getRankingMap().getRawRankingObject(mKeys[0]), + nru.getRankingMap().getRawRankingObject(mKeys[1]), + nru.getRankingMap().getRawRankingObject(mKeys[2]), + nru.getRankingMap().getRawRankingObject(mKeys[3]), + nru.getRankingMap().getRawRankingObject(mKeys[4]) + }); - NotificationListenerService.RankingMap retrievedRankings = - retrievedRankingUpdate.getRankingMap(); - assertNotNull(retrievedRankings); - // 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)); - assertEquals(123, retrievedRanking.getRank()); - assertTrue(retrievedRankingUpdate.equals(rankingUpdate)); - parceledRankingUpdate.recycle(); + detailedAssertEquals(nru, constructedNru); } @Test public void testRankingUpdate_emptyParcelInCheck() { - NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); - NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( - new NotificationListenerService.Ranking[]{ranking}); - + NotificationRankingUpdate rankingUpdate = generateUpdate(getContext()); Parcel parceledRankingUpdate = Parcel.obtain(); rankingUpdate.writeToParcel(parceledRankingUpdate, 0); @@ -163,37 +601,119 @@ public class NotificationRankingUpdateTest { NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( parceledRankingUpdate); assertNull(retrievedRankingUpdate.getRankingMap()); + parceledRankingUpdate.recycle(); } @Test public void testRankingUpdate_describeContents() { - NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); - NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( - new NotificationListenerService.Ranking[]{ranking}); + NotificationRankingUpdate rankingUpdate = generateUpdate(getContext()); assertEquals(0, rankingUpdate.describeContents()); } @Test public void testRankingUpdate_equals() { - NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); + NotificationListenerService.Ranking ranking = createEmptyTestRanking(TEST_KEY, 123, null); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); - // Reflexive equality. - assertTrue(rankingUpdate.equals(rankingUpdate)); - // Null or wrong class inequality. + // Reflexive equality, including handling nulls properly + detailedAssertEquals(rankingUpdate, rankingUpdate); + // Null or wrong class inequality assertFalse(rankingUpdate.equals(null)); assertFalse(rankingUpdate.equals(ranking)); - // Different ranking contents inequality. - NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456); + // Different rank inequality + NotificationListenerService.Ranking ranking2 = createEmptyTestRanking(TEST_KEY, 456, null); NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking2}); assertFalse(rankingUpdate.equals(rankingUpdate2)); - // Same ranking contents equality. - ranking2 = createTestRanking(TEST_KEY, 123); + // Different key inequality + ranking2 = createEmptyTestRanking(TEST_KEY + "DIFFERENT", 123, null); rankingUpdate2 = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking2}); - assertTrue(rankingUpdate.equals(rankingUpdate2)); + assertFalse(rankingUpdate.equals(rankingUpdate2)); + } + + @Test + public void testRankingUpdate_writesSmartActionToParcel() { + if (!mRankingUpdateAshmem) { + return; + } + ArrayList<Notification.Action> actions = new ArrayList<>(); + PendingIntent intent = PendingIntent.getBroadcast( + getContext(), + 0 /*requestCode*/, + new Intent("ACTION_" + TEST_KEY), + PendingIntent.FLAG_IMMUTABLE /*flags*/); + actions.add(new Notification.Action.Builder(null /*icon*/, TEST_KEY, intent).build()); + + NotificationListenerService.Ranking ranking = + createEmptyTestRanking(TEST_KEY, 123, actions); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + + Parcel parcel = Parcel.obtain(); + rankingUpdate.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + SharedMemory fd = parcel.readParcelable(getClass().getClassLoader(), SharedMemory.class); + Bundle smartActionsBundle = parcel.readBundle(getClass().getClassLoader()); + + // Assert the file descriptor is valid + assertNotNull(fd); + assertFalse(fd.getFd() == -1); + + // Assert that the smart action is in the parcel + assertNotNull(smartActionsBundle); + ArrayList<Notification.Action> recoveredActions = + smartActionsBundle.getParcelableArrayList(TEST_KEY, Notification.Action.class); + assertNotNull(recoveredActions); + assertEquals(actions.size(), recoveredActions.size()); + assertEquals(actions.get(0).title.toString(), recoveredActions.get(0).title.toString()); + parcel.recycle(); + } + + @Test + public void testRankingUpdate_handlesEmptySmartActionList() { + if (!mRankingUpdateAshmem) { + return; + } + ArrayList<Notification.Action> actions = new ArrayList<>(); + NotificationListenerService.Ranking ranking = + createEmptyTestRanking(TEST_KEY, 123, actions); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + + Parcel parcel = Parcel.obtain(); + rankingUpdate.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + // Ensure that despite an empty actions list, we can still unparcel the update. + NotificationRankingUpdate newRankingUpdate = new NotificationRankingUpdate(parcel); + assertNotNull(newRankingUpdate); + assertNotNull(newRankingUpdate.getRankingMap()); + detailedAssertEquals(rankingUpdate, newRankingUpdate); + parcel.recycle(); + } + + @Test + public void testRankingUpdate_handlesNullSmartActionList() { + if (!mRankingUpdateAshmem) { + return; + } + NotificationListenerService.Ranking ranking = + createEmptyTestRanking(TEST_KEY, 123, null); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + + Parcel parcel = Parcel.obtain(); + rankingUpdate.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + // Ensure that despite an empty actions list, we can still unparcel the update. + NotificationRankingUpdate newRankingUpdate = new NotificationRankingUpdate(parcel); + assertNotNull(newRankingUpdate); + assertNotNull(newRankingUpdate.getRankingMap()); + detailedAssertEquals(rankingUpdate, newRankingUpdate); + parcel.recycle(); } } diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 2afbb476bc16..6a9fc04230f8 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR; import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; @@ -38,12 +39,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; +import android.app.UiModeManager; import android.content.Context; import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; +import android.os.SystemProperties; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; +import android.util.Log; import android.view.WindowInsets.Side; import android.view.WindowInsets.Type; @@ -52,9 +59,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.compatibility.common.util.ShellIdentityUtils; + +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -71,6 +82,10 @@ import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidJUnit4.class) public class ViewRootImplTest { + private static final String TAG = "ViewRootImplTest"; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private ViewRootImpl mViewRootImpl; private volatile boolean mKeyReceived = false; @@ -101,6 +116,18 @@ public class ViewRootImplTest { mViewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify())); } + @After + public void teardown() { + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.Secure.resetToDefaults(sContext.getContentResolver(), TAG); + + var uiModeManager = sContext.getSystemService(UiModeManager.class); + uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); + + setForceDarkSysProp(false); + }); + } + @Test public void adjustLayoutParamsForCompatibility_layoutFullscreen() { final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); @@ -400,6 +427,100 @@ public class ViewRootImplTest { assertThat(result).isFalse(); } + @Test + public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.Secure.putInt( + sContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* value= */ 0 + ); + var uiModeManager = sContext.getSystemService(UiModeManager.class); + uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); + }); + + sInstrumentation.runOnMainSync(() -> + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); + + assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse(); + } + + @Test + public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.Secure.putInt( + sContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* value= */ 1 + ); + var uiModeManager = sContext.getSystemService(UiModeManager.class); + uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); + }); + + sInstrumentation.runOnMainSync(() -> + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); + + assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue(); + } + + @Test + public void forceInvertOffForceDarkOff_forceDarkModeDisabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.Secure.putInt( + sContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* value= */ 0 + ); + + // TODO(b/297556388): figure out how to set this without getting blocked by SELinux + assumeTrue(setForceDarkSysProp(true)); + }); + + sInstrumentation.runOnMainSync(() -> + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); + + assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse(); + } + + @Test + public void forceInvertOffForceDarkOn_forceDarkModeEnabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.Secure.putInt( + sContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* value= */ 0 + ); + + assumeTrue(setForceDarkSysProp(true)); + }); + + sInstrumentation.runOnMainSync(() -> + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); + + assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue(); + } + + private boolean setForceDarkSysProp(boolean isForceDarkEnabled) { + try { + SystemProperties.set( + ThreadedRenderer.DEBUG_FORCE_DARK, + Boolean.toString(isForceDarkEnabled) + ); + return true; + } catch (Exception e) { + Log.e(TAG, "Failed to set force_dark sysprop", e); + return false; + } + } + class KeyView extends View { KeyView(Context context) { super(context); diff --git a/core/tests/coretests/src/android/widget/DifferentialMotionFlingHelperTest.java b/core/tests/coretests/src/android/widget/DifferentialMotionFlingHelperTest.java index 51c8bc06e878..cce2faf71897 100644 --- a/core/tests/coretests/src/android/widget/DifferentialMotionFlingHelperTest.java +++ b/core/tests/coretests/src/android/widget/DifferentialMotionFlingHelperTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import android.view.MotionEvent; +import android.widget.flags.FakeFeatureFlagsImpl; +import android.widget.flags.Flags; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -54,6 +56,8 @@ public class DifferentialMotionFlingHelperTest { private final TestDifferentialMotionFlingTarget mFlingTarget = new TestDifferentialMotionFlingTarget(); + private final FakeFeatureFlagsImpl mFakeWidgetFeatureFlags = new FakeFeatureFlagsImpl(); + private DifferentialMotionFlingHelper mFlingHelper; @Before @@ -62,7 +66,10 @@ public class DifferentialMotionFlingHelperTest { ApplicationProvider.getApplicationContext(), mFlingTarget, mVelocityThresholdCalculator, - mVelocityProvider); + mVelocityProvider, + mFakeWidgetFeatureFlags); + mFakeWidgetFeatureFlags.setFlag( + Flags.FLAG_ENABLE_PLATFORM_WIDGET_DIFFERENTIAL_MOTION_FLING, true); } @Test @@ -139,6 +146,18 @@ public class DifferentialMotionFlingHelperTest { } @Test + public void flingFeatureFlagDisabled_noFlingCalculation() { + mFakeWidgetFeatureFlags.setFlag( + Flags.FLAG_ENABLE_PLATFORM_WIDGET_DIFFERENTIAL_MOTION_FLING, false); + mMinVelocity = 50; + mMaxVelocity = 100; + deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, 60); + + assertFalse(mVelocityCalculated); + assertEquals(0, mFlingTarget.mLastFlingVelocity, /* delta= */ 0); + } + + @Test public void negativeFlingVelocityAboveMaximum_velocityClamped() { mMinVelocity = 50; mMaxVelocity = 100; diff --git a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java new file mode 100644 index 000000000000..263e563bc224 --- /dev/null +++ b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java @@ -0,0 +1,429 @@ +/* + * 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.window; + +import static android.os.PerformanceHintManager.Session.CPU_LOAD_RESET; +import static android.os.PerformanceHintManager.Session.CPU_LOAD_UP; +import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT; +import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH; +import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN; +import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF; +import static android.window.SystemPerformanceHinter.HINT_ADPF; +import static android.window.SystemPerformanceHinter.HINT_ALL; +import static android.window.SystemPerformanceHinter.HINT_SF_EARLY_WAKEUP; +import static android.window.SystemPerformanceHinter.HINT_SF_FRAME_RATE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; + +import android.os.PerformanceHintManager; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; + +import androidx.annotation.NonNull; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashMap; + +/** + * Class for testing {@link android.window.SystemPerformanceHinter}. + * + * Build/Install/Run: + * atest FrameworksCoreTests:android.window.SystemPerformanceHinterTests + */ +@Presubmit +@RunWith(AndroidJUnit4.class) +public class SystemPerformanceHinterTests { + + private static final int DEFAULT_DISPLAY_ID = android.view.Display.DEFAULT_DISPLAY; + private static final int SECONDARY_DISPLAY_ID = DEFAULT_DISPLAY_ID + 1; + private static final int NO_ROOT_DISPLAY_ID = DEFAULT_DISPLAY_ID + 2; + private static final String TEST_REASON = "test"; + private static final String TEST_OTHER_REASON = "test_other"; + + private SystemPerformanceHinter mHinter; + private SystemPerformanceHinterTests.RootProvider mRootProvider; + + @Mock + private PerformanceHintManager.Session mAdpfSession; + + @Mock + private SurfaceControl.Transaction mTransaction; + private SurfaceControl mDefaultDisplayRoot; + private SurfaceControl mSecondaryDisplayRoot; + + + @Before + public void setUpOnce() { + MockitoAnnotations.initMocks(this); + + mDefaultDisplayRoot = new SurfaceControl(); + mSecondaryDisplayRoot = new SurfaceControl(); + mRootProvider = new SystemPerformanceHinterTests.RootProvider(); + mRootProvider.put(DEFAULT_DISPLAY_ID, mDefaultDisplayRoot); + mRootProvider.put(SECONDARY_DISPLAY_ID, mSecondaryDisplayRoot); + + mHinter = new SystemPerformanceHinter( + InstrumentationRegistry.getInstrumentation().getTargetContext(), + mRootProvider, + () -> mTransaction); + } + + @Test + public void testADPFHintWithoutADPFSession_expectThrows() { + assertThrows("Expected exception without ADPF session", + IllegalArgumentException.class, () -> { + mHinter.startSession(HINT_ADPF, DEFAULT_DISPLAY_ID, TEST_REASON); + }); + } + + @Test + public void testSFVRRHintWithoutDisplayRootProvider_expectThrows() { + assertThrows("Expected exception without display root", + IllegalArgumentException.class, () -> { + SystemPerformanceHinter hinter = new SystemPerformanceHinter( + InstrumentationRegistry.getInstrumentation().getTargetContext(), + null /* displayRootProvider */, + () -> mTransaction); + hinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON); + }); + } + + @Test + public void testGetDefaultDisplayRoot() { + mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON); + assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId); + assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot); + } + + @Test + public void testGetSecondaryDisplayRoot() { + mHinter.startSession(HINT_SF_FRAME_RATE, SECONDARY_DISPLAY_ID, TEST_REASON); + assertEquals(SECONDARY_DISPLAY_ID, mRootProvider.lastRequestedDisplayId); + assertEquals(mSecondaryDisplayRoot, mRootProvider.lastReturnedRoot); + } + + @Test + public void testOnlyCacheDisplayRoots() { + mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON); + mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON); + mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON); + assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId); + assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot); + } + + @Test + public void testVRRHint() { + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON); + + // Expect it to get a display root + assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId); + assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot); + + // Verify we call SF + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_HIGH)); + verify(mTransaction).applyAsyncUnsafe(); + } + + @Test + public void testVRRHintCloseSession() { + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_SF_FRAME_RATE, DEFAULT_DISPLAY_ID, TEST_REASON); + reset(mTransaction); + session.close(); + + // Verify we call SF + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_SELF)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_DEFAULT)); + verify(mTransaction).applyAsyncUnsafe(); + } + + @Test + public void testEarlyWakeupHint() { + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_SF_EARLY_WAKEUP, DEFAULT_DISPLAY_ID, TEST_REASON); + + // Expect that this hint does not require a display root + assertEquals(0, mRootProvider.getCount); + + // Verify we call SF + verify(mTransaction).setEarlyWakeupStart(); + verify(mTransaction).applyAsyncUnsafe(); + } + + @Test + public void testEarlyWakeupHintCloseSession() { + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_SF_EARLY_WAKEUP, DEFAULT_DISPLAY_ID, TEST_REASON); + reset(mTransaction); + session.close(); + + // Verify we call SF + verify(mTransaction).setEarlyWakeupEnd(); + verify(mTransaction).applyAsyncUnsafe(); + } + + @Test + public void testADPFHint() { + mHinter.setAdpfSession(mAdpfSession); + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_ADPF, DEFAULT_DISPLAY_ID, TEST_REASON); + + // Expect that this hint does not require a display root + assertEquals(0, mRootProvider.getCount); + + // Verify we call the perf manager + verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP)); + } + + @Test + public void testADPFHintCloseSession() { + mHinter.setAdpfSession(mAdpfSession); + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_ADPF, DEFAULT_DISPLAY_ID, TEST_REASON); + reset(mTransaction); + session.close(); + + // Verify we call the perf manager + verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET)); + } + + @Test + public void testAllHints() { + mHinter.setAdpfSession(mAdpfSession); + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON); + + // Expect it to get a display root + assertEquals(DEFAULT_DISPLAY_ID, mRootProvider.lastRequestedDisplayId); + assertEquals(mDefaultDisplayRoot, mRootProvider.lastReturnedRoot); + + // Verify we call SF and perf manager + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_HIGH)); + verify(mTransaction).setEarlyWakeupStart(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP)); + } + + @Test + public void testAllHintsCloseSession() { + mHinter.setAdpfSession(mAdpfSession); + final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON); + reset(mTransaction); + session.close(); + + // Verify we call SF and perf manager to clean up + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_SELF)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_DEFAULT)); + verify(mTransaction).setEarlyWakeupEnd(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET)); + } + + @Test + public void testAutocloseable() { + mHinter.setAdpfSession(mAdpfSession); + try (final SystemPerformanceHinter.HighPerfSession session = + mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON)) { + reset(mTransaction); + reset(mAdpfSession); + } finally { + // Verify we call SF and perf manager to clean up + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_SELF)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_DEFAULT)); + verify(mTransaction).setEarlyWakeupEnd(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET)); + } + } + + @Test + public void testOverlappingHintsOnSameDisplay() { + mHinter.setAdpfSession(mAdpfSession); + final SystemPerformanceHinter.HighPerfSession session1 = + mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON); + // Verify we call SF and perf manager + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_HIGH)); + verify(mTransaction).setEarlyWakeupStart(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP)); + reset(mTransaction); + reset(mAdpfSession); + + final SystemPerformanceHinter.HighPerfSession session2 = + mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_OTHER_REASON); + // Verify we never call SF and perf manager since session1 is already running + verify(mTransaction, never()).setFrameRateSelectionStrategy(any(), anyInt()); + verify(mTransaction, never()).setFrameRateCategory(any(), anyInt()); + verify(mTransaction, never()).setEarlyWakeupEnd(); + verify(mTransaction, never()).applyAsyncUnsafe(); + verify(mAdpfSession, never()).sendHint(anyInt()); + + session2.close(); + // Verify we have not cleaned up because session1 is still running + verify(mTransaction, never()).setFrameRateSelectionStrategy(any(), anyInt()); + verify(mTransaction, never()).setFrameRateCategory(any(), anyInt()); + verify(mTransaction, never()).setEarlyWakeupEnd(); + verify(mTransaction, never()).applyAsyncUnsafe(); + verify(mAdpfSession, never()).sendHint(anyInt()); + + session1.close(); + // Verify we call SF and perf manager to clean up + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_SELF)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_DEFAULT)); + verify(mTransaction).setEarlyWakeupEnd(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET)); + } + + @Test + public void testOverlappingHintsOnDifferentDisplays() { + mHinter.setAdpfSession(mAdpfSession); + final SystemPerformanceHinter.HighPerfSession session1 = + mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_REASON); + + // Verify we call SF and perf manager + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_HIGH)); + verify(mTransaction).setEarlyWakeupStart(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP)); + reset(mTransaction); + reset(mAdpfSession); + + // Create a new session and ensure only per-display flags are updated and not global flags + final SystemPerformanceHinter.HighPerfSession session2 = + mHinter.startSession(HINT_ALL, SECONDARY_DISPLAY_ID, TEST_REASON); + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mSecondaryDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN)); + verify(mTransaction).setFrameRateCategory( + eq(mSecondaryDisplayRoot), + eq(FRAME_RATE_CATEGORY_HIGH)); + verify(mTransaction, never()).setEarlyWakeupStart(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession, never()).sendHint(anyInt()); + reset(mTransaction); + reset(mAdpfSession); + + // Close the primary display session and ensure it doesn't affect secondary display flags + // or any global flags still requested by the secondary display session + session1.close(); + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_SELF)); + verify(mTransaction).setFrameRateCategory( + eq(mDefaultDisplayRoot), + eq(FRAME_RATE_CATEGORY_DEFAULT)); + verify(mTransaction, never()).setFrameRateSelectionStrategy( + eq(mSecondaryDisplayRoot), + anyInt()); + verify(mTransaction, never()).setFrameRateCategory( + eq(mSecondaryDisplayRoot), + anyInt()); + verify(mTransaction, never()).setEarlyWakeupEnd(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession, never()).sendHint(anyInt()); + reset(mTransaction); + reset(mAdpfSession); + + // Close all sessions, ensure it cleans up all the flags + session2.close(); + verify(mTransaction, never()).setFrameRateSelectionStrategy( + eq(mDefaultDisplayRoot), + anyInt()); + verify(mTransaction).setFrameRateSelectionStrategy( + eq(mSecondaryDisplayRoot), + eq(FRAME_RATE_SELECTION_STRATEGY_SELF)); + verify(mTransaction).setFrameRateCategory( + eq(mSecondaryDisplayRoot), + eq(FRAME_RATE_CATEGORY_DEFAULT)); + verify(mTransaction).setEarlyWakeupEnd(); + verify(mTransaction).applyAsyncUnsafe(); + verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET)); + } + + private class RootProvider implements SystemPerformanceHinter.DisplayRootProvider { + private HashMap<Integer, SurfaceControl> mRoots = new HashMap<>(); + public int getCount; + public int lastRequestedDisplayId = -1; + public SurfaceControl lastReturnedRoot; + + void put(int displayId, SurfaceControl root) { + mRoots.put(displayId, root); + } + + @NonNull + @Override + public SurfaceControl getRootForDisplay(int displayId) { + getCount++; + lastRequestedDisplayId = displayId; + lastReturnedRoot = mRoots.get(displayId); + return lastReturnedRoot; + } + } +} diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp index 3d04937c9195..5f6eaf96a846 100644 --- a/core/tests/hdmitests/Android.bp +++ b/core/tests/hdmitests/Android.bp @@ -30,7 +30,7 @@ android_test { "frameworks-base-testutils", "guava-android-testlib", "platform-test-annotations", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp index fde7c08715c7..2d778b1218d2 100644 --- a/core/tests/mockingcoretests/Android.bp +++ b/core/tests/mockingcoretests/Android.bp @@ -38,7 +38,7 @@ android_test { "androidx.test.ext.junit", "mockito-target-extended-minus-junit4", "platform-test-annotations", - "truth-prebuilt", + "truth", "testables", ], diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp index c74600bbab22..f81be494ad18 100644 --- a/core/tests/nfctests/Android.bp +++ b/core/tests/nfctests/Android.bp @@ -27,7 +27,7 @@ android_test { "androidx.test.ext.junit", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: [ "android.test.runner", diff --git a/core/tests/overlaytests/device_self_targeting/Android.bp b/core/tests/overlaytests/device_self_targeting/Android.bp index 063c5694ab5b..931eac515e31 100644 --- a/core/tests/overlaytests/device_self_targeting/Android.bp +++ b/core/tests/overlaytests/device_self_targeting/Android.bp @@ -30,7 +30,7 @@ android_test { "androidx.test.runner", "androidx.test.ext.junit", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], optimize: { diff --git a/core/tests/packagemonitortests/Android.bp b/core/tests/packagemonitortests/Android.bp index 7b5d7dff0a85..b08850e90d28 100644 --- a/core/tests/packagemonitortests/Android.bp +++ b/core/tests/packagemonitortests/Android.bp @@ -32,7 +32,7 @@ android_test { "compatibility-device-util-axt", "frameworks-base-testutils", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp index bc3dd810be8c..4e24cd5d91cb 100644 --- a/core/tests/privacytests/Android.bp +++ b/core/tests/privacytests/Android.bp @@ -14,7 +14,7 @@ android_test { "junit", "rappor-tests", "androidx.test.rules", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp index 3798da592cd5..580e73c331a1 100644 --- a/core/tests/utiltests/Android.bp +++ b/core/tests/utiltests/Android.bp @@ -33,7 +33,7 @@ android_test { "frameworks-base-testutils", "mockito-target-minus-junit4", "androidx.test.ext.junit", - "truth-prebuilt", + "truth", "servicestests-utils", ], diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp index 829409a36986..09608e9bf507 100644 --- a/core/tests/vibrator/Android.bp +++ b/core/tests/vibrator/Android.bp @@ -18,7 +18,7 @@ android_test { "androidx.test.runner", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", "testng", ], diff --git a/errorprone/Android.bp b/errorprone/Android.bp index ad08026622d1..c1d2235e3324 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -41,7 +41,7 @@ java_test_host { java_resource_dirs: ["tests/res"], java_resources: [":error_prone_android_framework_testdata"], static_libs: [ - "truth-prebuilt", + "truth", "kxml2-2.3.0", "compile-testing-prebuilt", "error_prone_android_framework_lib", diff --git a/framework-minus-apex-ravenwood-policies.txt b/framework-minus-apex-ravenwood-policies.txt new file mode 100644 index 000000000000..6bac58bf8ed7 --- /dev/null +++ b/framework-minus-apex-ravenwood-policies.txt @@ -0,0 +1 @@ +# Ravenwood "policy" file for framework-minus-apex. diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index e7814cbd67e7..d1aceafc4e2c 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -114,6 +114,7 @@ public class Canvas extends BaseCanvas { throw new IllegalStateException("Immutable bitmap passed to Canvas constructor"); } throwIfCannotDraw(bitmap); + bitmap.setGainmap(null); mNativeCanvasWrapper = nInitRaster(bitmap.getNativeInstance()); mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation( this, mNativeCanvasWrapper); @@ -178,7 +179,7 @@ public class Canvas extends BaseCanvas { throw new IllegalStateException(); } throwIfCannotDraw(bitmap); - + bitmap.setGainmap(null); nSetBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance()); mDensity = bitmap.mDensity; } diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 735bc180c015..52b0b95d3e76 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -236,7 +236,9 @@ public class FontListParser { } } - return new FontConfig(families, filtered, resultNamedFamilies, lastModifiedDate, + return new FontConfig(families, filtered, resultNamedFamilies, + customization.getLocaleFamilyCustomizations(), + lastModifiedDate, configVersion); } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 9d32272755c0..db1cc446b2d6 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -16,8 +16,11 @@ package android.graphics; +import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; + import android.annotation.ColorInt; import android.annotation.ColorLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -2125,7 +2128,7 @@ public class Paint { * @return the font's recommended interline spacing. */ public float getFontMetrics(FontMetrics metrics) { - return nGetFontMetrics(mNativePaint, metrics); + return nGetFontMetrics(mNativePaint, metrics, false); } /** @@ -2139,6 +2142,32 @@ public class Paint { } /** + * Get the font metrics used for the locale + * + * Obtain the metrics of the font that is used for the specified locale by + * {@link #setTextLocales(LocaleList)}. If multiple locales are specified, the minimum ascent + * and maximum descent will be set. + * + * This API is useful for determining the default line height of the empty text field, e.g. + * {@link android.widget.EditText}. + * + * Note, if the {@link android.graphics.Typeface} is created from the custom font files, its + * metrics are reserved, i.e. the ascent will be the custom font's ascent or smaller, the + * descent will be the custom font's descent or larger. + * + * Note, if the {@link android.graphics.Typeface} is a system fallback, e.g. + * {@link android.graphics.Typeface#SERIF}, the default font's metrics are reserved, i.e. the + * ascent will be the serif font's ascent or smaller, the descent will be the serif font's + * descent or larger. + * + * @param metrics an output parameter. All members will be set by calling this function. + */ + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public void getFontMetricsForLocale(@NonNull FontMetrics metrics) { + nGetFontMetrics(mNativePaint, metrics, true); + } + + /** * Returns the font metrics value for the given text. * * If the text is rendered with multiple font files, this function returns the large ascent and @@ -2318,7 +2347,7 @@ public class Paint { * @return the font's interline spacing. */ public int getFontMetricsInt(FontMetricsInt fmi) { - return nGetFontMetricsInt(mNativePaint, fmi); + return nGetFontMetricsInt(mNativePaint, fmi, false); } public FontMetricsInt getFontMetricsInt() { @@ -2328,6 +2357,32 @@ public class Paint { } /** + * Get the font metrics used for the locale + * + * Obtain the metrics of the font that is used for the specified locale by + * {@link #setTextLocales(LocaleList)}. If multiple locales are specified, the minimum ascent + * and maximum descent will be set. + * + * This API is useful for determining the default line height of the empty text field, e.g. + * {@link android.widget.EditText}. + * + * Note, if the {@link android.graphics.Typeface} is created from the custom font files, its + * metrics are reserved, i.e. the ascent will be the custom font's ascent or smaller, the + * descent will be the custom font's descent or larger. + * + * Note, if the {@link android.graphics.Typeface} is a system fallback, e.g. + * {@link android.graphics.Typeface#SERIF}, the default font's metrics are reserved, i.e. the + * ascent will be the serif font's ascent or smaller, the descent will be the serif font's + * descent or larger. + * + * @param metrics an output parameter. All members will be set by calling this function. + */ + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public void getFontMetricsIntForLocale(@NonNull FontMetricsInt metrics) { + nGetFontMetricsInt(mNativePaint, metrics, true); + } + + /** * Return the recommend line spacing based on the current typeface and * text size. * @@ -3446,9 +3501,11 @@ public class Paint { @FastNative private static native void nSetFontFeatureSettings(long paintPtr, String settings); @FastNative - private static native float nGetFontMetrics(long paintPtr, FontMetrics metrics); + private static native float nGetFontMetrics(long paintPtr, FontMetrics metrics, + boolean useLocale); @FastNative - private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi); + private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi, + boolean useLocale); // ---------------- @CriticalNative ------------------------ diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index b458dd9021d0..6e04a2f5e405 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -22,6 +22,7 @@ import static android.text.FontConfig.NamedFamilyList; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; +import android.text.FontConfig; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; @@ -34,6 +35,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; /** @@ -52,14 +54,19 @@ public class FontCustomizationParser { private final List<Alias> mAdditionalAliases; + private final List<FontConfig.Customization.LocaleFallback> mLocaleFamilyCustomizations; + public Result() { mAdditionalNamedFamilies = Collections.emptyMap(); + mLocaleFamilyCustomizations = Collections.emptyList(); mAdditionalAliases = Collections.emptyList(); } public Result(Map<String, NamedFamilyList> additionalNamedFamilies, + List<FontConfig.Customization.LocaleFallback> localeFamilyCustomizations, List<Alias> additionalAliases) { mAdditionalNamedFamilies = additionalNamedFamilies; + mLocaleFamilyCustomizations = localeFamilyCustomizations; mAdditionalAliases = additionalAliases; } @@ -70,6 +77,10 @@ public class FontCustomizationParser { public List<Alias> getAdditionalAliases() { return mAdditionalAliases; } + + public List<FontConfig.Customization.LocaleFallback> getLocaleFamilyCustomizations() { + return mLocaleFamilyCustomizations; + } } /** @@ -89,7 +100,9 @@ public class FontCustomizationParser { } private static Result validateAndTransformToResult( - List<NamedFamilyList> families, List<Alias> aliases) { + List<NamedFamilyList> families, + List<FontConfig.Customization.LocaleFallback> outLocaleFamilies, + List<Alias> aliases) { HashMap<String, NamedFamilyList> namedFamily = new HashMap<>(); for (int i = 0; i < families.size(); ++i) { final NamedFamilyList family = families.get(i); @@ -105,7 +118,7 @@ public class FontCustomizationParser { + "requires fallackTarget attribute"); } } - return new Result(namedFamily, aliases); + return new Result(namedFamily, outLocaleFamilies, aliases); } private static Result readFamilies( @@ -115,12 +128,13 @@ public class FontCustomizationParser { ) throws XmlPullParserException, IOException { List<NamedFamilyList> families = new ArrayList<>(); List<Alias> aliases = new ArrayList<>(); + List<FontConfig.Customization.LocaleFallback> outLocaleFamilies = new ArrayList<>(); parser.require(XmlPullParser.START_TAG, null, "fonts-modification"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - readFamily(parser, fontDir, families, updatableFontMap); + readFamily(parser, fontDir, families, outLocaleFamilies, updatableFontMap); } else if (tag.equals("family-list")) { readFamilyList(parser, fontDir, families, updatableFontMap); } else if (tag.equals("alias")) { @@ -129,13 +143,14 @@ public class FontCustomizationParser { FontListParser.skip(parser); } } - return validateAndTransformToResult(families, aliases); + return validateAndTransformToResult(families, outLocaleFamilies, aliases); } private static void readFamily( @NonNull XmlPullParser parser, @NonNull String fontDir, @NonNull List<NamedFamilyList> out, + @NonNull List<FontConfig.Customization.LocaleFallback> outCustomization, @Nullable Map<String, File> updatableFontMap) throws XmlPullParserException, IOException { final String customizationType = parser.getAttributeValue(null, "customizationType"); @@ -148,6 +163,29 @@ public class FontCustomizationParser { if (fontFamily != null) { out.add(fontFamily); } + } else if (customizationType.equals("new-locale-family")) { + final String lang = parser.getAttributeValue(null, "lang"); + final String op = parser.getAttributeValue(null, "operation"); + final int intOp; + if (op.equals("append")) { + intOp = FontConfig.Customization.LocaleFallback.OPERATION_APPEND; + } else if (op.equals("prepend")) { + intOp = FontConfig.Customization.LocaleFallback.OPERATION_PREPEND; + } else if (op.equals("replace")) { + intOp = FontConfig.Customization.LocaleFallback.OPERATION_REPLACE; + } else { + throw new IllegalArgumentException("Unknown operation=" + op); + } + + final FontConfig.FontFamily family = FontListParser.readFamily( + parser, fontDir, updatableFontMap, false); + + // For ignoring the customization, consume the new-locale-family element but don't + // register any customizations. + if (com.android.text.flags.Flags.customLocaleFallback()) { + outCustomization.add(new FontConfig.Customization.LocaleFallback( + Locale.forLanguageTag(lang), intOp, family)); + } } else { throw new IllegalArgumentException("Unknown customizationType=" + customizationType); } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index d4e35b30c8d0..618aa5b5019c 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -16,10 +16,15 @@ package android.graphics.fonts; +import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_APPEND; +import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_PREPEND; +import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_REPLACE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; import android.graphics.Typeface; +import android.os.LocaleList; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; @@ -38,6 +43,7 @@ import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -119,7 +125,6 @@ public final class SystemFonts { } } - final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( defaultFonts, languageTags, variant, xmlFamily.getVariableFontFamilyType(), false, cache); @@ -300,11 +305,11 @@ public final class SystemFonts { } catch (IOException e) { Log.e(TAG, "Failed to open/read system font configurations.", e); return new FontConfig(Collections.emptyList(), Collections.emptyList(), - Collections.emptyList(), 0, 0); + Collections.emptyList(), Collections.emptyList(), 0, 0); } catch (XmlPullParserException e) { Log.e(TAG, "Failed to parse the system font configuration.", e); return new FontConfig(Collections.emptyList(), Collections.emptyList(), - Collections.emptyList(), 0, 0); + Collections.emptyList(), Collections.emptyList(), 0, 0); } } @@ -328,6 +333,8 @@ public final class SystemFonts { ArrayMap<String, ByteBuffer> outBufferCache) { final ArrayMap<String, NativeFamilyListSet> fallbackListMap = new ArrayMap<>(); + final List<FontConfig.Customization.LocaleFallback> localeFallbacks = + fontConfig.getLocaleFallbackCustomizations(); final List<FontConfig.NamedFamilyList> namedFamilies = fontConfig.getNamedFamilyLists(); for (int i = 0; i < namedFamilies.size(); ++i) { @@ -336,10 +343,54 @@ public final class SystemFonts { } // Then, add fallback fonts to the fallback map. + final List<FontConfig.Customization.LocaleFallback> customizations = new ArrayList<>(); final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies(); + final SparseIntArray seenCustomization = new SparseIntArray(); for (int i = 0; i < xmlFamilies.size(); i++) { final FontConfig.FontFamily xmlFamily = xmlFamilies.get(i); - pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache); + + customizations.clear(); + for (int j = 0; j < localeFallbacks.size(); ++j) { + if (seenCustomization.get(j, -1) != -1) { + continue; // The customization is already applied. + } + FontConfig.Customization.LocaleFallback localeFallback = localeFallbacks.get(j); + if (scriptMatch(xmlFamily.getLocaleList(), localeFallback.getScript())) { + customizations.add(localeFallback); + seenCustomization.put(j, 1); + } + } + + if (customizations.isEmpty()) { + pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache); + } else { + for (int j = 0; j < customizations.size(); ++j) { + FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j); + if (localeFallback.getOperation() == OPERATION_PREPEND) { + pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap, + outBufferCache); + } + } + boolean isReplaced = false; + for (int j = 0; j < customizations.size(); ++j) { + FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j); + if (localeFallback.getOperation() == OPERATION_REPLACE) { + pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap, + outBufferCache); + isReplaced = true; + } + } + if (!isReplaced) { // If nothing is replaced, push the original one. + pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache); + } + for (int j = 0; j < customizations.size(); ++j) { + FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j); + if (localeFallback.getOperation() == OPERATION_APPEND) { + pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap, + outBufferCache); + } + } + } } // Build the font map and fallback map. @@ -365,4 +416,42 @@ public final class SystemFonts { Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result); return result; } + + private static boolean scriptMatch(LocaleList localeList, String targetScript) { + if (localeList == null || localeList.isEmpty()) { + return false; + } + for (int i = 0; i < localeList.size(); ++i) { + Locale locale = localeList.get(i); + if (locale == null) { + continue; + } + String baseScript = FontConfig.resolveScript(locale); + if (baseScript.equals(targetScript)) { + return true; + } + + // Subtag match + if (targetScript.equals("Bopo") && baseScript.equals("Hanb")) { + // Hanb is Han with Bopomofo. + return true; + } else if (targetScript.equals("Hani")) { + if (baseScript.equals("Hanb") || baseScript.equals("Hans") + || baseScript.equals("Hant") || baseScript.equals("Kore") + || baseScript.equals("Jpan")) { + // Han id suppoted by Taiwanese, Traditional Chinese, Simplified Chinese, Korean + // and Japanese. + return true; + } + } else if (targetScript.equals("Hira") || targetScript.equals("Hrkt") + || targetScript.equals("Kana")) { + if (baseScript.equals("Jpan") || baseScript.equals("Hrkt")) { + // Hiragana, Hiragana-Katakana, Katakana is supported by Japanese and + // Hiragana-Katakana script. + return true; + } + } + } + return false; + } } diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java index e81525fb7d60..f5e5803d4796 100644 --- a/graphics/java/android/graphics/text/LineBreakConfig.java +++ b/graphics/java/android/graphics/text/LineBreakConfig.java @@ -134,10 +134,25 @@ public final class LineBreakConfig { */ public static final int LINE_BREAK_STYLE_STRICT = 3; + /** + * The line break style that used for preventing automatic line breaking. + * + * This is useful when you want to preserve some words in the same line by using + * {@link android.text.style.LineBreakConfigSpan} or + * {@link android.text.style.LineBreakConfigSpan.NoBreakSpan} as a shorthand. + * Note that even if this style is specified, the grapheme based line break is still performed + * for preventing clipping text. + * + * @see android.text.style.LineBreakConfigSpan + * @see android.text.style.LineBreakConfigSpan.NoBreakSpan + */ + @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN) + public static final int LINE_BREAK_STYLE_NO_BREAK = 4; + /** @hide */ @IntDef(prefix = { "LINE_BREAK_STYLE_" }, value = { LINE_BREAK_STYLE_NONE, LINE_BREAK_STYLE_LOOSE, LINE_BREAK_STYLE_NORMAL, - LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED + LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED, LINE_BREAK_STYLE_NO_BREAK }) @Retention(RetentionPolicy.SOURCE) public @interface LineBreakStyle {} diff --git a/keystore/aaid/aidl/Android.bp b/keystore/aaid/aidl/Android.bp new file mode 100644 index 000000000000..97acfb4ea4c3 --- /dev/null +++ b/keystore/aaid/aidl/Android.bp @@ -0,0 +1,31 @@ +// Copyright 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +aidl_interface { + name: "android.security.aaid_aidl", + srcs: ["android/security/keystore/*.aidl"], + unstable: true, + backend: { + rust: { + enabled: true, + }, + cpp: { + enabled: true, + }, + }, +} diff --git a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl b/keystore/aaid/aidl/android/security/keystore/IKeyAttestationApplicationIdProvider.aidl index dbffd5f57ce2..c360cb8f281a 100644 --- a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl +++ b/keystore/aaid/aidl/android/security/keystore/IKeyAttestationApplicationIdProvider.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016, 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. @@ -14,19 +14,15 @@ * limitations under the License. */ -package android.security.keymaster; +package android.security.keystore; -import android.security.keymaster.KeyAttestationApplicationId; -import android.security.keymaster.KeyAttestationPackageInfo; -import android.content.pm.Signature; +import android.security.keystore.KeyAttestationApplicationId; -/** - * This must be kept manually in sync with system/security/keystore until AIDL - * can generate both Java and C++ bindings. - * - * @hide - */ +/** @hide */ interface IKeyAttestationApplicationIdProvider { - /* keep in sync with /system/security/keystore/keystore_attestation_id.cpp */ + /** + * Provides information describing the possible applications identified by a UID. + * @hide + */ KeyAttestationApplicationId getKeyAttestationApplicationId(int uid); } diff --git a/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl b/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl new file mode 100644 index 000000000000..c33e8309b2f2 --- /dev/null +++ b/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl @@ -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.security.keystore; + +import android.security.keystore.KeyAttestationPackageInfo; + +/** + * @hide + * The information aggregated by this parcelable is used by keystore to identify a caller of the + * keystore API toward a remote party. It aggregates multiple PackageInfos because keystore + * can only determine a caller by uid granularity, and a uid can be shared by multiple packages. + * The remote party must decide if it trusts all of the packages enough to consider the + * confidentiality of the key material in question intact. + */ +parcelable KeyAttestationApplicationId { + KeyAttestationPackageInfo[] packageInfos; +} diff --git a/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl b/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl new file mode 100644 index 000000000000..5f647d0b1abe --- /dev/null +++ b/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl @@ -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 android.security.keystore; + +import android.security.keystore.Signature; + +/** + * @hide + * This parcelable constitutes and excerpt from the PackageManager's PackageInfo for the purpose of + * key attestation. It is part of the KeyAttestationApplicationId, which is used by + * keystore to identify the caller of the keystore API towards a remote party. + */ +parcelable KeyAttestationPackageInfo { + String packageName; + + long versionCode; + + Signature[] signatures; +} diff --git a/keystore/aaid/aidl/android/security/keystore/Signature.aidl b/keystore/aaid/aidl/android/security/keystore/Signature.aidl new file mode 100644 index 000000000000..800499a13355 --- /dev/null +++ b/keystore/aaid/aidl/android/security/keystore/Signature.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016, The Android Open 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.security.keystore; + +/** + * @hide + * Represents a signature data read from the package file. Extracted from from the PackageManager's + * PackageInfo for the purpose of key attestation. It is part of the KeyAttestationPackageInfo, + * which is used by keystore to identify the caller of the keystore API towards a remote party. + */ +parcelable Signature { + /** + * Represents signing certificate data associated with application package, signatures are + * expected to be a hex-encoded ASCII string representing valid X509 certificate. + */ + byte[] data; +} diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java index 0f3488bbe8d1..31c2eb2efaed 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -28,8 +28,8 @@ import android.system.keystore2.ResponseCode; import android.util.Log; /** - * @hide This is the client side for IKeystoreUserManager AIDL. - * It shall only be used by the LockSettingsService. + * @hide This is the client side for IKeystoreMaintenance AIDL. + * It is used mainly by LockSettingsService. */ public class AndroidKeyStoreMaintenance { private static final String TAG = "AndroidKeyStoreMaintenance"; @@ -66,7 +66,7 @@ public class AndroidKeyStoreMaintenance { } /** - * Informs Keystore 2.0 about removing a usergit mer + * Informs Keystore 2.0 about removing a user * * @param userId - Android user id of the user being removed * @return 0 if successful or a {@code ResponseCode} @@ -91,7 +91,7 @@ public class AndroidKeyStoreMaintenance { * * @param userId - Android user id of the user * @param password - a secret derived from the synthetic password provided by the - * LockSettingService + * LockSettingsService * @return 0 if successful or a {@code ResponseCode} * @hide */ @@ -110,7 +110,7 @@ public class AndroidKeyStoreMaintenance { } /** - * Informs Keystore 2.0 that an app was uninstalled and the corresponding namspace is to + * Informs Keystore 2.0 that an app was uninstalled and the corresponding namespace is to * be cleared. */ public static int clearNamespace(@Domain int domain, long namespace) { @@ -172,10 +172,10 @@ public class AndroidKeyStoreMaintenance { * namespace. * * @return * 0 on success - * * KEY_NOT_FOUND if the source did not exists. + * * KEY_NOT_FOUND if the source did not exist. * * PERMISSION_DENIED if any of the required permissions was missing. * * INVALID_ARGUMENT if the destination was occupied or any domain value other than - * the allowed once were specified. + * the allowed ones was specified. * * SYSTEM_ERROR if an unexpected error occurred. */ public static int migrateKeyNamespace(KeyDescriptor source, KeyDescriptor destination) { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index c55a781ce2a4..11278e84ceaa 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -43,6 +43,7 @@ import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; import javax.crypto.SecretKey; @@ -181,6 +182,8 @@ public class AndroidKeyStoreProvider extends Provider { spi = ((Mac) cryptoPrimitive).getCurrentSpi(); } else if (cryptoPrimitive instanceof Cipher) { spi = ((Cipher) cryptoPrimitive).getCurrentSpi(); + } else if (cryptoPrimitive instanceof KeyAgreement) { + spi = ((KeyAgreement) cryptoPrimitive).getCurrentSpi(); } else { throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive + ". Supported: Signature, Mac, Cipher"); diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp index b6e743a2b7e1..ed2ff2de245b 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp +++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp @@ -37,7 +37,7 @@ android_test { "androidx.test.rules", "androidx.test.ext.junit", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", "testables", "platform-test-annotations", ], diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml new file mode 100644 index 000000000000..ee9f070f6765 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml @@ -0,0 +1,145 @@ +<?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. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="@dimen/desktop_mode_handle_menu_width" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height" + android:layout_marginTop="@dimen/desktop_mode_handle_menu_margin_top" + android:layout_marginStart="1dp" + android:elevation="1dp" + android:orientation="horizontal" + android:background="@drawable/desktop_mode_decor_handle_menu_background" + android:gravity="center_vertical"> + + <ImageView + android:id="@+id/application_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="14dp" + android:layout_marginEnd="14dp" + android:contentDescription="@string/app_icon_text"/> + + <TextView + android:id="@+id/application_name" + android:layout_width="0dp" + android:layout_height="wrap_content" + tools:text="Gmail" + android:textColor="?androidprv:attr/materialColorOnSurface" + android:textSize="14sp" + android:textFontWeight="500" + android:lineHeight="20dp" + android:textStyle="normal" + android:layout_weight="1"/> + + <ImageButton + android:id="@+id/collapse_menu_button" + android:layout_width="32dp" + android:layout_height="32dp" + android:padding="4dp" + android:layout_marginEnd="14dp" + android:layout_marginStart="14dp" + android:contentDescription="@string/collapse_menu_text" + android:src="@drawable/ic_baseline_expand_more_24" + android:rotation="180" + android:tint="?androidprv:attr/materialColorOnSurface" + android:background="?android:selectableItemBackgroundBorderless"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height" + android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin" + android:layout_marginStart="1dp" + android:orientation="horizontal" + android:elevation="1dp" + android:background="@drawable/desktop_mode_decor_handle_menu_background" + android:gravity="center_vertical"> + + <ImageButton + android:id="@+id/fullscreen_button" + android:layout_marginEnd="4dp" + android:contentDescription="@string/fullscreen_text" + android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + <ImageButton + android:id="@+id/split_screen_button" + android:layout_marginStart="4dp" + android:layout_marginEnd="4dp" + android:contentDescription="@string/split_screen_text" + android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + <ImageButton + android:id="@+id/floating_button" + android:layout_marginStart="4dp" + android:layout_marginEnd="4dp" + android:contentDescription="@string/float_button_text" + android:src="@drawable/desktop_mode_ic_handle_menu_floating" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + <ImageButton + android:id="@+id/desktop_button" + android:layout_marginStart="4dp" + android:contentDescription="@string/desktop_text" + android:src="@drawable/desktop_mode_ic_handle_menu_desktop" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height" + android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin" + android:layout_marginStart="1dp" + android:orientation="vertical" + android:elevation="1dp" + android:background="@drawable/desktop_mode_decor_handle_menu_background"> + + <Button + android:id="@+id/screenshot_button" + android:contentDescription="@string/screenshot_text" + android:text="@string/screenshot_text" + android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot" + android:drawableTint="?androidprv:attr/materialColorOnSurface" + style="@style/DesktopModeHandleMenuActionButton"/> + + <Button + android:id="@+id/select_button" + android:contentDescription="@string/select_text" + android:text="@string/select_text" + android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select" + android:drawableTint="?androidprv:attr/materialColorOnSurface" + style="@style/DesktopModeHandleMenuActionButton"/> + + </LinearLayout> +</LinearLayout> + diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml deleted file mode 100644 index c2ee3066d059..000000000000 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml +++ /dev/null @@ -1,58 +0,0 @@ -<?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. - --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="@dimen/desktop_mode_handle_menu_width" - android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height" - android:orientation="horizontal" - android:background="@drawable/desktop_mode_decor_handle_menu_background" - android:gravity="center_vertical"> - - <ImageView - android:id="@+id/application_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="14dp" - android:layout_marginEnd="14dp" - android:contentDescription="@string/app_icon_text"/> - - <TextView - android:id="@+id/application_name" - android:layout_width="0dp" - android:layout_height="wrap_content" - tools:text="Gmail" - android:textColor="?androidprv:attr/materialColorOnSurface" - android:textSize="14sp" - android:textFontWeight="500" - android:lineHeight="20dp" - android:textStyle="normal" - android:layout_weight="1"/> - - <ImageButton - android:id="@+id/collapse_menu_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:padding="4dp" - android:layout_marginEnd="14dp" - android:layout_marginStart="14dp" - android:contentDescription="@string/collapse_menu_text" - android:src="@drawable/ic_baseline_expand_more_24" - android:rotation="180" - android:tint="?androidprv:attr/materialColorOnSurface" - android:background="?android:selectableItemBackgroundBorderless"/> -</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml deleted file mode 100644 index e637671937bd..000000000000 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?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. - --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:layout_width="@dimen/desktop_mode_handle_menu_width" - android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height" - android:orientation="vertical" - android:background="@drawable/desktop_mode_decor_handle_menu_background"> - - <Button - android:id="@+id/screenshot_button" - android:contentDescription="@string/screenshot_text" - android:text="@string/screenshot_text" - android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot" - android:drawableTint="?androidprv:attr/materialColorOnSurface" - style="@style/DesktopModeHandleMenuActionButton"/> - - <Button - android:id="@+id/select_button" - android:contentDescription="@string/select_text" - android:text="@string/select_text" - android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select" - android:drawableTint="?androidprv:attr/materialColorOnSurface" - style="@style/DesktopModeHandleMenuActionButton"/> - <Button - android:id="@+id/close_button" - android:contentDescription="@string/close_text" - android:text="@string/close_text" - android:drawableStart="@drawable/desktop_mode_ic_handle_menu_close" - android:drawableTint="?androidprv:attr/materialColorOnSurface" - style="@style/DesktopModeHandleMenuActionButton"/> - -</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml deleted file mode 100644 index c4b688daeb6e..000000000000 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml +++ /dev/null @@ -1,63 +0,0 @@ -<?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. - --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:layout_width="@dimen/desktop_mode_handle_menu_width" - android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height" - android:orientation="horizontal" - android:background="@drawable/desktop_mode_decor_handle_menu_background" - android:gravity="center_vertical"> - - <ImageButton - android:id="@+id/fullscreen_button" - android:layout_marginEnd="4dp" - android:contentDescription="@string/fullscreen_text" - android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen" - android:tint="?androidprv:attr/materialColorOnSurface" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - - <ImageButton - android:id="@+id/split_screen_button" - android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" - android:contentDescription="@string/split_screen_text" - android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen" - android:tint="?androidprv:attr/materialColorOnSurface" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - - <ImageButton - android:id="@+id/floating_button" - android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" - android:contentDescription="@string/float_button_text" - android:src="@drawable/desktop_mode_ic_handle_menu_floating" - android:tint="?androidprv:attr/materialColorOnSurface" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - - <ImageButton - android:id="@+id/desktop_button" - android:layout_marginStart="4dp" - android:contentDescription="@string/desktop_text" - android:src="@drawable/desktop_mode_ic_handle_menu_desktop" - android:tint="?androidprv:attr/materialColorOnSurface" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - -</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 705e38731a0c..1f6f7aeadd45 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -143,7 +143,9 @@ <dimen name="bubble_expanded_view_padding">16dp</dimen> <!-- Padding for the edge of the expanded view that is closest to the edge of the screen used when displaying in landscape on a large screen. --> - <dimen name="bubble_expanded_view_largescreen_landscape_padding">128dp</dimen> + <dimen name="bubble_expanded_view_largescreen_landscape_padding">102dp</dimen> + <!-- The width of the expanded view on large screens. --> + <dimen name="bubble_expanded_view_largescreen_width">540dp</dimen> <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include a slight touch slop around the expanded view. --> <dimen name="bubble_expanded_view_slop">8dp</dimen> @@ -434,11 +436,11 @@ <!-- The height of the handle menu's "Windowing" pill in desktop mode. --> <dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen> - <!-- The height of the handle menu's "More Actions" pill in desktop mode, but not freeform. --> - <dimen name="desktop_mode_handle_menu_more_actions_pill_height">104dp</dimen> + <!-- The height of the handle menu's "More Actions" pill in desktop mode. --> + <dimen name="desktop_mode_handle_menu_more_actions_pill_height">52dp</dimen> - <!-- The height of the handle menu's "More Actions" pill in freeform desktop windowing mode. --> - <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">52dp</dimen> + <!-- The height of the handle menu in desktop mode. --> + <dimen name="desktop_mode_handle_menu_height">328dp</dimen> <!-- The top margin of the handle menu in desktop mode. --> <dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java index 410ae78dba1b..38550b405c0e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java @@ -32,6 +32,7 @@ import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; +import android.window.SystemPerformanceHinter; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -58,6 +59,14 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { /** {@link DisplayAreaContext} list, which is mapped by display IDs. */ private final SparseArray<DisplayAreaContext> mDisplayAreaContexts = new SparseArray<>(); + private final SystemPerformanceHinter.DisplayRootProvider mPerfRootProvider = + new SystemPerformanceHinter.DisplayRootProvider() { + @Override + public SurfaceControl getRootForDisplay(int displayId) { + return mLeashes.get(displayId); + } + }; + private final Context mContext; public RootTaskDisplayAreaOrganizer(Executor executor, Context context) { @@ -229,6 +238,11 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { return mDisplayAreaContexts.get(displayId); } + @NonNull + public SystemPerformanceHinter.DisplayRootProvider getPerformanceRootProvider() { + return mPerfRootProvider; + } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java index f1ee8fa38485..a67821b7e819 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java @@ -318,7 +318,7 @@ public class BadgedImageView extends ConstraintLayout { /** * Animates the dot to the given scale, running the optional callback when the animation ends. */ - private void animateDotScale(float toScale, @Nullable Runnable after) { + public void animateDotScale(float toScale, @Nullable Runnable after) { mDotIsAnimating = true; // Don't restart the animation if we're already animating to the given value. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index df19757203eb..dc099d9abda4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -27,6 +27,7 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable import android.util.PathParser import android.view.LayoutInflater +import android.view.View.VISIBLE import android.widget.FrameLayout import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.R @@ -156,7 +157,9 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl fun setShowDot(show: Boolean) { showDot = show - overflowBtn?.updateDotVisibility(true /* animate */) + if (overflowBtn?.visibility == VISIBLE) { + overflowBtn?.updateDotVisibility(true /* animate */) + } } /** Creates the expanded view for bubbles showing in the stack view. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index ea7053d8ee49..17e06e93b3a8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -54,10 +54,6 @@ public class BubblePositioner { public static final float FLYOUT_MAX_WIDTH_PERCENT_LARGE_SCREEN = 0.3f; /** The max percent of screen width to use for the flyout on phone. */ public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f; - /** The percent of screen width for the expanded view on a large screen. **/ - private static final float EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT = 0.48f; - /** The percent of screen width for the expanded view on a large screen. **/ - private static final float EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT = 0.70f; /** The percent of screen width for the expanded view on a small tablet. **/ private static final float EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT = 0.72f; /** The percent of screen width for the expanded view when shown in the bubble bar. **/ @@ -95,6 +91,7 @@ public class BubblePositioner { private int mPointerWidth; private int mPointerHeight; private int mPointerOverlap; + private int mManageButtonHeightIncludingMargins; private int mManageButtonHeight; private int mOverflowHeight; private int mMinimumFlyoutWidthLargeScreen; @@ -176,21 +173,20 @@ public class BubblePositioner { mExpandedViewLargeScreenWidth = (int) (bounds.width() * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT); } else { - mExpandedViewLargeScreenWidth = isLandscape() - ? (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT) - : (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT); + mExpandedViewLargeScreenWidth = + res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width); } if (mIsLargeScreen) { - if (isLandscape() && !mIsSmallTablet) { + if (mIsSmallTablet) { + final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2; + mExpandedViewLargeScreenInsetClosestEdge = centeredInset; + mExpandedViewLargeScreenInsetFurthestEdge = centeredInset; + } else { mExpandedViewLargeScreenInsetClosestEdge = res.getDimensionPixelSize( R.dimen.bubble_expanded_view_largescreen_landscape_padding); mExpandedViewLargeScreenInsetFurthestEdge = bounds.width() - mExpandedViewLargeScreenInsetClosestEdge - mExpandedViewLargeScreenWidth; - } else { - final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2; - mExpandedViewLargeScreenInsetClosestEdge = centeredInset; - mExpandedViewLargeScreenInsetFurthestEdge = centeredInset; } } else { mExpandedViewLargeScreenInsetClosestEdge = mExpandedViewPadding; @@ -202,7 +198,9 @@ public class BubblePositioner { mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap); - mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height); + mManageButtonHeightIncludingMargins = + res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height); + mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height); mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); mMinimumFlyoutWidthLargeScreen = res.getDimensionPixelSize( @@ -420,7 +418,7 @@ public class BubblePositioner { int pointerSize = showBubblesVertically() ? mPointerWidth : (mPointerHeight + mPointerMargin); - int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeight; + int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins; return getAvailableRect().height() - expandedContainerY - paddingTop @@ -438,6 +436,15 @@ public class BubblePositioner { // overflow in landscape on phone is max return MAX_HEIGHT; } + + if (mIsLargeScreen && !mIsSmallTablet && !isOverflow) { + // the expanded view height on large tablets is calculated based on the shortest screen + // size and is the same in both portrait and landscape + int maxVerticalInset = Math.max(mInsets.top, mInsets.bottom); + int shortestScreenSide = Math.min(mScreenRect.height(), mScreenRect.width()); + return shortestScreenSide - 2 * maxVerticalInset - mManageButtonHeight; + } + float desiredHeight = isOverflow ? mOverflowHeight : ((Bubble) bubble).getDesiredHeight(mContext); @@ -466,7 +473,8 @@ public class BubblePositioner { return topAlignment; } // If we're here, we're showing vertically & developer has made height less than maximum. - int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeight; + int manageButtonHeight = + isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins; float pointerPosition = getPointerPosition(bubblePosition); float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight; float topIfCentered = pointerPosition - (expandedViewHeight / 2); 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 c124b532b89d..2241c343a208 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 @@ -1864,6 +1864,14 @@ public class BubbleStackView extends FrameLayout : GONE); } + private void updateOverflowDotVisibility(boolean expanding) { + if (mBubbleOverflow.showDot()) { + mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> { + mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE); + }); + } + } + // via BubbleData.Listener void updateBubble(Bubble bubble) { animateInFlyoutForBubble(bubble); @@ -2274,6 +2282,7 @@ public class BubbleStackView extends FrameLayout if (mIsExpanded && mExpandedBubble.getExpandedView() != null) { maybeShowManageEdu(); } + updateOverflowDotVisibility(true /* expanding */); } /* after */); int index; if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) { @@ -2405,11 +2414,15 @@ public class BubbleStackView extends FrameLayout // since we're about to animate collapsed. mExpandedAnimationController.notifyPreparingToCollapse(); + updateOverflowDotVisibility(false /* expanding */); final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack( mStackAnimationController .getStackPositionAlongNearestHorizontalEdge() /* collapseTo */, - () -> mBubbleContainer.setActiveController(mStackAnimationController)); + () -> { + mBubbleContainer.setActiveController(mStackAnimationController); + updateOverflowVisibility(); + }); final Runnable after = () -> { final BubbleViewProvider previouslySelected = mExpandedBubble; @@ -2424,7 +2437,6 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(), mExpandedBubble)); } - updateOverflowVisibility(); updateZOrder(); updateBadges(true /* setBadgeForCollapsedStack */); afterExpandedViewAnimation(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 4d7042bbb3d2..738c94e82a95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -34,6 +34,8 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.BadgedImageView; +import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleStackView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -63,6 +65,12 @@ public class ExpandedAnimationController /** Damping ratio for expand/collapse spring. */ private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f; + /** + * Damping ratio for the overflow bubble spring; this is less bouncy so it doesn't bounce behind + * the top bubble when it goes to disappear. + */ + private static final float DAMPING_RATIO_OVERFLOW_BOUNCY = 0.90f; + /** Stiffness for the expand/collapse path-following animation. */ private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 400; @@ -274,9 +282,14 @@ public class ExpandedAnimationController // of the screen where the bubble will be stacked. path.lineTo(stackedX, p.y); + // The overflow should animate to the collapse point, so 0 offset. + final boolean isOverflow = bubble instanceof BadgedImageView + && BubbleOverflow.KEY.equals(((BadgedImageView) bubble).getKey()); + final float offsetY = isOverflow + ? 0 + : Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx; // Then, draw a line down to the stack position. - path.lineTo(stackedX, mCollapsePoint.y - + Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx); + path.lineTo(stackedX, mCollapsePoint.y + offsetY); } // The lead bubble should be the bubble with the longest distance to travel when we're @@ -505,8 +518,12 @@ public class ExpandedAnimationController @Override SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) { + boolean isOverflow = (view instanceof BadgedImageView) + && BubbleOverflow.KEY.equals(((BadgedImageView) view).getKey()); return new SpringForce() - .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY) + .setDampingRatio(isOverflow + ? DAMPING_RATIO_OVERFLOW_BOUNCY + : DAMPING_RATIO_MEDIUM_LOW_BOUNCY) .setStiffness(SpringForce.STIFFNESS_LOW); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 5e42782431fd..e9344ffcce0c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -203,7 +203,7 @@ public class SystemWindows { + "SystemWindow:" + view); return null; } - return root.getFocusGrantToken(); + return root.getInputTransferToken(); } private class PerDisplay { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java index 931cf0cee28c..c6c9b3562308 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java @@ -94,6 +94,10 @@ public class TvWindowMenuActionButton extends RelativeLayout { mCurrentIcon = icon; // Remove old image while waiting for the new one to load. mIconImageView.setImageDrawable(null); + if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // Disallow loading icon from content URI + return; + } icon.loadDrawableAsync(mContext, d -> { // The image hasn't been set any other way and the drawable belongs to the most // recently set Icon. 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 998cd5d08c72..e6d3abcc6e1d 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 @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.SystemProperties; import android.view.IWindowManager; import android.view.accessibility.AccessibilityManager; +import android.window.SystemPerformanceHinter; import com.android.internal.logging.UiEventLogger; import com.android.launcher3.icons.IconProvider; @@ -85,6 +86,7 @@ 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.performance.PerfHintController; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; @@ -296,6 +298,17 @@ public abstract class WMShellBaseModule { return new LaunchAdjacentController(syncQueue); } + @WMSingleton + @Provides + static SystemPerformanceHinter provideSystemPerformanceHinter(Context context, + ShellInit shellInit, + ShellCommandHandler shellCommandHandler, + RootTaskDisplayAreaOrganizer rootTdaOrganizer) { + final PerfHintController perfHintController = + new PerfHintController(context, shellInit, shellCommandHandler, rootTdaOrganizer); + return perfHintController.getHinter(); + } + // // Back animation // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 11aa054676cb..5dfba5e7ff1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -494,13 +494,14 @@ public abstract class WMShellModule { ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, LaunchAdjacentController launchAdjacentController, + RecentsTransitionHandler recentsTransitionHandler, @ShellMainThread ShellExecutor mainExecutor ) { return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController, displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, - launchAdjacentController, mainExecutor); + launchAdjacentController, recentsTransitionHandler, mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index af97cf68915f..8a6403705c1c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -23,7 +23,7 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; -import com.android.wm.shell.pip2.PipTransition; +import com.android.wm.shell.pip2.phone.PipTransition; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java index 570f0a3db35a..f2631eff890d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java @@ -19,6 +19,7 @@ package com.android.wm.shell.dagger.pip; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip2.phone.PipTransition; import dagger.Module; import dagger.Provides; @@ -36,7 +37,7 @@ public abstract class PipModule { @Provides static PipTransitionController providePipTransitionController( com.android.wm.shell.pip.PipTransition legacyPipTransition, - com.android.wm.shell.pip2.PipTransition newPipTransition) { + PipTransition newPipTransition) { if (PipUtils.isPip2ExperimentEnabled()) { return newPipTransition; } else { 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 09ba4f79326e..412a5b5a6997 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 @@ -60,6 +60,8 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP import com.android.wm.shell.sysui.ShellCommandHandler @@ -68,7 +70,6 @@ import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions -import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.util.KtProtoLog import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration import com.android.wm.shell.windowdecor.MoveToDesktopAnimator @@ -93,6 +94,7 @@ class DesktopTasksController( ToggleResizeDesktopTaskTransitionHandler, private val desktopModeTaskRepository: DesktopModeTaskRepository, private val launchAdjacentController: LaunchAdjacentController, + private val recentsTransitionHandler: RecentsTransitionHandler, @ShellMainThread private val mainExecutor: ShellExecutor ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler { @@ -119,6 +121,8 @@ class DesktopTasksController( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width ) + private var recentsAnimationRunning = false + // This is public to avoid cyclic dependency; it is set by SplitScreenController lateinit var splitScreenController: SplitScreenController @@ -139,6 +143,19 @@ class DesktopTasksController( ) transitions.addHandler(this) desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor) + + recentsTransitionHandler.addTransitionStateListener( + object : RecentsTransitionStateListener { + override fun onAnimationStateChanged(running: Boolean) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: recents animation state changed running=%b", + running + ) + recentsAnimationRunning = running + } + } + ) } /** Show all tasks, that are part of the desktop, on top of launcher */ @@ -644,6 +661,10 @@ class DesktopTasksController( val triggerTask = request.triggerTask val shouldHandleRequest = when { + recentsAnimationRunning -> { + reason = "recents animation is running" + false + } // Only handle open or to front transitions request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> { reason = "transition type not handled (${request.type})" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt new file mode 100644 index 000000000000..f7977f88006e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt @@ -0,0 +1,56 @@ +/* + * 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.performance + +import android.content.Context +import android.os.PerformanceHintManager +import android.os.Process +import android.window.SystemPerformanceHinter +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.sysui.ShellCommandHandler +import com.android.wm.shell.sysui.ShellInit +import java.io.PrintWriter +import java.util.concurrent.TimeUnit + +/** + * Manages the performance hints to the system. + */ +class PerfHintController(private val mContext: Context, + shellInit: ShellInit, + private val mShellCommandHandler: ShellCommandHandler, + rootTdaOrganizer: RootTaskDisplayAreaOrganizer) { + + // The system perf hinter + val hinter: SystemPerformanceHinter + + init { + hinter = SystemPerformanceHinter(mContext, + rootTdaOrganizer.performanceRootProvider) + shellInit.addInitCallback(this::onInit, this) + } + + private fun onInit() { + mShellCommandHandler.addDumpCallback(this::dump, this) + val perfHintMgr = mContext.getSystemService(PerformanceHintManager::class.java) + val adpfSession = perfHintMgr!!.createHintSession(intArrayOf(Process.myTid()), + TimeUnit.SECONDS.toNanos(1)) + hinter.setAdpfSession(adpfSession) + } + + fun dump(pw: PrintWriter, prefix: String?) { + hinter.dump(pw, prefix) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index b8e4c04ac262..d704b091754f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -14,9 +14,14 @@ * limitations under the License. */ -package com.android.wm.shell.pip2; +package com.android.wm.shell.pip2.phone; + +import static android.view.WindowManager.TRANSIT_OPEN; import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.PictureInPictureParams; +import android.graphics.Rect; import android.os.IBinder; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -34,8 +39,13 @@ import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; -/** Placeholder, for demonstrate purpose only. */ +/** + * Implementation of transitions for PiP on phone. + */ public class PipTransition extends PipTransitionController { + @Nullable + private IBinder mAutoEnterButtonNavTransition; + public PipTransition( @NonNull ShellInit shellInit, @NonNull ShellTaskOrganizer shellTaskOrganizer, @@ -58,15 +68,63 @@ public class PipTransition extends PipTransitionController { @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { + if (isAutoEnterInButtonNavigation(request)) { + mAutoEnterButtonNavTransition = transition; + return getEnterPipTransaction(transition, request); + } return null; } @Override + public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request, + @NonNull WindowContainerTransaction outWct) { + if (isAutoEnterInButtonNavigation(request)) { + outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */); + mAutoEnterButtonNavTransition = transition; + } + } + + private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + final ActivityManager.RunningTaskInfo pipTask = request.getPipTask(); + PictureInPictureParams pipParams = pipTask.pictureInPictureParams; + mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo, + pipParams, mPipBoundsAlgorithm); + + // calculate the entry bounds and notify core to move task to pinned with final bounds + final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds); + return wct; + } + + private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) { + final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask(); + if (pipTask == null) { + return false; + } + if (pipTask.pictureInPictureParams == null) { + return false; + } + + // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type + // implies that we are entering PiP in button navigation mode. This is guaranteed by + // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav. + return requestInfo.getType() == TRANSIT_OPEN + && pipTask.pictureInPictureParams.isAutoEnterEnabled(); + } + + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (transition == mAutoEnterButtonNavTransition) { + startTransaction.apply(); + finishCallback.onTransitionFinished(null); + return true; + } return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index ead2f9cbd1ad..d31476c63890 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -54,6 +54,7 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.IResultReceiver; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -279,7 +280,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mDeathHandler = () -> { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.DeathRecipient: binder died", mInstanceId); - finish(mWillFinishToHome, false /* leaveHint */); + finish(mWillFinishToHome, false /* leaveHint */, null /* finishCb */); }; try { mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */); @@ -313,7 +314,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } if (mFinishCB != null) { - finishInner(toHome, false /* userLeave */); + finishInner(toHome, false /* userLeave */, null /* finishCb */); } else { cleanUp(); } @@ -670,7 +671,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // now and let it do its animation (since recents is going to be occluded). sendCancelWithSnapshots(); mExecutor.executeDelayed( - () -> finishInner(true /* toHome */, false /* userLeaveHint */), 0); + () -> finishInner(true /* toHome */, false /* userLeaveHint */, + null /* finishCb */), 0); return; } if (recentsOpening != null) { @@ -899,11 +901,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @Override @SuppressLint("NewApi") - public void finish(boolean toHome, boolean sendUserLeaveHint) { - mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint)); + public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) { + mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint, finishCb)); } - private void finishInner(boolean toHome, boolean sendUserLeaveHint) { + private void finishInner(boolean toHome, boolean sendUserLeaveHint, + IResultReceiver runnerFinishCb) { if (mFinishCB == null) { Slog.e(TAG, "Duplicate call to finish"); return; @@ -993,6 +996,16 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } cleanUp(); finishCB.onTransitionFinished(wct.isEmpty() ? null : wct); + if (runnerFinishCb != null) { + try { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: calling finish callback", + mInstanceId); + runnerFinishCb.send(0, null); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report transition finished", e); + } + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 5e2c61b9d3cd..026f9898359f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -143,6 +143,8 @@ import com.android.wm.shell.util.SplitBounds; import com.android.wm.shell.util.TransitionUtil; import com.android.wm.shell.windowdecor.WindowDecorViewModel; +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; @@ -2691,7 +2693,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @NonNull Transitions.TransitionFinishCallback finishCallback) { boolean shouldAnimate = true; if (mSplitTransitions.isPendingEnter(transition)) { - shouldAnimate = startPendingEnterAnimation( + shouldAnimate = startPendingEnterAnimation(transition, mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction); } else if (mSplitTransitions.isPendingDismiss(transition)) { final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss; @@ -2730,7 +2732,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private boolean startPendingEnterAnimation( + private boolean startPendingEnterAnimation(@NonNull IBinder transition, @NonNull SplitScreenTransitions.EnterSession enterTransition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { @@ -2759,21 +2761,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - if (mSplitTransitions.mPendingEnter.mExtraTransitType + SplitScreenTransitions.EnterSession pendingEnter = mSplitTransitions.mPendingEnter; + if (pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { // Open to side should only be used when split already active and foregorund. if (mainChild == null && sideChild == null) { Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "Launched a task in split, but didn't receive any task in transition.")); // This should happen when the target app is already on front, so just cancel. - mSplitTransitions.mPendingEnter.cancel(null); + pendingEnter.cancel(null); return true; } } else { if (mainChild == null || sideChild == null) { final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN : (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED); - mSplitTransitions.mPendingEnter.cancel( + pendingEnter.cancel( (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct)); Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "launched 2 tasks in split, but didn't receive " @@ -2784,6 +2787,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mRecentTasks.isPresent() && sideChild != null) { mRecentTasks.get().removeSplitPair(sideChild.getTaskInfo().taskId); } + if (pendingEnter.mRemoteHandler != null) { + // Pass false for aborted since WM didn't abort, business logic chose to + // terminate/exit early + pendingEnter.mRemoteHandler.onTransitionConsumed(transition, + false /*aborted*/, finishT); + } mSplitUnsupportedToast.show(); return true; } @@ -3109,6 +3118,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); } + @NeverCompile @Override public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 83dc7fa5e869..e828eedc275c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; + import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; @@ -693,9 +694,19 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + Transitions.TransitionFinishCallback finishCB = wct -> { + mixed.mInFlightSubAnimations--; + if (mixed.mInFlightSubAnimations == 0) { + mActiveTransitions.remove(mixed); + finishCallback.onTransitionFinished(wct); + } + }; + + mixed.mInFlightSubAnimations++; boolean consumed = mRecentsHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCallback); + mixed.mTransition, info, startTransaction, finishTransaction, finishCB); if (!consumed) { + mixed.mInFlightSubAnimations--; return false; } if (mDesktopTasksController != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index e0635ac2e19a..de03f5826925 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -322,7 +322,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; mAnimations.remove(transition); - info.releaseAllSurfaces(); finishCallback.onTransitionFinished(null /* wct */); }; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index fab2dd2bf3e1..8b050e524038 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -152,6 +152,16 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { } @Override + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, + @Nullable SurfaceControl.Transaction finishTransaction) { + try { + mRemote.getRemoteTransition().onTransitionConsumed(transition, aborted); + } catch (RemoteException e) { + Log.e(Transitions.TAG, "Error calling onTransitionConsumed()", e); + } + } + + @Override public String toString() { return "OneShotRemoteHandler:" + mRemote.getDebugName() + ":" + mRemote.getRemoteTransition(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index a90edf20f94e..592b22a47bc4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -86,7 +86,16 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishT) { - mRequestedRemotes.remove(transition); + RemoteTransition remoteTransition = mRequestedRemotes.remove(transition); + if (remoteTransition == null) { + return; + } + + try { + remoteTransition.getRemoteTransition().onTransitionConsumed(transition, aborted); + } catch (RemoteException e) { + Log.e(TAG, "Error delegating onTransitionConsumed()", e); + } } @Override 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 780bbb5c9f31..bf99ab35cdd7 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 @@ -376,7 +376,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { public void onClick(View v) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final int id = v.getId(); - if (id == R.id.close_window || id == R.id.close_button) { + if (id == R.id.close_window) { mTaskOperations.closeTask(mTaskToken); if (isTaskInSplitScreen(mTaskId)) { RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java index a7a11dee80f2..15f8f1cfadf2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -42,7 +42,6 @@ import android.widget.TextView; import android.window.SurfaceSyncGroup; import com.android.wm.shell.R; -import com.android.wm.shell.desktopmode.DesktopModeStatus; /** * Handle menu opened when the appropriate button is clicked on. @@ -56,12 +55,8 @@ class HandleMenu { private static final String TAG = "HandleMenu"; private final Context mContext; private final WindowDecoration mParentDecor; - private WindowDecoration.AdditionalWindow mAppInfoPill; - private WindowDecoration.AdditionalWindow mWindowingPill; - private WindowDecoration.AdditionalWindow mMoreActionsPill; - private final PointF mAppInfoPillPosition = new PointF(); - private final PointF mWindowingPillPosition = new PointF(); - private final PointF mMoreActionsPillPosition = new PointF(); + private WindowDecoration.AdditionalWindow mHandleMenuWindow; + private final PointF mHandleMenuPosition = new PointF(); private final boolean mShouldShowWindowingPill; private final Drawable mAppIcon; private final CharSequence mAppName; @@ -73,13 +68,8 @@ class HandleMenu { private final int mCaptionY; private int mMarginMenuTop; private int mMarginMenuStart; - private int mMarginMenuSpacing; + private int mMenuHeight; private int mMenuWidth; - private int mAppInfoPillHeight; - private int mWindowingPillHeight; - private int mMoreActionsPillHeight; - private int mShadowRadius; - private int mCornerRadius; HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY, @@ -104,102 +94,86 @@ class HandleMenu { final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG); SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - createAppInfoPill(t, ssg); - if (mShouldShowWindowingPill) { - createWindowingPill(t, ssg); - } - createMoreActionsPill(t, ssg); + createHandleMenuWindow(t, ssg); ssg.addTransaction(t); ssg.markSyncReady(); setupHandleMenu(); } - private void createAppInfoPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { - final int x = (int) mAppInfoPillPosition.x; - final int y = (int) mAppInfoPillPosition.y; - mAppInfoPill = mParentDecor.addWindow( - R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, - "Menu's app info pill", - t, ssg, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius); + private void createHandleMenuWindow(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { + final int x = (int) mHandleMenuPosition.x; + final int y = (int) mHandleMenuPosition.y; + mHandleMenuWindow = mParentDecor.addWindow( + R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu", + t, ssg, x, y, mMenuWidth, mMenuHeight); } - private void createWindowingPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { - final int x = (int) mWindowingPillPosition.x; - final int y = (int) mWindowingPillPosition.y; - mWindowingPill = mParentDecor.addWindow( - R.layout.desktop_mode_window_decor_handle_menu_windowing_pill, - "Menu's windowing pill", - t, ssg, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius); - } - - private void createMoreActionsPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { - final int x = (int) mMoreActionsPillPosition.x; - final int y = (int) mMoreActionsPillPosition.y; - mMoreActionsPill = mParentDecor.addWindow( - R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill, - "Menu's more actions pill", - t, ssg, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius); + /** + * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions + * pill. + */ + private void setupHandleMenu() { + final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView(); + handleMenu.setOnTouchListener(mOnTouchListener); + setupAppInfoPill(handleMenu); + if (mShouldShowWindowingPill) { + setupWindowingPill(handleMenu); + } + setupMoreActionsPill(handleMenu); } /** - * Set up interactive elements and color of this handle menu + * Set up interactive elements of handle menu's app info pill. */ - private void setupHandleMenu() { - // App Info pill setup. - final View appInfoPillView = mAppInfoPill.mWindowViewHost.getView(); - final ImageButton collapseBtn = appInfoPillView.findViewById(R.id.collapse_menu_button); - final ImageView appIcon = appInfoPillView.findViewById(R.id.application_icon); - final TextView appName = appInfoPillView.findViewById(R.id.application_name); + private void setupAppInfoPill(View handleMenu) { + final ImageButton collapseBtn = handleMenu.findViewById(R.id.collapse_menu_button); + final ImageView appIcon = handleMenu.findViewById(R.id.application_icon); + final TextView appName = handleMenu.findViewById(R.id.application_name); collapseBtn.setOnClickListener(mOnClickListener); - appInfoPillView.setOnTouchListener(mOnTouchListener); appIcon.setImageDrawable(mAppIcon); appName.setText(mAppName); + } - // Windowing pill setup. - if (mShouldShowWindowingPill) { - final View windowingPillView = mWindowingPill.mWindowViewHost.getView(); - final ImageButton fullscreenBtn = windowingPillView.findViewById( - R.id.fullscreen_button); - final ImageButton splitscreenBtn = windowingPillView.findViewById( - R.id.split_screen_button); - final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button); - // TODO: Remove once implemented. - floatingBtn.setVisibility(View.GONE); - - final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button); - fullscreenBtn.setOnClickListener(mOnClickListener); - splitscreenBtn.setOnClickListener(mOnClickListener); - floatingBtn.setOnClickListener(mOnClickListener); - desktopBtn.setOnClickListener(mOnClickListener); - // The button corresponding to the windowing mode that the task is currently in uses a - // different color than the others. - final int[] iconColors = getWindowingIconColor(); - final ColorStateList inActiveColorStateList = ColorStateList.valueOf(iconColors[0]); - final ColorStateList activeColorStateList = ColorStateList.valueOf(iconColors[1]); - fullscreenBtn.setImageTintList( - mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - ? activeColorStateList : inActiveColorStateList); - splitscreenBtn.setImageTintList( - mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW - ? activeColorStateList : inActiveColorStateList); - floatingBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED - ? activeColorStateList : inActiveColorStateList); - desktopBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM - ? activeColorStateList : inActiveColorStateList); - } + /** + * Set up interactive elements and color of handle menu's windowing pill. + */ + private void setupWindowingPill(View handleMenu) { + final ImageButton fullscreenBtn = handleMenu.findViewById( + R.id.fullscreen_button); + final ImageButton splitscreenBtn = handleMenu.findViewById( + R.id.split_screen_button); + final ImageButton floatingBtn = handleMenu.findViewById(R.id.floating_button); + // TODO: Remove once implemented. + floatingBtn.setVisibility(View.GONE); + + final ImageButton desktopBtn = handleMenu.findViewById(R.id.desktop_button); + fullscreenBtn.setOnClickListener(mOnClickListener); + splitscreenBtn.setOnClickListener(mOnClickListener); + floatingBtn.setOnClickListener(mOnClickListener); + desktopBtn.setOnClickListener(mOnClickListener); + // The button corresponding to the windowing mode that the task is currently in uses a + // different color than the others. + final ColorStateList[] iconColors = getWindowingIconColor(); + final ColorStateList inActiveColorStateList = iconColors[0]; + final ColorStateList activeColorStateList = iconColors[1]; + final int windowingMode = mTaskInfo.getWindowingMode(); + fullscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_FULLSCREEN + ? activeColorStateList : inActiveColorStateList); + splitscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_MULTI_WINDOW + ? activeColorStateList : inActiveColorStateList); + floatingBtn.setImageTintList(windowingMode == WINDOWING_MODE_PINNED + ? activeColorStateList : inActiveColorStateList); + desktopBtn.setImageTintList(windowingMode == WINDOWING_MODE_FREEFORM + ? activeColorStateList : inActiveColorStateList); + } - // More Actions pill setup. - final View moreActionsPillView = mMoreActionsPill.mWindowViewHost.getView(); - final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button); - if (shouldShowCloseButton()) { - closeBtn.setVisibility(View.GONE); - } else { - closeBtn.setVisibility(View.VISIBLE); - closeBtn.setOnClickListener(mOnClickListener); - } - final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button); + /** + * Set up interactive elements & height of handle menu's more actions pill + */ + private void setupMoreActionsPill(View handleMenu) { + final Button selectBtn = handleMenu.findViewById(R.id.select_button); selectBtn.setOnClickListener(mOnClickListener); - final Button screenshotBtn = moreActionsPillView.findViewById(R.id.screenshot_button); + final Button screenshotBtn = handleMenu.findViewById(R.id.screenshot_button); // TODO: Remove once implemented. screenshotBtn.setVisibility(View.GONE); } @@ -208,7 +182,7 @@ class HandleMenu { * Returns array of windowing icon color based on current UI theme. First element of the * array is for inactive icons and the second is for active icons. */ - private int[] getWindowingIconColor() { + private ColorStateList[] getWindowingIconColor() { final int mode = mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES); @@ -218,11 +192,12 @@ class HandleMenu { final int inActiveColor = typedArray.getColor(0, isNightMode ? Color.WHITE : Color.BLACK); final int activeColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK); typedArray.recycle(); - return new int[] {inActiveColor, activeColor}; + return new ColorStateList[]{ColorStateList.valueOf(inActiveColor), + ColorStateList.valueOf(activeColor)}; } /** - * Updates the handle menu pills' position variables to reflect their next positions + * Updates handle menu's position variables to reflect its next position. */ private void updateHandleMenuPillPositions() { final int menuX, menuY; @@ -239,39 +214,19 @@ class HandleMenu { menuY = mCaptionY + mMarginMenuStart; } - // App Info pill setup. - final int appInfoPillY = menuY; - mAppInfoPillPosition.set(menuX, appInfoPillY); + // Handle Menu position setup. + mHandleMenuPosition.set(menuX, menuY); - final int windowingPillY, moreActionsPillY; - if (mShouldShowWindowingPill) { - windowingPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing; - mWindowingPillPosition.set(menuX, windowingPillY); - moreActionsPillY = windowingPillY + mWindowingPillHeight + mMarginMenuSpacing; - mMoreActionsPillPosition.set(menuX, moreActionsPillY); - } else { - // Just start after the end of the app info pill + margins. - moreActionsPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing; - mMoreActionsPillPosition.set(menuX, moreActionsPillY); - } } /** * Update pill layout, in case task changes have caused positioning to change. */ void relayout(SurfaceControl.Transaction t) { - if (mAppInfoPill != null) { + if (mHandleMenuWindow != null) { updateHandleMenuPillPositions(); - t.setPosition(mAppInfoPill.mWindowSurface, - mAppInfoPillPosition.x, mAppInfoPillPosition.y); - // Only show windowing buttons in proto2. Proto1 uses a system-level mode only. - final boolean shouldShowWindowingPill = DesktopModeStatus.isEnabled(); - if (shouldShowWindowingPill) { - t.setPosition(mWindowingPill.mWindowSurface, - mWindowingPillPosition.x, mWindowingPillPosition.y); - } - t.setPosition(mMoreActionsPill.mWindowSurface, - mMoreActionsPillPosition.x, mMoreActionsPillPosition.y); + t.setPosition(mHandleMenuWindow.mWindowSurface, + mHandleMenuPosition.x, mHandleMenuPosition.y); } } @@ -283,12 +238,12 @@ class HandleMenu { * @param ev the MotionEvent to compare against. */ void checkClickEvent(MotionEvent ev) { - final View appInfoPill = mAppInfoPill.mWindowViewHost.getView(); - final ImageButton collapse = appInfoPill.findViewById(R.id.collapse_menu_button); + final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView(); + final ImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button); // Translate the input point from display coordinates to the same space as the collapse // button, meaning its parent (app info pill view). - final PointF inputPoint = new PointF(ev.getX() - mAppInfoPillPosition.x, - ev.getY() - mAppInfoPillPosition.y); + final PointF inputPoint = new PointF(ev.getX() - mHandleMenuPosition.x, + ev.getY() - mHandleMenuPosition.y); if (pointInView(collapse, inputPoint.x, inputPoint.y)) { mOnClickListener.onClick(collapse); } @@ -303,23 +258,10 @@ class HandleMenu { */ boolean isValidMenuInput(PointF inputPoint) { if (!viewsLaidOut()) return true; - final boolean pointInAppInfoPill = pointInView( - mAppInfoPill.mWindowViewHost.getView(), - inputPoint.x - mAppInfoPillPosition.x, - inputPoint.y - mAppInfoPillPosition.y); - boolean pointInWindowingPill = false; - if (mWindowingPill != null) { - pointInWindowingPill = pointInView( - mWindowingPill.mWindowViewHost.getView(), - inputPoint.x - mWindowingPillPosition.x, - inputPoint.y - mWindowingPillPosition.y); - } - final boolean pointInMoreActionsPill = pointInView( - mMoreActionsPill.mWindowViewHost.getView(), - inputPoint.x - mMoreActionsPillPosition.x, - inputPoint.y - mMoreActionsPillPosition.y); - - return pointInAppInfoPill || pointInWindowingPill || pointInMoreActionsPill; + return pointInView( + mHandleMenuWindow.mWindowViewHost.getView(), + inputPoint.x - mHandleMenuPosition.x, + inputPoint.y - mHandleMenuPosition.y); } private boolean pointInView(View v, float x, float y) { @@ -331,33 +273,31 @@ class HandleMenu { * Check if the views for handle menu can be seen. */ private boolean viewsLaidOut() { - return mAppInfoPill.mWindowViewHost.getView().isLaidOut(); + return mHandleMenuWindow.mWindowViewHost.getView().isLaidOut(); } - private void loadHandleMenuDimensions() { final Resources resources = mContext.getResources(); mMenuWidth = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_width); + mMenuHeight = getHandleMenuHeight(resources); mMarginMenuTop = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_margin_top); mMarginMenuStart = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_margin_start); - mMarginMenuSpacing = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_pill_spacing_margin); - mAppInfoPillHeight = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_app_info_pill_height); - mWindowingPillHeight = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_windowing_pill_height); - mMoreActionsPillHeight = shouldShowCloseButton() - ? loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_more_actions_pill_freeform_height) - : loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_more_actions_pill_height); - mShadowRadius = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_shadow_radius); - mCornerRadius = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_corner_radius); + } + + /** + * Determines handle menu height based on if windowing pill should be shown. + */ + private int getHandleMenuHeight(Resources resources) { + int menuHeight = loadDimensionPixelSize(resources, + R.dimen.desktop_mode_handle_menu_height); + if (!mShouldShowWindowingPill) { + menuHeight -= loadDimensionPixelSize(resources, + R.dimen.desktop_mode_handle_menu_windowing_pill_height); + } + return menuHeight; } private int loadDimensionPixelSize(Resources resources, int resourceId) { @@ -367,19 +307,9 @@ class HandleMenu { return resources.getDimensionPixelSize(resourceId); } - private boolean shouldShowCloseButton() { - return mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; - } - void close() { - mAppInfoPill.releaseView(); - mAppInfoPill = null; - if (mWindowingPill != null) { - mWindowingPill.releaseView(); - mWindowingPill = null; - } - mMoreActionsPill.releaseView(); - mMoreActionsPill = null; + mHandleMenuWindow.releaseView(); + mHandleMenuWindow = null; } static final class Builder { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 07fee4316c08..335a5886ba28 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -44,6 +44,7 @@ import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import java.util.function.Supplier; @@ -283,10 +284,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> // Task surface itself float shadowRadius = loadDimension(resources, params.mShadowRadiusId); - int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor(); - mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f; - mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f; - mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f; final Point taskPosition = mTaskInfo.positionInParent; if (isFullscreen) { // Setting the task crop to the width/height stops input events from being sent to @@ -302,13 +299,22 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); } startT.setShadowRadius(mTaskSurface, shadowRadius) - .setColor(mTaskSurface, mTmpColor) .show(mTaskSurface); finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y) .setShadowRadius(mTaskSurface, shadowRadius); if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + if (!DesktopModeStatus.isVeiledResizeEnabled()) { + // When fluid resize is enabled, add a background to freeform tasks + int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor(); + mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f; + mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f; + mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f; + startT.setColor(mTaskSurface, mTmpColor); + } startT.setCornerRadius(mTaskSurface, params.mCornerRadius); finishT.setCornerRadius(mTaskSurface, params.mCornerRadius); + } else if (!DesktopModeStatus.isVeiledResizeEnabled()) { + startT.unsetColor(mTaskSurface); } if (mCaptionWindowManager == null) { @@ -425,13 +431,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> * @param yPos y position of new window * @param width width of new window * @param height height of new window - * @param shadowRadius radius of the shadow of the new window - * @param cornerRadius radius of the corners of the new window * @return the {@link AdditionalWindow} that was added. */ AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t, - SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height, int shadowRadius, - int cornerRadius) { + SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); SurfaceControl windowSurfaceControl = builder .setName(namePrefix + " of Task=" + mTaskInfo.taskId) @@ -442,8 +445,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> t.setPosition(windowSurfaceControl, xPos, yPos) .setWindowCrop(windowSurfaceControl, width, height) - .setShadowRadius(windowSurfaceControl, shadowRadius) - .setCornerRadius(windowSurfaceControl, cornerRadius) .show(windowSurfaceControl); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt new file mode 100644 index 000000000000..4f27ceddd705 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt @@ -0,0 +1,64 @@ +/* + * 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.flicker.pip + +import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +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 com.android.wm.shell.flicker.pip.common.PipTransition +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** Test changing aspect ratio of pip. */ +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { + pipApp.changeAspectRatio() + } + } + + @Presubmit + @Test + fun pipAspectRatioChangesProperly() { + flicker.assertLayersStart { this.visibleRegion(pipApp).isSameAspectRatio(16, 9) } + flicker.assertLayersEnd { this.visibleRegion(pipApp).isSameAspectRatio(1, 2) } + } + + companion object { + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index 54f94986d90c..d09a90cd7dc7 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -45,7 +45,7 @@ android_test { "kotlinx-coroutines-core", "mockito-kotlin2", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", "testables", "platform-test-annotations", "servicestests-utils", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java index 58d9a6486ff2..287a97c9b5b0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java @@ -22,20 +22,24 @@ import static android.view.View.LAYOUT_DIRECTION_LTR; import static android.view.View.LAYOUT_DIRECTION_RTL; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.content.Intent; import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; +import android.util.DisplayMetrics; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; @@ -257,6 +261,27 @@ public class BubblePositionerTest extends ShellTestCase { assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue(); } + @Test + public void testExpandedViewHeight_onLargeTablet() { + Insets insets = Insets.of(10, 20, 5, 15); + Rect screenBounds = new Rect(0, 0, 1800, 2600); + + new WindowManagerConfig() + .setLargeScreen() + .setInsets(insets) + .setScreenBounds(screenBounds) + .setUpConfig(); + mPositioner.update(); + + Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); + Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); + + int manageButtonHeight = + mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); + float expectedHeight = 1800 - 2 * 20 - manageButtonHeight; + assertThat(mPositioner.getExpandedViewHeight(bubble)).isWithin(0.1f).of(expectedHeight); + } + /** * Calculates the Y position bubbles should be placed based on the config. Based on * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and @@ -323,6 +348,8 @@ public class BubblePositionerTest extends ShellTestCase { ? MIN_WIDTH_FOR_TABLET : MIN_WIDTH_FOR_TABLET - 1; mConfiguration.orientation = mOrientation; + mConfiguration.screenWidthDp = pxToDp(mScreenBounds.width()); + mConfiguration.screenHeightDp = pxToDp(mScreenBounds.height()); when(mConfiguration.getLayoutDirection()).thenReturn(mLayoutDirection); WindowInsets windowInsets = mock(WindowInsets.class); @@ -331,5 +358,11 @@ public class BubblePositionerTest extends ShellTestCase { when(mWindowMetrics.getBounds()).thenReturn(mScreenBounds); when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics); } + + private int pxToDp(float px) { + int dpi = mContext.getResources().getDisplayMetrics().densityDpi; + float dp = px / ((float) dpi / DisplayMetrics.DENSITY_DEFAULT); + return (int) dp; + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index dea161786da2..ebcb6407a6fd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -54,6 +54,8 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreef import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController @@ -101,11 +103,13 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var launchAdjacentController: LaunchAdjacentController @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration @Mock lateinit var splitScreenController: SplitScreenController + @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController private lateinit var shellInit: ShellInit private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener private val shellExecutor = TestShellExecutor() // Mock running tasks are registered here so we can get the list from mock shell task organizer @@ -126,6 +130,10 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.splitScreenController = splitScreenController shellInit.init() + + val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java) + verify(recentsTransitionHandler).addTransitionStateListener(captor.capture()) + recentsTransitionStateListener = captor.value } private fun createController(): DesktopTasksController { @@ -144,6 +152,7 @@ class DesktopTasksControllerTest : ShellTestCase() { mToggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, launchAdjacentController, + recentsTransitionHandler, shellExecutor ) } @@ -355,7 +364,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToDesktop_splitTaskExitsSplit() { - var task = setUpSplitScreenTask() + val task = setUpSplitScreenTask() controller.moveToDesktop(desktopModeWindowDecoration, task) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -367,7 +376,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToDesktop_fullscreenTaskDoesNotExitSplit() { - var task = setUpFullscreenTask() + val task = setUpFullscreenTask() controller.moveToDesktop(desktopModeWindowDecoration, task) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -666,6 +675,20 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun handleRequest_recentsAnimationRunning_returnNull() { + // Set up a visible freeform task so a fullscreen task should be converted to freeform + val freeformTask = setUpFreeformTask() + markTaskVisible(freeformTask) + + // Mark recents animation running + recentsTransitionStateListener.onAnimationStateChanged(true) + + // Open a fullscreen task, check that it does not result in a WCT with changes to it + val fullscreenTask = createFullscreenTask() + assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull() + } + + @Test fun stashDesktopApps_stateUpdates() { whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index ebc284b1b77e..befc702b01aa 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -208,6 +208,30 @@ public class SplitTransitionTests extends ShellTestCase { @Test @UiThreadTest + public void testRemoteTransitionConsumed() { + // Omit side child change + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) + .addChange(TRANSIT_OPEN, mMainChild) + .build(); + TestRemoteTransition testRemote = new TestRemoteTransition(); + + IBinder transition = mSplitScreenTransitions.startEnterTransition( + TRANSIT_OPEN, new WindowContainerTransaction(), + new RemoteTransition(testRemote, "Test"), mStageCoordinator, + TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); + mMainStage.onTaskAppeared(mMainChild, createMockSurface()); + boolean accepted = mStageCoordinator.startAnimation(transition, info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), + mock(Transitions.TransitionFinishCallback.class)); + assertTrue(accepted); + + assertTrue(testRemote.isConsumed()); + + } + + @Test + @UiThreadTest public void testMonitorInSplit() { enterSplit(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index d5986780d5d8..a3af9e269209 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -285,6 +285,10 @@ public class ShellTransitionTests extends ShellTestCase { SurfaceControl.Transaction t, IBinder mergeTarget, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + } }; IBinder transitToken = new Binder(); transitions.requestStartTransition(transitToken, @@ -427,6 +431,10 @@ public class ShellTransitionTests extends ShellTestCase { SurfaceControl.Transaction t, IBinder mergeTarget, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + } }; TransitionFilter filter = new TransitionFilter(); @@ -473,6 +481,10 @@ public class ShellTransitionTests extends ShellTestCase { SurfaceControl.Transaction t, IBinder mergeTarget, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + } }; final int transitType = TRANSIT_FIRST_CUSTOM + 1; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java index 39ab23877c68..87330d2dc877 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java @@ -31,6 +31,7 @@ import android.window.WindowContainerTransaction; */ public class TestRemoteTransition extends IRemoteTransition.Stub { private boolean mCalled = false; + private boolean mConsumed = false; final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction(); @Override @@ -48,6 +49,11 @@ public class TestRemoteTransition extends IRemoteTransition.Stub { IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { } + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + mConsumed = true; + } + /** * Check whether this remote transition * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, @@ -56,4 +62,12 @@ public class TestRemoteTransition extends IRemoteTransition.Stub { public boolean isCalled() { return mCalled; } + + /** + * Check whether this remote transition's {@link #onTransitionConsumed(IBinder, boolean)} + * is called + */ + public boolean isConsumed() { + return mConsumed; + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 76bc25aa66ef..fcb7863429d6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -17,7 +17,9 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlBuilder; import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction; @@ -36,6 +38,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.LENIENT; import android.app.ActivityManager; import android.content.Context; @@ -59,10 +62,12 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.tests.R; import org.junit.Before; @@ -119,8 +124,6 @@ public class WindowDecorationTests extends ShellTestCase { private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams(); private Configuration mWindowConfiguration = new Configuration(); private int mCaptionMenuWidthId; - private int mCaptionMenuShadowRadiusId; - private int mCaptionMenuCornerRadiusId; @Before public void setUp() { @@ -131,8 +134,6 @@ public class WindowDecorationTests extends ShellTestCase { mRelayoutParams.mLayoutResId = 0; mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height; mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width; - mCaptionMenuShadowRadiusId = R.dimen.test_caption_menu_shadow_radius; - mCaptionMenuCornerRadiusId = R.dimen.test_caption_menu_corner_radius; mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius; mRelayoutParams.mCornerRadius = CORNER_RADIUS; @@ -205,12 +206,8 @@ public class WindowDecorationTests extends ShellTestCase { createMockSurfaceControlBuilder(captionContainerSurface); mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); - final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = - new ActivityManager.TaskDescription.Builder() - .setBackgroundColor(Color.YELLOW); final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() .setDisplayId(Display.DEFAULT_DISPLAY) - .setTaskDescriptionBuilder(taskDescriptionBuilder) .setBounds(TASK_BOUNDS) .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y) .setVisible(true) @@ -259,8 +256,6 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlFinishT).setCornerRadius(taskSurface, CORNER_RADIUS); verify(mMockSurfaceControlStartT) .show(taskSurface); - verify(mMockSurfaceControlStartT) - .setColor(taskSurface, new float[] {1.f, 1.f, 0.f}); verify(mMockSurfaceControlStartT).setShadowRadius(taskSurface, 10); assertEquals(300, mRelayoutResult.mWidth); @@ -417,16 +412,6 @@ public class WindowDecorationTests extends ShellTestCase { final int height = WindowDecoration.loadDimensionPixelSize( windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId); verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height); - final int shadowRadius = WindowDecoration.loadDimensionPixelSize( - windowDecor.mDecorWindowContext.getResources(), - mCaptionMenuShadowRadiusId); - verify(mMockSurfaceControlAddWindowT) - .setShadowRadius(additionalWindowSurface, shadowRadius); - final int cornerRadius = WindowDecoration.loadDimensionPixelSize( - windowDecor.mDecorWindowContext.getResources(), - mCaptionMenuCornerRadiusId); - verify(mMockSurfaceControlAddWindowT) - .setCornerRadius(additionalWindowSurface, cornerRadius); verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface); verify(mMockSurfaceControlViewHostFactory, Mockito.times(2)) .create(any(), eq(defaultDisplay), any()); @@ -516,6 +501,86 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); } + @Test + public void testRelayout_fluidResizeEnabled_freeformTask_setTaskSurfaceColor() { + StaticMockitoSession mockitoSession = mockitoSession().mockStatic( + DesktopModeStatus.class).strictness( + LENIENT).startMocking(); + when(DesktopModeStatus.isVeiledResizeEnabled()).thenReturn(false); + + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final SurfaceControl decorContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder decorContainerSurfaceBuilder = + createMockSurfaceControlBuilder(decorContainerSurface); + mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); + + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder() + .setBackgroundColor(Color.YELLOW); + + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(true) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .build(); + taskInfo.isFocused = true; + final SurfaceControl taskSurface = mock(SurfaceControl.class); + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); + + windowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlStartT).setColor(taskSurface, new float[] {1.f, 1.f, 0.f}); + + mockitoSession.finishMocking(); + } + + @Test + public void testRelayout_fluidResizeEnabled_fullscreenTask_clearTaskSurfaceColor() { + StaticMockitoSession mockitoSession = mockitoSession().mockStatic( + DesktopModeStatus.class).strictness(LENIENT).startMocking(); + when(DesktopModeStatus.isVeiledResizeEnabled()).thenReturn(false); + + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final SurfaceControl decorContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder decorContainerSurfaceBuilder = + createMockSurfaceControlBuilder(decorContainerSurface); + mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); + + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder() + .setBackgroundColor(Color.YELLOW); + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(true) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .build(); + taskInfo.isFocused = true; + final SurfaceControl taskSurface = mock(SurfaceControl.class); + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); + + windowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlStartT).unsetColor(taskSurface); + + mockitoSession.finishMocking(); + } + private TestWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) { return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer, @@ -588,13 +653,11 @@ public class WindowDecorationTests extends ShellTestCase { int y = mRelayoutParams.mCaptionY; int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId); int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId); - int shadowRadius = loadDimensionPixelSize(resources, mCaptionMenuShadowRadiusId); - int cornerRadius = loadDimensionPixelSize(resources, mCaptionMenuCornerRadiusId); String name = "Test Window"; WindowDecoration.AdditionalWindow additionalWindow = - addWindow(R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, name, + addWindow(R.layout.desktop_mode_window_decor_handle_menu, name, mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, x, y, - width, height, shadowRadius, cornerRadius); + width, height); return additionalWindow; } } diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp index 64b53cbb5c5a..4dafd0aa6df4 100644 --- a/libs/dream/lowlight/tests/Android.bp +++ b/libs/dream/lowlight/tests/Android.bp @@ -34,7 +34,7 @@ android_test { "mockito-target-extended-minus-junit4", "platform-test-annotations", "testables", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS index 6ca991d8b294..bc174599a4d3 100644 --- a/libs/hwui/OWNERS +++ b/libs/hwui/OWNERS @@ -4,9 +4,8 @@ alecmouri@google.com djsollen@google.com jreck@google.com njawad@google.com -reed@google.com scroggo@google.com -stani@google.com +sumir@google.com # For text, e.g. Typeface, Font, Minikin, etc. nona@google.com diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index 7aef7a51b90c..14639456f13b 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -593,7 +593,7 @@ namespace PaintGlue { return result; } - static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) { + static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics* metrics, bool useLocale) { const int kElegantTop = 2500; const int kElegantBottom = -1000; const int kElegantAscent = 1900; @@ -622,6 +622,17 @@ namespace PaintGlue { metrics->fLeading = size * kElegantLeading / 2048; spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading; } + + if (useLocale) { + minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); + minikin::MinikinExtent extent = + typeface->fFontCollection->getReferenceExtentForLocale(minikinPaint); + metrics->fAscent = std::min(extent.ascent, metrics->fAscent); + metrics->fDescent = std::max(extent.descent, metrics->fDescent); + metrics->fTop = std::min(metrics->fAscent, metrics->fTop); + metrics->fBottom = std::max(metrics->fDescent, metrics->fBottom); + } + return spacing; } @@ -634,7 +645,7 @@ namespace PaintGlue { MinikinUtils::getFontExtent(paint, bidiFlags, typeface, buf, start, count, bufSize); SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); metrics.fAscent = extent.ascent; metrics.fDescent = extent.descent; @@ -686,20 +697,21 @@ namespace PaintGlue { } } - static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj, + jboolean useLocale) { SkFontMetrics metrics; - SkScalar spacing = getMetricsInternal(paintHandle, &metrics); + SkScalar spacing = getMetricsInternal(paintHandle, &metrics, useLocale); GraphicsJNI::set_metrics(env, metricsObj, metrics); return SkScalarToFloat(spacing); } - static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj, + jboolean useLocale) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, useLocale); return GraphicsJNI::set_metrics_int(env, metricsObj, metrics); } - // ------------------ @CriticalNative --------------------------- static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { @@ -1002,19 +1014,19 @@ namespace PaintGlue { static jfloat ascent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); return SkScalarToFloat(metrics.fAscent); } static jfloat descent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); return SkScalarToFloat(metrics.fDescent); } static jfloat getUnderlinePosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); SkScalar position; if (metrics.hasUnderlinePosition(&position)) { return SkScalarToFloat(position); @@ -1026,7 +1038,7 @@ namespace PaintGlue { static jfloat getUnderlineThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); SkScalar thickness; if (metrics.hasUnderlineThickness(&thickness)) { return SkScalarToFloat(thickness); @@ -1121,9 +1133,9 @@ static const JNINativeMethod methods[] = { {"nSetTextLocales", "(JLjava/lang/String;)I", (void*)PaintGlue::setTextLocales}, {"nSetFontFeatureSettings", "(JLjava/lang/String;)V", (void*)PaintGlue::setFontFeatureSettings}, - {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F", + {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;Z)F", (void*)PaintGlue::getFontMetrics}, - {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I", + {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;Z)I", (void*)PaintGlue::getFontMetricsInt}, // --------------- @CriticalNative ------------------ diff --git a/libs/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp index 7df546ae0ff6..80b501da1aa5 100644 --- a/libs/securebox/tests/Android.bp +++ b/libs/securebox/tests/Android.bp @@ -32,7 +32,7 @@ android_test { "platform-test-annotations", "testables", "testng", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 230fb07f9f43..bf9419fe6603 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -875,18 +875,7 @@ public final class AudioAttributes implements Parcelable { /** * Sets the attribute describing what is the intended use of the audio signal, * such as alarm or ringtone. - * @param usage one of {@link AttributeSdkUsage#USAGE_UNKNOWN}, - * {@link AttributeSdkUsage#USAGE_MEDIA}, - * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION}, - * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING}, - * {@link AttributeSdkUsage#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION}, - * {@link AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE}, - * {@link AttributeSdkUsage#USAGE_NOTIFICATION_EVENT}, - * {@link AttributeSdkUsage#USAGE_ASSISTANT}, - * {@link AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY}, - * {@link AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, - * {@link AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION}, - * {@link AttributeSdkUsage#USAGE_GAME}. + * @param usage the usage to set. * @return the same Builder instance. */ public Builder setUsage(@AttributeSdkUsage int usage) { diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java index 0f962f9e9d4b..4e61549a5e5a 100644 --- a/media/java/android/media/AudioMetadata.java +++ b/media/java/android/media/AudioMetadata.java @@ -226,16 +226,15 @@ public final class AudioMetadata { * * An Integer value representing presentation content classifier. * - * @see AudioPresentation.ContentClassifier - * One of {@link AudioPresentation#CONTENT_UNKNOWN}, - * {@link AudioPresentation#CONTENT_MAIN}, - * {@link AudioPresentation#CONTENT_MUSIC_AND_EFFECTS}, - * {@link AudioPresentation#CONTENT_VISUALLY_IMPAIRED}, - * {@link AudioPresentation#CONTENT_HEARING_IMPAIRED}, - * {@link AudioPresentation#CONTENT_DIALOG}, - * {@link AudioPresentation#CONTENT_COMMENTARY}, - * {@link AudioPresentation#CONTENT_EMERGENCY}, - * {@link AudioPresentation#CONTENT_VOICEOVER}. + * @see AudioPresentation#CONTENT_UNKNOWN + * @see AudioPresentation#CONTENT_MAIN + * @see AudioPresentation#CONTENT_MUSIC_AND_EFFECTS + * @see AudioPresentation#CONTENT_VISUALLY_IMPAIRED + * @see AudioPresentation#CONTENT_HEARING_IMPAIRED + * @see AudioPresentation#CONTENT_DIALOG + * @see AudioPresentation#CONTENT_COMMENTARY + * @see AudioPresentation#CONTENT_EMERGENCY + * @see AudioPresentation#CONTENT_VOICEOVER */ @NonNull public static final Key<Integer> KEY_PRESENTATION_CONTENT_CLASSIFIER = createKey("presentation-content-classifier", Integer.class); diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl index 73f15f21596c..1e57be2c1e22 100644 --- a/media/java/android/media/IRingtonePlayer.aidl +++ b/media/java/android/media/IRingtonePlayer.aidl @@ -49,7 +49,7 @@ interface IRingtonePlayer { oneway void setHapticGeneratorEnabled(IBinder token, boolean hapticGeneratorEnabled); /** Used for Notification sound playback. */ - oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa); + oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa, float volume); oneway void stopAsync(); /** Return the title of the media. */ diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 0319f32521c1..8800dc865a55 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -29,7 +29,6 @@ import android.database.Cursor; import android.media.audiofx.HapticGenerator; import android.net.Uri; import android.os.RemoteException; -import android.os.SystemProperties; import android.os.Trace; import android.os.VibrationEffect; import android.os.Vibrator; @@ -767,15 +766,4 @@ public class Ringtone { @RingtoneMedia int getEnabledMedia(); VibrationEffect getVibrationEffect(); } - - /** - * Switch for using the new ringtone implementation (RingtoneV1 vs RingtoneV2). This may be - * called from both system server and app-side sdk. - * - * @hide - */ - public static boolean useRingtoneV2() { - // TODO(b/293846645): chang eto new flagging infra - return SystemProperties.getBoolean("persist.audio.ringtone.use_v2", false); - } } diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 12db8c0824db..b5a9ae27263f 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -42,6 +42,7 @@ import android.os.FileUtils; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.vibrator.Flags; import android.os.vibrator.persistence.VibrationXmlParser; import android.provider.BaseColumns; import android.provider.MediaStore; @@ -607,7 +608,7 @@ public class RingtoneManager { Ringtone ringtone; Uri positionUri = getRingtoneUri(position); - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { mPreviousRingtone = new Ringtone.Builder( mContext, mMediaType, getDefaultAudioAttributes(mType)) .setUri(positionUri) @@ -953,7 +954,7 @@ public class RingtoneManager { * @return A {@link Ringtone} for the given URI, or null. */ public static Ringtone getRingtone(final Context context, Uri ringtoneUri) { - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { return new Ringtone.Builder( context, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(-1)) .setUri(ringtoneUri) @@ -970,7 +971,7 @@ public class RingtoneManager { @Nullable VolumeShaper.Configuration volumeShaperConfig, AudioAttributes audioAttributes) { // TODO: move caller(s) away from this method: inline the builder call. - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { return new Ringtone.Builder(context, Ringtone.MEDIA_SOUND, audioAttributes) .setUri(ringtoneUri) .setVolumeShaperConfig(volumeShaperConfig) diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java index 74f72767c3f8..b74b6a3dbac9 100644 --- a/media/java/android/media/RingtoneSelection.java +++ b/media/java/android/media/RingtoneSelection.java @@ -18,16 +18,23 @@ package android.media; import static java.util.Objects.requireNonNull; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.content.ContentProvider; import android.content.ContentResolver; import android.net.Uri; +import android.os.UserHandle; +import android.os.vibrator.Flags; import android.provider.MediaStore; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Immutable representation a desired ringtone, usually originating from a user preference. @@ -46,7 +53,7 @@ import java.lang.annotation.RetentionPolicy; * to be internally consistent and reflect effective values - with the exception of not verifying * the actual URI content. For example, loading a selection Uri that sets a sound source to * {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class - * instead returning {@link #SOUND_SOURCE_DEFAULT} from {@link #getSoundSource}. + * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}. * * <h2>Storing preferences</h2> * @@ -57,6 +64,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @TestApi +@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED) public final class RingtoneSelection { /** @@ -70,7 +78,7 @@ public final class RingtoneSelection { * The sound source is not explicitly specified, so it can follow default behavior for its * context. */ - public static final int SOUND_SOURCE_DEFAULT = 0; + public static final int SOUND_SOURCE_UNSPECIFIED = 0; /** * Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker. @@ -83,15 +91,25 @@ public final class RingtoneSelection { public static final int SOUND_SOURCE_URI = 2; /** + * The sound should explicitly use the system default. + * + * <p>This value isn't valid within the system default itself. + */ + public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; + + // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT. + + /** * Directive for how to make sound. * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "SOUND_SOURCE_", value = { SOUND_SOURCE_UNKNOWN, - SOUND_SOURCE_DEFAULT, + SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_OFF, SOUND_SOURCE_URI, + SOUND_SOURCE_SYSTEM_DEFAULT, }) public @interface SoundSource {} @@ -106,9 +124,9 @@ public final class RingtoneSelection { /** * Vibration source is not explicitly specified. If vibration is enabled, this will use the * first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL}, - * {@link #VIBRATION_SOURCE_APPLICATION_PROVIDED}, or system default vibration. + * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. */ - public static final int VIBRATION_SOURCE_DEFAULT = 0; + public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; /** Specifies that vibration is explicitly disabled for this ringtone. */ public static final int VIBRATION_SOURCE_OFF = 1; @@ -117,22 +135,31 @@ public final class RingtoneSelection { public static final int VIBRATION_SOURCE_URI = 2; /** + * The vibration should explicitly use the system default. + * + * <p>This value isn't valid within the system default itself. + */ + public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; + + /** * Specifies that vibration should use the vibration provided by the application. This is * typically the application's own default for the use-case, provided via * {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration * effect saved on the notification channel. * * <p>If no vibration is specified by the application, this value behaves if the source was - * {@link #VIBRATION_SOURCE_DEFAULT}. + * {@link #VIBRATION_SOURCE_UNSPECIFIED}. + * + * <p>This value isn't valid within the system default. */ - public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3; + public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; /** * Specifies that vibration should use haptic audio channels from the * sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified - * by {@link #VIBRATION_SOURCE_DEFAULT}. + * by {@link #VIBRATION_SOURCE_UNSPECIFIED}. */ - // Numeric gap from VIBRATION_SOURCE_APPLICATION_PROVIDED in case we want other common elements. + // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements. public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; /** @@ -151,10 +178,10 @@ public final class RingtoneSelection { @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "VIBRATION_SOURCE_", value = { VIBRATION_SOURCE_UNKNOWN, - VIBRATION_SOURCE_DEFAULT, + VIBRATION_SOURCE_UNSPECIFIED, VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_URI, - VIBRATION_SOURCE_APPLICATION_PROVIDED, + VIBRATION_SOURCE_APPLICATION_DEFAULT, VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR, }) @@ -162,9 +189,12 @@ public final class RingtoneSelection { /** * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri - * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF}. - * This behavior is particularly suited to loading values from older settings that may contain - * a raw sound Uri or null for silent. + * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF}, + * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning + * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}. + * + * <p>This behavior is particularly suited to loading values from older settings that may + * contain a raw sound Uri or null for silent. * * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false. */ @@ -173,7 +203,8 @@ public final class RingtoneSelection { /** * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration * Uri for the returned {@link RingtoneSelection}, with null meaning - * {@link #VIBRATION_SOURCE_OFF}. + * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs + * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. * * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false. */ @@ -182,7 +213,9 @@ public final class RingtoneSelection { /** * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid * value. Null or an invalid values will revert to default behavior correspnoding to - * {@link #DEFAULT_SELECTION_URI_STRING}. + * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs + * ({@link RingtoneManager#getDefaultUri}) will set both + * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. * * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false, * which include {@code null}. @@ -218,10 +251,11 @@ public final class RingtoneSelection { /* Common param values */ private static final String SOURCE_OFF_STRING = "off"; + private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys"; /* Vibration source param values. */ private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac"; - private static final String VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING = "app"; + private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app"; private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg"; @Nullable @@ -236,53 +270,45 @@ public final class RingtoneSelection { private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource, @Nullable Uri vibrationUri, @VibrationSource int vibrationSource) { - // Enforce guarantees on the source values: revert to unset if they depend on something - // that's not set. - switch (soundSource) { - case SOUND_SOURCE_URI: - case SOUND_SOURCE_UNKNOWN: // Allow unknown to revert to URI before default. - mSoundSource = soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_DEFAULT; - break; - default: - mSoundSource = soundSource; - break; - } - switch (vibrationSource) { - case VIBRATION_SOURCE_AUDIO_CHANNEL: - case VIBRATION_SOURCE_HAPTIC_GENERATOR: - mVibrationSource = soundUri != null ? vibrationSource : VIBRATION_SOURCE_DEFAULT; - break; - case VIBRATION_SOURCE_URI: - case VIBRATION_SOURCE_UNKNOWN: // Allow unknown to revert to URI. - mVibrationSource = - vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_DEFAULT; - break; - default: - mVibrationSource = vibrationSource; - break; - } + // Enforce guarantees on the source values: revert to unspecified if they depend on + // something that's not set. + // + // The non-public "unknown" value can't appear in a getter result, it's just a reserved + // "null" value and should be treated the same as an unrecognized value. This can be seen + // in Uri parsing. For this and other unrecognized values, we either revert them to the URI + // source, if a Uri was included, or the "unspecified" source otherwise. This can be + // seen in action in the Uri parsing. + // + // The "unspecified" source is a public value meaning that there is no specific + // behavior indicated, and the defaults and fallbacks should be applied. For example, an + // vibration source value of "system default" means to explicitly use the system default + // vibration. However, an "unspecified" vibration source will first see if audio coupled + // or application-default vibrations are available. + mSoundSource = switch (soundSource) { + // Supported explicit values that don't have a Uri. + case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT -> + soundSource; + // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to + // unspecified. + default -> + soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED; + }; + mVibrationSource = switch (vibrationSource) { + // Enforce vibration sources that require a sound Uri. + case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR -> + soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED; + // Supported explicit values that don't rely on any Uri. + case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED, + VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT -> + vibrationSource; + // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to + // unspecified. + default -> + vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED; + }; // Clear Uri values if they're un-used by the source. - switch (mSoundSource) { - case SOUND_SOURCE_OFF: - mSoundUri = null; - break; - default: - // Unset case isn't handled here: the defaulting behavior is left to the player. - mSoundUri = soundUri; - break; - } - switch (mVibrationSource) { - case VIBRATION_SOURCE_OFF: - case VIBRATION_SOURCE_APPLICATION_PROVIDED: - case VIBRATION_SOURCE_AUDIO_CHANNEL: - case VIBRATION_SOURCE_HAPTIC_GENERATOR: - mVibrationUri = null; - break; - default: - // Unset case isn't handled here: the defaulting behavior is left to the player. - mVibrationUri = vibrationUri; - break; - } + mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null; + mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null; } /** @@ -360,18 +386,83 @@ public final class RingtoneSelection { } // Any URI content://media/ringtone return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) - && MediaStore.AUTHORITY.equals(uri.getAuthority()) + && MediaStore.AUTHORITY.equals( + ContentProvider.getAuthorityWithoutUserId(uri.getAuthority())) && MEDIA_URI_RINGTONE_PATH.equals(uri.getPath()); } + /** + * Strip the specified userId from internal Uris. Non-stripped userIds will typically be + * for work profiles referencing system ringtones from the host user. + * + * This is only for use in RingtoneManager. + * @hide + */ + @VisibleForTesting + public RingtoneSelection getWithoutUserId(int userIdToStrip) { + if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) { + return this; + } + // Ok if uri is null. We only replace explicit references to the specified (current) userId. + int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL); + int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL); + boolean needToChangeSound = + soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip; + boolean needToChangeVibration = + vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip; + + // Anything to do? + if (!needToChangeSound && !needToChangeVibration) { + return this; + } + + RingtoneSelection.Builder updated = new Builder(this); + // The relevant uris can't be null, because they contain userIdToStrip. + if (needToChangeSound) { + // mSoundUri is not null, so the result of getUriWithoutUserId won't be null. + updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri)); + } + if (needToChangeVibration) { + updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri)); + } + return updated.build(); + } + + @Override + public String toString() { + return toUri().toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof RingtoneSelection other)) { + return false; + } + return this.mSoundSource == other.mSoundSource + && this.mVibrationSource == other.mVibrationSource + && Objects.equals(this.mSoundUri, other.mSoundUri) + && Objects.equals(this.mVibrationUri, other.mVibrationUri); + } + + @Override + public int hashCode() { + return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri); + } /** * Converts a Uri into a RingtoneSelection. * - * <p>Null values and Uris that {@link #isRingtoneSelectionUri(Uri)} returns false will be - * treated according to the behaviour specified by the {@code unrecognizedValueBehavior} - * parameter. + * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for + * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated + * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter. + * + * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1, + * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and + * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. * * @param uri The Uri to convert, potentially null. * @param unrecognizedValueBehavior indicates how to treat values for which @@ -384,21 +475,35 @@ public final class RingtoneSelection { if (isRingtoneSelectionUri(uri)) { return parseRingtoneSelectionUri(uri); } + // Old symbolic default URIs map to the sources suggested by the unrecognized behavior. + // It doesn't always map to both sources because the app may have its own default behavior + // to apply, so non-primary sources are left as unspecified, which will revert to the + // system default in the absence of an app default. + boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0; RingtoneSelection.Builder builder = new RingtoneSelection.Builder(); switch (unrecognizedValueBehavior) { case FROM_URI_RINGTONE_SELECTION_ONLY: - // Always return use-defaults for unrecognized ringtone selection Uris. + if (isDefaultUri) { + builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT); + builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT); + } + // Always return unspecified (defaults) for unrecognized ringtone selection Uris. return builder.build(); case FROM_URI_RINGTONE_SELECTION_OR_SOUND: if (uri == null) { return builder.setSoundSource(SOUND_SOURCE_OFF).build(); + } else if (isDefaultUri) { + return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build(); } else { return builder.setSoundSource(uri).build(); } case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION: if (uri == null) { return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build(); + } else if (isDefaultUri) { + return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build(); } else { + // Unlike sound, there's no legacy settings alias uri for the default. return builder.setVibrationSource(uri).build(); } default: @@ -407,7 +512,12 @@ public final class RingtoneSelection { } } - /** Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}. */ + /** + * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}. + * As a special case to be more compatible, if the RingtoneSelection has a userId specified in + * the authority, then this is pushed down into the sound and vibration Uri, as the selection + * itself is agnostic. + */ @NonNull private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) { RingtoneSelection.Builder builder = new RingtoneSelection.Builder(); @@ -416,19 +526,39 @@ public final class RingtoneSelection { uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE)); Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI); Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI); + + // Add userId if necessary. This won't override an existing one in the sound/vib Uris. + int userIdFromAuthority = ContentProvider.getUserIdFromAuthority( + uri.getAuthority(), UserHandle.USER_NULL); + if (userIdFromAuthority != UserHandle.USER_NULL) { + // Won't override existing user id. + if (soundUri != null) { + soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority); + } + if (vibrationUri != null) { + vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority); + } + } + + // Set sound uri if present, but map system default Uris to the system default source. if (soundUri != null) { - builder.setSoundSource(soundUri); + if (RingtoneManager.getDefaultType(uri) >= 0) { + builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT); + } else { + builder.setSoundSource(soundUri); + } } if (vibrationUri != null) { builder.setVibrationSource(vibrationUri); } + // Don't set the source if there's a URI and the source is default, because that will // override the Uri source set above. In effect, we prioritise "explicit" sources over // an implicit Uri source - except for "default", which isn't really explicit. - if (soundSource != SOUND_SOURCE_DEFAULT || soundUri == null) { + if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) { builder.setSoundSource(soundSource); } - if (vibrationSource != VIBRATION_SOURCE_DEFAULT || vibrationUri == null) { + if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) { builder.setVibrationSource(vibrationSource); } return builder.build(); @@ -450,26 +580,28 @@ public final class RingtoneSelection { */ @Nullable private static String soundSourceToString(@SoundSource int soundSource) { - switch (soundSource) { - case SOUND_SOURCE_OFF: return SOURCE_OFF_STRING; - default: return null; - } + return switch (soundSource) { + case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING; + case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING; + default -> null; + }; } /** * Returns the sound source int corresponding to the query string value. Returns * {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and - * {@link #SOUND_SOURCE_DEFAULT} if the value is {@code null} (not in the Uri). + * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri). */ @SoundSource private static int stringToSoundSource(@Nullable String soundSource) { if (soundSource == null) { - return SOUND_SOURCE_DEFAULT; - } - switch (soundSource) { - case SOURCE_OFF_STRING: return SOUND_SOURCE_OFF; - default: return SOUND_SOURCE_UNKNOWN; + return SOUND_SOURCE_UNSPECIFIED; } + return switch (soundSource) { + case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF; + case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT; + default -> SOUND_SOURCE_UNKNOWN; + }; } /** @@ -478,30 +610,31 @@ public final class RingtoneSelection { */ @Nullable private static String vibrationSourceToString(@VibrationSource int vibrationSource) { - switch (vibrationSource) { - case VIBRATION_SOURCE_OFF: return SOURCE_OFF_STRING; - case VIBRATION_SOURCE_AUDIO_CHANNEL: return VIBRATION_SOURCE_AUDIO_CHANNEL_STRING; - case VIBRATION_SOURCE_HAPTIC_GENERATOR: - return VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING; - case VIBRATION_SOURCE_APPLICATION_PROVIDED: - return VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING; - default: return null; - } + return switch (vibrationSource) { + case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING; + case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING; + case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING; + case VIBRATION_SOURCE_APPLICATION_DEFAULT -> + VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING; + case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING; + default -> null; + }; } @VibrationSource private static int stringToVibrationSource(@Nullable String vibrationSource) { if (vibrationSource == null) { - return VIBRATION_SOURCE_DEFAULT; - } - switch (vibrationSource) { - case SOURCE_OFF_STRING: return VIBRATION_SOURCE_OFF; - case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING: return VIBRATION_SOURCE_AUDIO_CHANNEL; - case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING: return VIBRATION_SOURCE_HAPTIC_GENERATOR; - case VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING: - return VIBRATION_SOURCE_APPLICATION_PROVIDED; - default: return VIBRATION_SOURCE_UNKNOWN; + return VIBRATION_SOURCE_UNSPECIFIED; } + return switch (vibrationSource) { + case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF; + case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT; + case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL; + case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR; + case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING -> + VIBRATION_SOURCE_APPLICATION_DEFAULT; + default -> VIBRATION_SOURCE_UNKNOWN; + }; } /** @@ -512,12 +645,13 @@ public final class RingtoneSelection { public static final class Builder { private Uri mSoundUri; private Uri mVibrationUri; - @SoundSource private int mSoundSource = SOUND_SOURCE_DEFAULT; - @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_DEFAULT; + @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED; + @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED; /** * Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its - * sound and vibration source unset, which means they would fall back to system defaults. + * sound and vibration source unspecified, which means they would fall back to app/system + * defaults. */ public Builder() {} @@ -559,7 +693,9 @@ public final class RingtoneSelection { */ @NonNull public Builder setSoundSource(@NonNull Uri soundUri) { - mSoundUri = requireNonNull(soundUri); + // getCanonicalUri shouldn't return null. If it somehow did, then the + // RingtoneSelection constructor will revert this to unspecified. + mSoundUri = requireNonNull(soundUri).getCanonicalUri(); mSoundSource = SOUND_SOURCE_URI; return this; } @@ -587,7 +723,9 @@ public final class RingtoneSelection { */ @NonNull public Builder setVibrationSource(@NonNull Uri vibrationUri) { - mVibrationUri = requireNonNull(vibrationUri); + // getCanonicalUri shouldn't return null. If it somehow did, then the + // RingtoneSelection constructor will revert this to unspecified. + mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri(); mVibrationSource = VIBRATION_SOURCE_URI; return this; } diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index 83d2b613f80a..a354f9180e09 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -15,8 +15,15 @@ flag { } flag { - namespace: "media_solutions" - name: "enable_audio_policies_device_and_bluetooth_controller" - description: "Use Audio Policies implementation for device and Bluetooth route controllers." - bug: "280576228" + namespace: "media_solutions" + name: "enable_audio_policies_device_and_bluetooth_controller" + description: "Use Audio Policies implementation for device and Bluetooth route controllers." + bug: "280576228" +} + +flag { + namespace: "media_solutions" + name: "disable_screen_off_broadcast_receiver" + description: "Disables the broadcast receiver that prevents scanning when the screen is off." + bug: "304234628" } diff --git a/media/java/android/media/tv/SectionRequest.java b/media/java/android/media/tv/SectionRequest.java index 078e83222e4e..ec0d7f7a2ce4 100644 --- a/media/java/android/media/tv/SectionRequest.java +++ b/media/java/android/media/tv/SectionRequest.java @@ -81,7 +81,7 @@ public final class SectionRequest extends BroadcastInfoRequest implements Parcel /** * Gets the version number of requested session. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/java/android/media/tv/SectionResponse.java b/media/java/android/media/tv/SectionResponse.java index f38ea9dfac99..10333fe424a6 100644 --- a/media/java/android/media/tv/SectionResponse.java +++ b/media/java/android/media/tv/SectionResponse.java @@ -76,7 +76,7 @@ public final class SectionResponse extends BroadcastInfoResponse implements Parc /** * Gets the Version number of requested session. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java index d9587f6ac089..06df07fbc899 100644 --- a/media/java/android/media/tv/TableRequest.java +++ b/media/java/android/media/tv/TableRequest.java @@ -129,7 +129,7 @@ public final class TableRequest extends BroadcastInfoRequest implements Parcelab /** * Gets the version number of requested table. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java index c4fc26ef1932..1daf452fa422 100644 --- a/media/java/android/media/tv/TableResponse.java +++ b/media/java/android/media/tv/TableResponse.java @@ -269,7 +269,7 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel /** * Gets the version number of requested table. If it is null, value will be -1. * <p>The consistency of version numbers between request and response depends on - * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be * different from the version of the request. Otherwise, response with a different version from * its request will be considered invalid. diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp index ca20225e8885..bdd7afeb2f65 100644 --- a/media/tests/MediaFrameworkTest/Android.bp +++ b/media/tests/MediaFrameworkTest/Android.bp @@ -22,7 +22,7 @@ android_test { "android-ex-camera2", "testables", "testng", - "truth-prebuilt", + "truth", ], jni_libs: [ "libdexmakerjvmtiagent", diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp index 4cccf8972798..61b18c88e734 100644 --- a/media/tests/MediaRouter/Android.bp +++ b/media/tests/MediaRouter/Android.bp @@ -24,7 +24,7 @@ android_test { "compatibility-device-util-axt", "mockito-target-minus-junit4", "testng", - "truth-prebuilt", + "truth", ], test_suites: ["general-tests"], platform_apis: true, diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp index e313c46d1973..48cd8b69ade8 100644 --- a/media/tests/projection/Android.bp +++ b/media/tests/projection/Android.bp @@ -30,7 +30,7 @@ android_test { "platform-test-annotations", "testng", "testables", - "truth-prebuilt", + "truth", "platform-compat-test-rules", ], diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml index 7ca172e2fbe9..4eb8de6be5e1 100644 --- a/packages/CompanionDeviceManager/res/values-pl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -37,7 +37,7 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Zezwolić urządzeniu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> na wykonanie tego działania?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o uprawnienia do strumieniowego odtwarzania treści i innych funkcji systemowych na urządzeniach w pobliżu"</string> <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string> - <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak nazwa osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string> + <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak imię i nazwisko osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string> <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string> <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string> <string name="consent_cancel" msgid="5655005528379285841">"Anuluj"</string> diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml index 118a77c2f73c..c25fa99513d1 100644 --- a/packages/CredentialManager/res/values-hu/strings.xml +++ b/packages/CredentialManager/res/values-hu/strings.xml @@ -70,7 +70,7 @@ <string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"Elvetés"</string> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett azonosítókulcsot használni?"</string> <string name="get_dialog_title_use_password_for" msgid="625828023234318484">"Szeretné az elmentett jelszavát használni a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz?"</string> - <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"Szeretné használni a következőhöz tartozó bejelentkezési adatait: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"Szeretné használni bejelentkezési adatait a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"Feloldja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> bejelentkezési lehetőségeit?"</string> <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Mentett azonosítókulcs kiválasztása a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz"</string> <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Mentett jelszó kiválasztása a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz"</string> diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml index ed42f3afcfe0..8125ec6b053f 100644 --- a/packages/CredentialManager/res/values-ka/strings.xml +++ b/packages/CredentialManager/res/values-ka/strings.xml @@ -70,12 +70,12 @@ <string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"დახურვა"</string> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"გსურთ თქვენი დამახსოვრებული წვდომის გასაღების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_password_for" msgid="625828023234318484">"გამოიყენებთ შენახულ პაროლს <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის?"</string> - <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"გსურთ შესვლის <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის გამოყენება?"</string> + <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"გსურთ შესვლის <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის გამოყენება?"</string> <string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"გსურთ შესვლის ვარიანტების განბლოკვა <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის?"</string> <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"აირჩიეთ შენახული წვდომის გასაღები <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string> <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"აირჩიეთ შენახული პაროლი <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string> <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"აირჩიეთ სისტემაში შესვლის ინფორმაცია <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string> - <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"აირჩიეთ შესვლა <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string> + <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"აირჩიეთ შესვლა <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის"</string> <string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"გსურთ აირჩიოთ ვარიანტი <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის?"</string> <string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"გსურთ ამ ინფორმაციის გამოყენება <xliff:g id="APP_NAME">%1$s</xliff:g>-ში?"</string> <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"სხვა ხერხით შესვლა"</string> diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml index b4c567077859..e2de2efb60d8 100644 --- a/packages/CredentialManager/res/values-ky/strings.xml +++ b/packages/CredentialManager/res/values-ky/strings.xml @@ -70,12 +70,12 @@ <string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"Жабуу"</string> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү үчүн сакталган ачкычты колдоносузбу?"</string> <string name="get_dialog_title_use_password_for" msgid="625828023234318484">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган сырсөздү колдоносузбу?"</string> - <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү жолун тандайсызбы?"</string> + <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна төмөнкү аккаунт менен киресизби?"</string> <string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн кирүү параметрлеринин кулпусу ачылсынбы?"</string> <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган киргизүүчү ачкычты тандаңыз"</string> <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган сырсөздү тандаңыз"</string> <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн кирүү маалыматын тандаңыз"</string> - <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү жолун тандаңыз"</string> + <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кайсы аккаунт менен киресиз:"</string> <string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн параметр тандайсызбы?"</string> <string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"Бул маалыматты <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда пайдаланасызбы?"</string> <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Башка жол менен кирүү"</string> diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml index 15a668a73d1f..222b8654d619 100644 --- a/packages/CredentialManager/res/values-zh-rCN/strings.xml +++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml @@ -70,7 +70,7 @@ <string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"忽略"</string> <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用您为“<xliff:g id="APP_NAME">%1$s</xliff:g>”保存的通行密钥吗?"</string> <string name="get_dialog_title_use_password_for" msgid="625828023234318484">"要使用已保存的密码登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”吗?"</string> - <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"使用您的<xliff:g id="APP_NAME">%1$s</xliff:g>登录凭据?"</string> + <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"是否使用您的<xliff:g id="APP_NAME">%1$s</xliff:g>登录凭据继续?"</string> <string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"要解锁“<xliff:g id="APP_NAME">%1$s</xliff:g>”的登录选项吗?"</string> <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"选择一个已保存的通行密钥来登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string> <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"选择一个已保存的密码来登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string> diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml index f14a5ce3bfd0..c09bf86d3516 100644 --- a/packages/CredentialManager/res/values-zh-rTW/strings.xml +++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="4539824758261855508">"憑證管理工具"</string> + <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string> <string name="string_cancel" msgid="6369133483981306063">"取消"</string> <string name="string_continue" msgid="1346732695941131882">"繼續"</string> <string name="string_more_options" msgid="2763852250269945472">"儲存其他方式"</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 473d7b6f32df..477e61d2a7b1 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -61,14 +61,20 @@ import androidx.credentials.provider.PasswordCredentialEntry import androidx.credentials.provider.PublicKeyCredentialEntry import androidx.credentials.provider.RemoteEntry import org.json.JSONObject +import android.credentials.flags.Flags import java.time.Instant + fun getAppLabel( pm: PackageManager, appPackageName: String ): String? { return try { - val pkgInfo = getPackageInfo(pm, appPackageName) + val pkgInfo = if (Flags.instantAppsEnabled()) { + getPackageInfo(pm, appPackageName) + } else { + pm.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0)) + } val applicationInfo = checkNotNull(pkgInfo.applicationInfo) applicationInfo.loadSafeLabel( pm, 0f, @@ -91,7 +97,14 @@ private fun getServiceLabelAndIcon( // Test data has only package name not component name. // For test data usage only. try { - val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName) + val pkgInfo = if (Flags.instantAppsEnabled()) { + getPackageInfo(pm, providerFlattenedComponentName) + } else { + pm.getPackageInfo( + providerFlattenedComponentName, + PackageManager.PackageInfoFlags.of(0) + ) + } val applicationInfo = checkNotNull(pkgInfo.applicationInfo) providerLabel = applicationInfo.loadSafeLabel( @@ -115,7 +128,14 @@ private fun getServiceLabelAndIcon( // Added for mdoc use case where the provider may not need to register a service and // instead only relies on the registration api. try { - val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName) + val pkgInfo = if (Flags.instantAppsEnabled()) { + getPackageInfo(pm, providerFlattenedComponentName) + } else { + pm.getPackageInfo( + component.packageName, + PackageManager.PackageInfoFlags.of(0) + ) + } val applicationInfo = checkNotNull(pkgInfo.applicationInfo) providerLabel = applicationInfo.loadSafeLabel( @@ -143,12 +163,12 @@ private fun getPackageInfo( pm: PackageManager, packageName: String ): PackageInfo { - val flags = PackageManager.MATCH_INSTANT + val packageManagerFlags = PackageManager.MATCH_INSTANT return pm.getPackageInfo( packageName, PackageManager.PackageInfoFlags.of( - (flags).toLong()) + (packageManagerFlags).toLong()) ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index ba88484518aa..9355517d8bf9 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -18,21 +18,31 @@ package com.android.credentialmanager.autofill import android.app.assist.AssistStructure import android.content.Context -import android.credentials.GetCredentialRequest import android.credentials.CredentialManager -import android.credentials.GetCandidateCredentialsResponse import android.credentials.CredentialOption import android.credentials.GetCandidateCredentialsException +import android.credentials.GetCandidateCredentialsResponse +import android.credentials.GetCredentialRequest import android.os.Bundle import android.os.CancellationSignal import android.os.OutcomeReceiver -import android.service.autofill.FillRequest import android.service.autofill.AutofillService -import android.service.autofill.FillResponse +import android.service.autofill.Dataset +import android.service.autofill.Field import android.service.autofill.FillCallback -import android.service.autofill.SaveRequest +import android.service.autofill.FillRequest +import android.service.autofill.FillResponse +import android.service.autofill.InlinePresentation +import android.service.autofill.Presentations import android.service.autofill.SaveCallback +import android.service.autofill.SaveRequest +import android.service.credentials.CredentialProviderService import android.util.Log +import android.view.autofill.AutofillId +import org.json.JSONException +import android.widget.inline.InlinePresentationSpec +import androidx.autofill.inline.v1.InlineSuggestionUi +import com.android.credentialmanager.GetFlowUtils import org.json.JSONObject import java.util.concurrent.Executors @@ -47,11 +57,9 @@ class CredentialAutofillService : AutofillService() { private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired" private const val CRED_OPTIONS_KEY = "credentialOptions" private const val TYPE_KEY = "type" + private const val REQ_TYPE_KEY = "get" } - private val credentialManager: CredentialManager = - getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager - override fun onFillRequest( request: FillRequest, cancellationSignal: CancellationSignal, @@ -64,16 +72,24 @@ class CredentialAutofillService : AutofillService() { val getCredRequest: GetCredentialRequest? = getCredManRequest(structure) if (getCredRequest == null) { + Log.i(TAG, "No credential manager request found") callback.onFailure("No credential manager request found") return } + val credentialManager: CredentialManager = + getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse, GetCandidateCredentialsException> { override fun onResult(result: GetCandidateCredentialsResponse) { Log.i(TAG, "getCandidateCredentials onResponse") - val fillResponse: FillResponse? = convertToFillResponse(result, request) - callback.onSuccess(fillResponse) + val fillResponse = convertToFillResponse(result, request) + if (fillResponse != null) { + callback.onSuccess(fillResponse) + } else { + Log.e(TAG, "Failed to create a FillResponse from the CredentialResponse.") + callback.onFailure("No dataset was created from the CredentialResponse") + } } override fun onError(error: GetCandidateCredentialsException) { @@ -95,7 +111,74 @@ class CredentialAutofillService : AutofillService() { getCredResponse: GetCandidateCredentialsResponse, filLRequest: FillRequest ): FillResponse? { - TODO("Not yet implemented") + val providerList = GetFlowUtils.toProviderList( + getCredResponse.candidateProviderDataList, + this@CredentialAutofillService) + var totalEntryCount = 0 + providerList.forEach { provider -> + totalEntryCount += provider.credentialEntryList.size + } + val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest + val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0 + val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs + val inlinePresentationSpecsCount = inlinePresentationSpecs?.size ?: 0 + var maxItemCount = totalEntryCount + if (inlineMaxSuggestedCount > 0) { + maxItemCount = maxItemCount.coerceAtMost(inlineMaxSuggestedCount) + } + var i = 0 + val fillResponseBuilder = FillResponse.Builder() + var emptyFillResponse = true + providerList.forEach {provider -> + // TODO(b/299321128): Before iterating the list, sort the list so that + // the relevant entries don't get truncated + provider.credentialEntryList.forEach entryLoop@ {entry -> + val autofillId: AutofillId? = entry.fillInIntent?.getParcelableExtra( + CredentialProviderService.EXTRA_AUTOFILL_ID, + AutofillId::class.java) + val pendingIntent = entry.pendingIntent + if (autofillId == null || pendingIntent == null) { + return@entryLoop + } + var inlinePresentation: InlinePresentation? = null + // Create inline presentation + if (inlinePresentationSpecs != null && i < maxItemCount) { + val spec: InlinePresentationSpec + if (i < inlinePresentationSpecsCount) { + spec = inlinePresentationSpecs[i] + } else { + spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1] + } + val sliceBuilder = InlineSuggestionUi + .newContentBuilder(pendingIntent) + .setTitle(entry.userName) + inlinePresentation = InlinePresentation( + sliceBuilder.build().slice, spec, /* pinned= */ false) + } + i++ + + val dataSetBuilder = Dataset.Builder() + val presentationBuilder = Presentations.Builder() + if (inlinePresentation != null) { + presentationBuilder.setInlinePresentation(inlinePresentation) + } + fillResponseBuilder.addDataset( + dataSetBuilder + .setField( + autofillId, + Field.Builder().setPresentations( + presentationBuilder.build()) + .build()) + .setAuthentication(entry.pendingIntent.intentSender) + .setAuthenticationExtras(entry.fillInIntent.extras) + .build()) + emptyFillResponse = false + } + } + if (emptyFillResponse) { + return null + } + return fillResponseBuilder.build() } override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { @@ -129,27 +212,31 @@ class CredentialAutofillService : AutofillService() { } private fun traverseNode( - viewNode: AssistStructure.ViewNode?, + viewNode: AssistStructure.ViewNode, cmRequests: MutableList<CredentialOption> ) { - val options = getCredentialOptionsFromViewNode(viewNode) - cmRequests.addAll(options) + viewNode.autofillId?.let { + val options = getCredentialOptionsFromViewNode(viewNode, it) + cmRequests.addAll(options) + } - val children: List<AssistStructure.ViewNode>? = - viewNode?.run { + val children: List<AssistStructure.ViewNode> = + viewNode.run { (0 until childCount).map { getChildAt(it) } } - children?.forEach { childNode: AssistStructure.ViewNode -> + children.forEach { childNode: AssistStructure.ViewNode -> traverseNode(childNode, cmRequests) } } - private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?): - List<CredentialOption> { + private fun getCredentialOptionsFromViewNode( + viewNode: AssistStructure.ViewNode, + autofillId: AutofillId + ): List<CredentialOption> { // TODO(b/293945193) Replace with isCredential check from viewNode val credentialHints: MutableList<String> = mutableListOf() - if (viewNode != null && viewNode.autofillHints != null) { + if (viewNode.autofillHints != null) { for (hint in viewNode.autofillHints!!) { if (hint.startsWith(CRED_HINT_PREFIX)) { credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX)) @@ -159,25 +246,35 @@ class CredentialAutofillService : AutofillService() { val credentialOptions: MutableList<CredentialOption> = mutableListOf() for (credentialHint in credentialHints) { - convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) } + try { + convertJsonToCredentialOption(credentialHint, autofillId) + .let { credentialOptions.addAll(it) } + } catch (e: JSONException) { + Log.i(TAG, "Exception while parsing response: " + e.message) + } } return credentialOptions } - private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> { + private fun convertJsonToCredentialOption(jsonString: String, autofillId: AutofillId): + List<CredentialOption> { // TODO(b/302000646) Move this logic to jetpack so that is consistent // with building the json val credentialOptions: MutableList<CredentialOption> = mutableListOf() val json = JSONObject(jsonString) - val options = json.getJSONArray(CRED_OPTIONS_KEY) + val jsonGet = json.getJSONObject(REQ_TYPE_KEY) + val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY) for (i in 0 until options.length()) { val option = options.getJSONObject(i) - + val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)) + candidateBundle.putParcelable( + CredentialProviderService.EXTRA_AUTOFILL_ID, + autofillId) credentialOptions.add(CredentialOption( option.getString(TYPE_KEY), convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)), - convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)), + candidateBundle, option.getBoolean(SYS_PROVIDER_REQ_KEY), )) } diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp index 633f18610921..86c62ef299e4 100644 --- a/packages/ExternalStorageProvider/tests/Android.bp +++ b/packages/ExternalStorageProvider/tests/Android.bp @@ -25,7 +25,7 @@ android_test { static_libs: [ "androidx.test.rules", "mockito-target", - "truth-prebuilt", + "truth", ], certificate: "platform", diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp index 64b4c54e74ad..61a82701d155 100644 --- a/packages/FusedLocation/Android.bp +++ b/packages/FusedLocation/Android.bp @@ -47,7 +47,7 @@ android_test { test_config: "test/AndroidTest.xml", srcs: [ "test/src/**/*.java", - "src/**/*.java", // include real sources because we're forced to test this directly + "src/**/*.java", // include real sources because we're forced to test this directly ], libs: [ "android.test.base", @@ -60,9 +60,9 @@ android_test { "androidx.test.ext.junit", "androidx.test.ext.truth", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], platform_apis: true, certificate: "platform", - test_suites: ["device-tests"] + test_suites: ["device-tests"], } diff --git a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm index ad3199ff2dc8..d9338b1fdab7 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm @@ -337,7 +337,7 @@ key M { label: 'M' base: 'm' shift, capslock: 'M' - shift+capslock: 'n' + shift+capslock: 'm' } key COMMA { diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm index 67449220b189..fc53cbad91db 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm @@ -12,9 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - - -type FULL +type OVERLAY ### Basic QWERTY keys ### diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml index 88bb30b34ede..7f23f747090b 100644 --- a/packages/InputDevices/res/xml/keyboard_layouts.xml +++ b/packages/InputDevices/res/xml/keyboard_layouts.xml @@ -227,13 +227,6 @@ android:label="@string/keyboard_layout_turkish" android:keyboardLayout="@raw/keyboard_layout_turkish" android:keyboardLocale="tr-Latn" - android:keyboardLayoutType="qwerty" /> - - <keyboard-layout - android:name="keyboard_layout_turkish" - android:label="@string/keyboard_layout_turkish" - android:keyboardLayout="@raw/keyboard_layout_turkish" - android:keyboardLocale="tr-Latn" android:keyboardLayoutType="turkish_q" /> <keyboard-layout diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 2d231f2f55da..8964adaf586e 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -11,50 +11,14 @@ android_library { name: "SettingsLib", static_libs: [ - "androidx.annotation_annotation", - "androidx.appcompat_appcompat", - "androidx.coordinatorlayout_coordinatorlayout", - "androidx.core_core", - "androidx.fragment_fragment", - "androidx.lifecycle_lifecycle-runtime", - "androidx.loader_loader", "androidx.localbroadcastmanager_localbroadcastmanager", - "androidx.preference_preference", - "androidx.recyclerview_recyclerview", - "com.google.android.material_material", - "iconloader", + "androidx.room_room-runtime", + "zxing-core", "WifiTrackerLibRes", - "SettingsLibDeviceStateRotationLock", - "SettingsLibDisplayUtils", - "SettingsLibEmergencyNumber", - "SettingsLibSearchWidget", - "SettingsLibUtils", - "SettingsLibWidget", + "iconloader", "setupdesign", - "zxing-core-1.7", - "androidx.room_room-runtime", - "settingslib_flags_lib", - ], - - plugins: ["androidx.room_room-compiler-plugin"], - use_resource_processor: true, - resource_dirs: ["res"], - - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], -} -// Group all the libraries with namespace "com.android.settingslib.widget", to allow SettingsLib to -// set use_resource_processor = true. -// We can remove SettingsLibWidget when all these libraries have its own namespace. -android_library { - name: "SettingsLibWidget", - visibility: ["//visibility:private"], - manifest: "AndroidManifest-SettingsLibWidget.xml", - static_libs: [ "SettingsLibActionBarShadow", "SettingsLibActionButtonsPreference", "SettingsLibAdaptiveIcon", @@ -63,6 +27,9 @@ android_library { "SettingsLibBarChartPreference", "SettingsLibButtonPreference", "SettingsLibCollapsingToolbarBaseActivity", + "SettingsLibDeviceStateRotationLock", + "SettingsLibDisplayUtils", + "SettingsLibEmergencyNumber", "SettingsLibEntityHeaderWidgets", "SettingsLibFooterPreference", "SettingsLibHelpUtils", @@ -72,31 +39,31 @@ android_library { "SettingsLibProfileSelector", "SettingsLibProgressBar", "SettingsLibRestrictedLockUtils", + "SettingsLibSearchWidget", "SettingsLibSelectorWithWidgetPreference", "SettingsLibSettingsSpinner", "SettingsLibSettingsTransition", "SettingsLibTopIntroPreference", "SettingsLibTwoTargetPreference", "SettingsLibUsageProgressBarPreference", + "SettingsLibUtils", + "settingslib_flags_lib", ], - resource_dirs: [], + plugins: ["androidx.room_room-compiler-plugin"], + use_resource_processor: true, + resource_dirs: ["res"], + + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], } // NOTE: Keep this module in sync with ./common.mk java_defaults { name: "SettingsLibDefaults", static_libs: [ - "androidx.annotation_annotation", - "androidx.appcompat_appcompat", - "androidx.coordinatorlayout_coordinatorlayout", - "androidx.core_core", - "androidx.fragment_fragment", - "androidx.lifecycle_lifecycle-runtime", - "androidx.loader_loader", - "androidx.localbroadcastmanager_localbroadcastmanager", - "androidx.preference_preference", - "androidx.recyclerview_recyclerview", "SettingsLib", ], } diff --git a/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml b/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml deleted file mode 100644 index 38a7d6ace1f8..000000000000 --- a/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?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 package="com.android.settingslib.widget" /> diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index 33aa985b32e8..4871ef3097a5 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -9,6 +9,7 @@ package { android_library { name: "SettingsLibMainSwitchPreference", + use_resource_processor: true, srcs: ["src/**/*.java"], resource_dirs: ["res"], diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java index 56b3eacf6c1f..600115545a16 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java @@ -31,12 +31,11 @@ import android.widget.TextView; import androidx.annotation.ColorInt; import com.android.settingslib.utils.BuildCompatUtils; +import com.android.settingslib.widget.mainswitch.R; import java.util.ArrayList; import java.util.List; -import com.android.settingslib.widget.mainswitch.R; - /** * MainSwitchBar is a View with a customized Switch. * This component is used as the main switch of the page @@ -77,7 +76,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec final TypedArray a = context.obtainStyledAttributes( new int[]{android.R.attr.colorAccent}); mBackgroundActivatedColor = a.getColor(0, 0); - mBackgroundColor = context.getColor(R.color.material_grey_600); + mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600); a.recycle(); } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt index 0552c408a8eb..1ad075c11985 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt @@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Delete -import androidx.compose.material.icons.outlined.Launch import androidx.compose.material.icons.outlined.WarningAmber import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.FilledTonalButton @@ -52,6 +51,7 @@ import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.framework.theme.SettingsShape import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.spa.framework.theme.divider +import androidx.compose.material.icons.automirrored.outlined.Launch data class ActionButton( val text: String, @@ -101,7 +101,9 @@ private fun RowScope.ActionButton(actionButton: ActionButton) { modifier = Modifier.size(SettingsDimension.itemIconSize), ) Box( - modifier = Modifier.padding(top = 4.dp).fillMaxHeight(), + modifier = Modifier + .padding(top = 4.dp) + .fillMaxHeight(), contentAlignment = Alignment.Center, ) { Text( @@ -129,7 +131,7 @@ private fun ActionButtonsPreview() { SettingsTheme { ActionButtons( listOf( - ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {}, + ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) {}, ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {}, ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {}, ) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt index d0a61882593c..0757df347d68 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt @@ -29,8 +29,8 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag @@ -46,6 +46,7 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme fun SettingsTextFieldPassword( value: String, label: String, + enabled: Boolean = true, onTextChange: (String) -> Unit, ) { var visibility by remember { mutableStateOf(false) } @@ -60,6 +61,7 @@ fun SettingsTextFieldPassword( keyboardType = KeyboardType.Password, imeAction = ImeAction.Send ), + enabled = enabled, trailingIcon = { Icon( imageVector = if (visibility) Icons.Outlined.VisibilityOff diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt index 62189dccc9bf..6ef45900a103 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt @@ -18,7 +18,6 @@ package com.android.settingslib.spa.widget.scaffold import androidx.appcompat.R import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.Clear import androidx.compose.material.icons.outlined.FindInPage import androidx.compose.material3.Icon @@ -31,6 +30,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.LayoutDirection import com.android.settingslib.spa.framework.compose.LocalNavController +import androidx.compose.material.icons.automirrored.outlined.ArrowBack /** Action that navigates back to last page. */ @Composable @@ -53,7 +53,7 @@ internal fun CollapseAction(onClick: () -> Unit) { private fun BackAction(contentDescription: String, onClick: () -> Unit) { IconButton(onClick) { Icon( - imageVector = Icons.Outlined.ArrowBack, + imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = contentDescription, modifier = Modifier.autoMirrored(), ) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt index aa148b022b92..9f7f040be7ce 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt @@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.material3.TabRow +import androidx.compose.material3.PrimaryTabRow import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -43,7 +43,7 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit val coroutineScope = rememberCoroutineScope() val pagerState = rememberPagerState { titles.size } - TabRow( + PrimaryTabRow( selectedTabIndex = pagerState.currentPage, modifier = Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd), containerColor = Color.Transparent, diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt index f59b0decf1e5..8d9bac64b078 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt @@ -17,8 +17,8 @@ package com.android.settingslib.spa.widget.button import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.Launch import androidx.compose.material.icons.outlined.Close -import androidx.compose.material.icons.outlined.Launch import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -43,7 +43,10 @@ class ActionButtonsTest { composeTestRule.setContent { ActionButtons( listOf( - ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {}, + ActionButton( + text = "Open", + imageVector = Icons.AutoMirrored.Outlined.Launch + ) {}, ) ) } @@ -57,7 +60,7 @@ class ActionButtonsTest { composeTestRule.setContent { ActionButtons( listOf( - ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) { + ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) { clicked = true }, ) @@ -74,7 +77,10 @@ class ActionButtonsTest { composeTestRule.setContent { ActionButtons( listOf( - ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {}, + ActionButton( + text = "Open", + imageVector = Icons.AutoMirrored.Outlined.Launch + ) {}, ActionButton(text = "Close", imageVector = Icons.Outlined.Close) {}, ) ) diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp index 4031cd7f7a6f..639d1a7a7f55 100644 --- a/packages/SettingsLib/Spa/testutils/Android.bp +++ b/packages/SettingsLib/Spa/testutils/Android.bp @@ -31,7 +31,7 @@ android_library { "androidx.compose.ui_ui-test-manifest", "androidx.lifecycle_lifecycle-runtime-testing", "mockito-kotlin2", - "truth-prebuilt", + "truth", ], kotlincflags: [ "-Xjvm-default=all", diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt index dfd8f6b8e373..352503742b9c 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt @@ -18,8 +18,10 @@ package com.android.settingslib.spaprivileged.model.enterprise import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER +import android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER import android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER import android.content.Context +import android.content.pm.UserInfo import com.android.settingslib.R class EnterpriseRepository(private val context: Context) { @@ -30,8 +32,10 @@ class EnterpriseRepository(private val context: Context) { fun getEnterpriseString(updatableStringId: String, resId: Int): String = checkNotNull(resources.getString(updatableStringId) { context.getString(resId) }) - fun getProfileTitle(isManagedProfile: Boolean): String = if (isManagedProfile) { + fun getProfileTitle(userInfo: UserInfo): String = if (userInfo.isManagedProfile) { getEnterpriseString(WORK_CATEGORY_HEADER, R.string.category_work) + } else if (userInfo.isPrivateProfile) { + getEnterpriseString(PRIVATE_CATEGORY_HEADER, R.string.category_private) } else { getEnterpriseString(PERSONAL_CATEGORY_HEADER, R.string.category_personal) } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt index 6a76c93ac9a9..5447f21fd417 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt @@ -45,7 +45,7 @@ fun UserProfilePager(content: @Composable (userGroup: UserGroup) -> Unit) { val enterpriseRepository = EnterpriseRepository(context) userGroups.map { userGroup -> enterpriseRepository.getProfileTitle( - isManagedProfile = userGroup.userInfos.first().isManagedProfile, + userGroup.userInfos.first(), ) } } diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig index d1bcb5746414..4936f882f91e 100644 --- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig +++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig @@ -5,4 +5,11 @@ flag { namespace: "media_solutions" description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation." bug: "192657812" +} + +flag { + name: "enable_tv_media_output_dialog" + namespace: "tv_system_ui" + description: "Gates all the changes for the tv specific media output dialog" + bug: "303205631" }
\ No newline at end of file diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index d9596569a0da..431fd44cd952 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -18,18 +18,5 @@ # to the corresponding module. # NOTE: keep this file and ./Android.bp in sync. -LOCAL_STATIC_JAVA_LIBRARIES += \ - androidx.annotation_annotation - LOCAL_STATIC_ANDROID_LIBRARIES += \ - androidx.appcompat_appcompat \ - androidx.coordinatorlayout_coordinatorlayout \ - androidx.core_core \ - androidx.fragment_fragment \ - androidx.lifecycle_lifecycle-runtime \ - androidx.loader_loader \ - androidx.localbroadcastmanager_localbroadcastmanager \ - androidx.preference_preference \ - androidx.recyclerview_recyclerview \ SettingsLib - diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 53db19207947..afdb92b1bbab 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi twee stawe."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi drie stawe."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi-sein vol."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Gekoppel aan jou toestel."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Oop netwerk"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Veilige netwerk"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android-bedryfstelsel"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 77c9a970ce43..ea8491b2138c 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ሁለት የWiFi አሞሌዎች።"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ሦስት የWiFi አሞሌዎች።"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"የWiFi ምልክት ሙሉ ነው።"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ከመሣሪያዎ ጋር ተገናኝቷል።"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"አውታረ መረብ ክፈት"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ደህንነቱ የተጠበቀ አውታረ መረብ"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android ስርዓተ ክወና"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 30d011aa2b58..c29e6911396b 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"إشارة Wi-Fi تتكون من شريطين."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"إشارة Wi-Fi تتكون من ثلاثة أشرطة."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"إشارة Wi-Fi كاملة."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"تم الاتصال بجهازك."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"شبكة مفتوحة"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"شبكة محمية بكلمة مرور"</string> <string name="process_kernel_label" msgid="950292573930336765">"نظام التشغيل Android"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 31aff6a136f6..8eff4f6eba44 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ৱাই-ফাইৰ দুডাল দণ্ড।"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ৱাই-ফাইৰ তিনিডাল দণ্ড।"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ৱাই-ফাই সংকেত সৰ্বোচ্চ।"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"আপোনাৰ ডিভাইচটোৰ সৈতে সংযোগ কৰা আছে।"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"মুক্ত নেটৱৰ্ক"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"সুৰক্ষিত নেটৱৰ্ক"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index c675ce797984..dfd1b7b83204 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi iki xətdir."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi üç xətdir."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi siqnalı tamdır."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Cihaza qoşuldu."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Açıq şəbəkə"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Təhlükəsiz şəbəkə"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index f7be60fe8be2..528e96e4c747 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi signal ima dve crte."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi signal ima tri crte."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi signal je najjači."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezano je sa uređajem."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorena mreža"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Bezbedna mreža"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index d2f2851f58a0..52614b7df160 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Два слупкi Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Тры слупкi Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Поўны сігнал Wi-Fi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Устаноўлена падключэнне да прылады."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Адкрытая сетка"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Бяспечная сетка"</string> <string name="process_kernel_label" msgid="950292573930336765">"АС Android"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 2540c129dd53..b28ae6742f68 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi е с две чертички."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi е с три чертички."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Сигналът за Wi-Fi е пълен."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Установена е връзка с устройството ви."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Отворена мрежа"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Защитена мрежа"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android (ОС)"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 745b9446a1ab..c91f14ca2fad 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ওয়াই ফাই এ দুইটি দণ্ড৷"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ওয়াই ফাই এ তিনটি দণ্ড৷"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ওয়াই ফাই এ সম্পূর্ণ সিগন্যাল৷"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"আপনার ডিভাইসের সাথে কানেক্ট করা হয়েছে।"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"খোলা নেটওয়ার্ক"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"সুরক্ষিত নেটওয়ার্ক"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index cab0841dd423..aa1073d74d40 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi signal ima dvije crte."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi signal ima tri crte."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi signal je pun."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezani ste s uređajem."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorena mreža"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sigurna mreža"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index a65b9c2b60d8..ab6393a7d407 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Senyal Wi-Fi: dues barres."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Senyal Wi-Fi: tres barres."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Senyal Wi-Fi: complet."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"S\'ha connectat al teu dispositiu."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Xarxa oberta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Xarxa segura"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 96c02aed1a65..e7d524317bae 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi – dvě čárky."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi – tři čárky."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi – plný signál."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Připojeno k vašemu zařízení."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Nezabezpečená síť"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Zabezpečená síť"</string> <string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 1e4c7b7b04fb..1612ac030205 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi har to bjælker."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi har tre bjælker."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi har fuldt signal."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Der er oprettet forbindelse til din enhed."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Åbent netværk"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sikkert netværk"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 37f7e5aa732a..73a44f140cbf 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WLAN: zwei Balken"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WLAN: drei Balken"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WLAN: volle Signalstärke"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Mit deinem Gerät verbunden."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Offenes Netzwerk"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sicheres Netzwerk"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 84b8bf221c40..7dfd679ffacf 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Δύο γραμμές Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Τρεις γραμμές Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Άριστο σήμα Wi-Fi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Συνδέθηκε στη συσκευή σας."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ανοικτό δίκτυο"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Ασφαλές δίκτυο"</string> <string name="process_kernel_label" msgid="950292573930336765">"Λειτουργικό σύστημα Android"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 7298c022e702..aead40d12072 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi two bars."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi three bars."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal full."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connected to your device."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open network"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Secure network"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 7298c022e702..aead40d12072 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi two bars."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi three bars."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal full."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connected to your device."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open network"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Secure network"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 7298c022e702..aead40d12072 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi two bars."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi three bars."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal full."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connected to your device."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open network"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Secure network"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 86541fd5b294..291a68bc14fc 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dos barras de Wi-Fi"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tres barras de Wi-Fi"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Señal de Wi-Fi excelente"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Se estableció conexión con el dispositivo."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Red abierta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Red segura"</string> <string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index ef9cc03421d2..b4bc31913902 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dos barras de Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tres barras de Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Señal de Wi-Fi al máximo."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectado a tu dispositivo."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Red abierta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Red segura"</string> <string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index c8d806b21bd4..e5cbaf61fbb5 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi: kaks pulka."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi: kolm pulka."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi-signaal on tugev."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Seadmega ühendatud."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Avatud võrk"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Turvaline võrk"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index 95adf968af12..00f43a3bf51b 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -264,7 +264,7 @@ <string-array name="debug_hw_overdraw_entries"> <item msgid="1968128556747588800">"Desaktibatuta"</item> <item msgid="3033215374382962216">"Erakutsi gainidatzi diren eremuak"</item> - <item msgid="3474333938380896988">"Erakutsi daltonismorako eremuak"</item> + <item msgid="3474333938380896988">"Erakutsi deuteranomaliarako eremuak"</item> </string-array> <string-array name="app_process_limit_entries"> <item msgid="794656271086646068">"Muga estandarra"</item> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 7d76514f2ef0..90d45eb44fdc 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi sarearen bi barra."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi sarearen hiru barra."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi sarearen seinalea osoa."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Gailura konektatuta."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Sare irekia"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sare segurua"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android sistema eragilea"</string> @@ -368,7 +367,7 @@ <string name="debug_hw_overdraw" msgid="8944851091008756796">"Araztu GPU gainidazketa"</string> <string name="disable_overlays" msgid="4206590799671557143">"Desgaitu HW gainjartzeak"</string> <string name="disable_overlays_summary" msgid="1954852414363338166">"Erabili beti GPUa pantaila-muntaietarako"</string> - <string name="simulate_color_space" msgid="1206503300335835151">"Simulatu kolore-eremua"</string> + <string name="simulate_color_space" msgid="1206503300335835151">"Simulatu kolore-espazioa"</string> <string name="enable_opengl_traces_title" msgid="4638773318659125196">"Gaitu OpenGL aztarnak"</string> <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Desgaitu USB bidez audioa bideratzeko aukera"</string> <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Desgaitu USB bidezko audio-gailuetara automatikoki bideratzeko aukera"</string> @@ -441,7 +440,7 @@ <string name="picture_color_mode_desc" msgid="151780973768136200">"Erabili sRGB"</string> <string name="daltonizer_mode_disabled" msgid="403424372812399228">"Desgaituta"</string> <string name="daltonizer_mode_monochromacy" msgid="362060873835885014">"Ikusmen-monokromia"</string> - <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Daltonismoa (gorri-berdeak)"</string> + <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Deuteranomalia (gorri-berdeak)"</string> <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopia (gorri-berdeak)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (urdin-horia)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koloreen zuzenketa"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index beb0b968b338..f3f97c460780 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"دو نوار برای Wi‑Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"سه نوار برای Wi‑Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"قدرت سیگنال Wi‑Fi کامل است."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"به دستگاهتان متصل شد."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"شبکه باز"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"شبکه ایمن"</string> <string name="process_kernel_label" msgid="950292573930336765">"سیستمعامل Android"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 88723162ae7d..7f3d0f286695 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi-signaali – kaksi palkkia"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi-signaali – kolme palkkia"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Vahva Wi-Fi-signaali"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Yhdistetty laitteeseesi."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Avoin verkko"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Suojattu verkko"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android-käyttöjärjestelmä"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 98e3753376e3..b7c3a81b47cd 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi : deux barres."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi : trois barres."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi : signal complet."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connecté à votre appareil."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Réseau ouvert"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Réseau sécurisé"</string> <string name="process_kernel_label" msgid="950292573930336765">"Système d\'exploitation Android"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 4a64041192a3..a9e0c6a5ccb5 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Signal Wi-Fi moyen"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Signal Wi-Fi bon"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Signal Wi-Fi excellent"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connecté à votre appareil."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Réseau ouvert"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Réseau sécurisé"</string> <string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 26738e6f54e7..09c2969bac2d 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dúas barras de wifi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tres barras de wifi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal completo de wifi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conexión establecida co teu dispositivo."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string> <string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index b620d4184b8e..575c67510e89 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi બે બાર."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi ત્રણ બાર."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"પૂર્ણ Wifi સિગ્નલ."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"તમારા ડિવાઇસ સાથે કનેક્ટેડ છે."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"નેટવર્ક ખોલો"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"સુરક્ષિત નેટવર્ક"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 3811167c9c49..655fac041c9b 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"वाई-फ़ाई की दो पट्टी मिल रही हैं."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"वाई-फ़ाई की एक पट्टी मिल रही है."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"पूरे वाई-फ़ाई सिग्नल मिल रहे हैं."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"आपके डिवाइस से कनेक्ट हो गया है."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"खुला नेटवर्क"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"सुरक्षित नेटवर्क"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> @@ -598,7 +597,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"प्रोफ़ाइल की जानकारी"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"इससे पहले कि आप कोई प्रतिबंधित प्रोफ़ाइल बनाएं, आपको अपने ऐप्लिकेशन और व्यक्तिगत डेटा की सुरक्षा करने के लिए एक स्क्रीन लॉक सेट करना होगा."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करें"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर जाएं"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर स्विच करें"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नए उपयोगकर्ता को जोड़ा जा रहा है…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"नया मेहमान खाता बनाया जा रहा है…"</string> <string name="add_user_failed" msgid="4809887794313944872">"नया उपयोगकर्ता जोड़ा नहीं जा सका"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 71a2daeb901c..f0e16a47346f 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi signal ima dva stupca."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi signal ima tri stupca."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal je pun."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezano s vašim uređajem."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorena mreža"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sigurna mreža"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> @@ -619,7 +618,7 @@ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time će se izbrisati aplikacije i podaci iz trenutačne gostujuće sesije."</string> <string name="grant_admin" msgid="4323199171790522574">"Da, dodijeli status administratora"</string> <string name="not_grant_admin" msgid="3557849576157702485">"Ne, nemoj dodijeliti status administratora"</string> - <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izlaz"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izađi"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Želite li spremiti aktivnosti gosta?"</string> <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete spremiti aktivnosti iz ove sesije ili izbrisati sve aplikacije i podatke"</string> <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 5fac75da75ab..420e0e969fd9 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi-jel: két sáv."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi-jel: három sáv."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi-jel: teljes."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Csatlakoztatva az eszközhöz."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Nyílt hálózat"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Biztonságos hálózat"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index e40c5e164110..705fcf6637b2 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi-ի ուժգնությունը՝ երկու գիծ:"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi-ի ուժգնությունը՝ երեք գիծ:"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi-ի ազդանշանը ուժեղ է:"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Միացած է ձեր սարքին։"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Բաց ցանց"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Անվտանգ ցանց"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index ca1b6be5b478..e33dd05264c3 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi dua baris"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi tiga baris."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinyal Wi-Fi penuh."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Terhubung ke perangkat Anda."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Jaringan terbuka"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Jaringan aman"</string> <string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 1f2dcfb7c790..e1fb248604c2 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: Tvö strik."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: Þrjú strik."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Fullur Wi-Fi sendistyrkur."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Tengt við tækið þitt."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Opið net"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Öruggt net"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android stýrikerfið"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index e255c85a7d69..b2cfd371fe25 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: due barre."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: tre barre."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Segnale Wi-Fi completo."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connessione al dispositivo effettuata."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rete aperta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rete protetta"</string> <string name="process_kernel_label" msgid="950292573930336765">"Sistema operativo Android"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index edcac10e8f24..9ca74778d978 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"שני פסים של Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"שלושה פסים של Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"אות Wi-Fi מלא."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"מחובר למכשיר שלך."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"רשת פתוחה"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"רשת מאובטחת"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index bf21de291e6a..dcce2b4436aa 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fiはレベル2です。"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fiはレベル3です。"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fiの電波はフルです。"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"デバイスに接続しました。"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"オープンネットワーク"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"保護されたネットワーク"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 9000de4c990b..eb32e1ce2a8d 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi სიგნალი ორ ზოლზეა."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi სიგნალი სამ ზოლზეა."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi სიგნალი სრულია."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"დაკავშირებულია თქვენს მოწყობილობასთან."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ღია ქსელი"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"დაცული ქსელი"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 610fed1f6550..73a8efde7a97 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi сигналы — екі жолақ."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi сигналы — үш жолақ."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi сигналы толық."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Құрылғыңызға жалғанды."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ашық желі"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Қауіпсіз желі"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index b7999c73f33a..1c32e626f1b6 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi ពីរកាំ"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi បីកាំ"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"សេវា Wifi ពេញ"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"បានភ្ជាប់ទៅឧបករណ៍របស់អ្នក។"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"បើកបណ្ដាញ"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"បណ្តាញដែលមានសុវត្ថិភាព"</string> <string name="process_kernel_label" msgid="950292573930336765">"ប្រព័ន្ធប្រតិបត្តិការ Android"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index c788dd5fc8bd..ede347db975f 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ವೈಫೈ ಎರಡು ಪಟ್ಟಿಗಳು."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ವೈಫೈ ಮೂರು ಪಟ್ಟಿಗಳು."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ವೈಫೈ ಸಿಗ್ನಲ್ ಪೂರ್ತಿ ಇದೆ."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ನೆಟ್ವರ್ಕ್ ತೆರೆಯಿರಿ"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ಸುರಕ್ಷಿತ ನೆಟ್ವರ್ಕ್"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 2c51d1f9edd1..78bd616dffe2 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi 신호 막대가 두 개입니다."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi 신호 막대가 세 개입니다."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi 신호가 강합니다."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"기기에 연결되었습니다."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"개방형 네트워크"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"보안 네트워크"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 14814f4ed6e8..a0b91230713d 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi: эки таякча."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi: үч таякча."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi: күчтүү сигнал."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Түзмөгүңүзгө туташты."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ачык тармак"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Коопсуз тармак"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 6430a9889e1a..96b2dc1a27cb 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ສັນຍານ Wi-Fi ສອງຂີດ."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi ສາມຂີດ."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ສັນຍານ Wi-Fi ເຕັມ"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານແລ້ວ."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ເຄືອຂ່າຍເປີດ"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ເຄືອຂ່າຍເຂົ້າລະຫັດ"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 3642a9091545..69630f436704 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dvi „Wi-Fi“ signalo juostos."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Trys „Wi-Fi“ signalo juostos."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Stiprus „Wi-Fi“ signalas."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Prisijungta prie įrenginio."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Atviras tinklas"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Saugus tinklas"</string> <string name="process_kernel_label" msgid="950292573930336765">"„Android“ OS"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index c3163b8efe1e..4f76ceeb2cca 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: divas joslas"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: trīs joslas"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Pilna piekļuve Wi-Fi signālam"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Izveidots savienojums ar ierīci."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Atvērts tīkls"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Drošs tīkls"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 017fed7a5524..1c80c6502fc1 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Две црти на Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Три црти на Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Полн сигнал на Wi-Fi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Поврзано со уредот."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Отворена мрежа"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Заклучена мрежа"</string> <string name="process_kernel_label" msgid="950292573930336765">"Оперативен систем Android"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 3ef3f63d32b7..31e3c83fb32f 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"വൈഫൈ സിഗ്നൽ രണ്ട് ബാറുകൾ."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"വൈഫൈ സിഗ്നൽ മൂന്ന് ബാറുകൾ."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"വൈഫൈ മികച്ച സിഗ്നൽ."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്തു."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ഓപ്പൺ നെറ്റ്വര്ക്ക്"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"സുരക്ഷിത നെറ്റ്വര്ക്ക്"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index ccd1d4179ffb..5a636d9c8260 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi сүлжээний дохио хоёр баганатай байна."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi сүлжээний дохио гурван баганатай байна."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi-н дохио дүүрэн байна."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Таны төхөөрөмжид холбогдсон."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Нээлттэй сүлжээ"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Аюулгүй сүлжээ"</string> <string name="process_kernel_label" msgid="950292573930336765">"Андройд OS"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index a34a9d327969..4d78e571b47d 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"वाय-फाय दोन बार."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"वाय-फाय तीन बार."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"वाय-फाय सिग्नल संपूर्ण आहे."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"तुमच्या डिव्हाइसशी कनेक्ट केले आहे."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"नेटवर्क उघडा"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"सुरक्षित नेटवर्क"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index b7d273409390..2ae69bdff241 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi dua bar."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi tiga bar."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Isyarat Wi-Fi penuh."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Disambungkan kepada peranti anda."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rangkaian terbuka"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rangkaian selamat"</string> <string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 0e43f6516f25..7d6f2a75ba5a 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi ၂ ဘား"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi ၃ ဘား"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi အပြည့်ရှိ"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"သင့်စက်သို့ ချိတ်ဆက်ထားသည်။"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"အများသုံး ကွန်ရက်"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"လုံခြုံသည့် ကွန်ရက်"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 43824c3ebc1a..9e550fb288e9 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi-signal med to stolper."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi-signal med tre stolper."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi-signalet er ved full styrke."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Koblet til enheten."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Åpent nettverk"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sikkert nettverk"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android-operativsystem"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index e879849f5f29..1f6ad5bb863c 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi दुई पट्टि।"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi तीन बारहरू।"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"पूर्ण Wi-Fi सिंग्नल।"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"तपाईंको डिभाइसमा कनेक्ट गरिएको छ।"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"खुला नेटवर्क"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"सुरक्षित नेटवर्क"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index dedacff6daf3..6a6f184f77d1 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi: twee streepjes."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi: drie streepjes."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifii-signaal is op volledige sterkte."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Verbonden met je apparaat."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open netwerk"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Beveiligd netwerk"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android-besturingssysteem"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index cbd9ffd50771..980a37413f8b 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ୱାଇ-ଫାଇର ଦୁଇଟି ବାର୍ ଅଛି।"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ୱାଇ-ଫାଇର ତିନୋଟି ବାର୍।"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ୱାଇ-ଫାଇର ସଙ୍କେତ ସର୍ବୋଚ୍ଚ।"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରାଯାଇଛି।"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ଖୋଲା ନେଟୱର୍କ"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ସୁରକ୍ଷିତ ନେଟ୍ୱର୍କ"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index e4814acdfb0c..dd3fa15440e1 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi ਦੋ ਬਾਰ।"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi ਤਿੰਨ ਬਾਰ।"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi ਸਿਗਨਲ ਪੂਰਾ।"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ।"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ਖੁੱਲ੍ਹਾ ਨੈੱਟਵਰਕ"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index f5dc9cc7e202..f4ebd29b1276 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: dwa paski."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: trzy paski."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi: pełna moc sygnału."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Połączono z urządzeniem."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Sieć otwarta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sieć zabezpieczona"</string> <string name="process_kernel_label" msgid="950292573930336765">"System operacyjny Android"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 4e96bc615c3d..bb6c7d85fe62 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Duas barras de Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Três barras de Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal Wi-Fi cheio."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectado ao dispositivo."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string> <string name="process_kernel_label" msgid="950292573930336765">"Sistema operacional Android"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 0c4abdbefda2..6bd5c2abccda 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Duas barras de Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Três barras de Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal de Wi-Fi completo."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Com ligação ao dispositivo."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string> <string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 4e96bc615c3d..bb6c7d85fe62 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Duas barras de Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Três barras de Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal Wi-Fi cheio."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectado ao dispositivo."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string> <string name="process_kernel_label" msgid="950292573930336765">"Sistema operacional Android"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 529a6c646979..f4399dc4e829 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Semnal Wi-Fi: două bare."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Semnal Wi-Fi: trei bare."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Semnal Wi-Fi: complet."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectată la dispozitiv."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rețea nesecurizată"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Securizează rețeaua"</string> <string name="process_kernel_label" msgid="950292573930336765">"Sistem de operare Android"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 87a81ee73faf..5d45e29ef9b6 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: два деления"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: три деления"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi: надежный сигнал"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Подключено к точке доступа на вашем устройстве."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Открытая сеть"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Защищенная сеть"</string> <string name="process_kernel_label" msgid="950292573930336765">"ОС Android"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 9015a428de38..996507d8c2f6 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi තීරු දෙකයි."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi තීරු තුනයි."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi සංඥාව පිරී ඇත."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ඔබේ උපාංගයට සම්බන්ධයි."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"විවෘත ජාලය"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ආරක්ෂිත ජාලය"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 8c6bf4fcb887..a51acd4fb227 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dve čiarky signálu Wi‑Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tri čiarky signálu Wi‑Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Plný signál Wi‑Fi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Pripojené k vášmu zariadeniu."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorená sieť"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Zabezpečená sieť"</string> <string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index cc19abe8be24..0d7188dcbc50 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dve črtici signala Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tri črtice signala Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Poln signal Wi-Fi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezava z napravo je vzpostavljena."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Odprto omrežje"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Varno omrežje"</string> <string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 43a2c4c19fd1..c2bcce79b061 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi ka dy vija."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: tre vija."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi ka sinjal të plotë."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Lidhur me pajisjen tënde"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rrjet i hapur"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rrjet i sigurt"</string> <string name="process_kernel_label" msgid="950292573930336765">"Sistemi operativ Android"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 00a98bca2383..09ff994477e1 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi сигнал има две црте."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi сигнал има три црте."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi сигнал је најјачи."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Повезано је са уређајем."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Отворена мрежа"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Безбедна мрежа"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android ОС"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 01b462da0165..fdd9d8c17d57 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi: två staplar."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi: tre staplar."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Full signalstyrka för wifi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Ansluten till enheten."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Öppet nätverk"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Säkert nätverk"</string> <string name="process_kernel_label" msgid="950292573930336765">"Operativsystemet Android"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 9453e5f84c6c..bd4175289585 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Vipima mtandao viwili vya Wifi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Vipima mtandao vitatu vya Wifi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Nguvu kamili ya mtandao wa Wifi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Imeunganishwa kwenye kifaa chako."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Mtandao unaotumiwa na mtu yeyote"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Mtandao salama"</string> <string name="process_kernel_label" msgid="950292573930336765">"Mfumo wa Uendeshaji wa Android"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 836b4da9b8a7..fd379f0a80be 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"வைஃபை சிக்னல்: இரண்டு கோடுகள்."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"வைஃபை சிக்னல்: மூன்று கோடுகள்."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"வைஃபை சிக்னல் முழுமையாக உள்ளது."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"உங்கள் சாதனத்துடன் இணைக்கப்பட்டது."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"கடவுச்சொல் தேவைப்படாத திறந்த நெட்வொர்க்"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"கடவுச்சொல் தேவைப்படும் பாதுகாப்பான நெட்வொர்க்"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 34b826441dbc..3e9d8be3cc1b 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi సిగ్నల్ రెండు బార్లు ఉంది."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi సిగ్నల్ మూడు బార్లు ఉంది."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi సిగ్నల్ పూర్తిగా ఉంది."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"మీ పరికరానికి కనెక్ట్ చేయబడింది."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ఓపెన్ నెట్వర్క్"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"సురక్షిత నెట్వర్క్"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index da3a5c5d09c6..e67f37172bd7 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"สัญญาณ Wi-Fi 2 ขีด"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"สัญญาณ Wi-Fi 3 ขีด"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"สัญญาณ Wi-Fi เต็ม"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"เชื่อมต่อกับอุปกรณ์แล้ว"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"เครือข่ายแบบเปิด"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"เครือข่ายที่ปลอดภัย"</string> <string name="process_kernel_label" msgid="950292573930336765">"ระบบปฏิบัติการ Android"</string> @@ -598,7 +597,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"ข้อมูลโปรไฟล์"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"ก่อนที่คุณจะสามารถสร้างโปรไฟล์ที่ถูกจำกัดได้ คุณจะต้องตั้งค่าล็อกหน้าจอเพื่อปกป้องแอปและข้อมูลส่วนตัวของคุณ"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ตั้งค่าล็อก"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น<xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"กำลังสร้างผู้ใช้ชั่วคราวใหม่…"</string> <string name="add_user_failed" msgid="4809887794313944872">"สร้างผู้ใช้ใหม่ไม่ได้"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index f6d2256c3e4e..440bbe768c05 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"May dalawang bar ang Wifi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"May tatlong bar ang Wifi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Puno ang signal ng Wifi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Nakakonekta sa iyong device."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Bukas na network"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Ligtas na network"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index acd3d00e56e6..b38012fbbd8c 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Kablosuz sinyal gücü iki çubuk."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Kablosuz sinyal gücü üç çubuk."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Kablosuz sinyal gücü tam."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Cihazınıza bağlandı."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Açık ağ"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Güvenli ağ"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index dd5898adc3c5..4f0854790e92 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Дві смужки сигналу Wi-Fi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Три смужки сигналу Wi-Fi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Максимальний сигнал Wi-Fi."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Підключено до пристрою."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Відкрита мережа"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Захищена мережа"</string> <string name="process_kernel_label" msgid="950292573930336765">"ОС Android"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 182bd0496527..ce67d1516273 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi دو بارز۔"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi تین بارز۔"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi سگنل پورا ہے۔"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"آپ کے آلے سے منسلک ہے۔"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"اوپن نیٹ ورک"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"محفوظ نیٹ ورک"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index b831a4b40bb9..77da981b29a9 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: ikkita ustun"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: uchta ustun"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi: signal to‘liq"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Qurilmaga ulandi."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ochiq tarmoq"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Xavfsiz tarmoq"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 3e94593351a2..bf510f6522f3 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Tín hiệu Wi-Fi hai vạch."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tín hiệu Wi-Fi ba vạch."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Tín hiệu Wi-Fi đủ."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Đã kết nối với thiết bị của bạn."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Mạng mở"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Mạng bảo mật"</string> <string name="process_kernel_label" msgid="950292573930336765">"Hệ điều hành Android"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 53389c790974..8e3145ace05a 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WLAN 信号强度为两格。"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WLAN 信号强度为三格。"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WLAN 信号满格。"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"已连接到您的设备。"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"开放网络"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"安全网络"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android 操作系统"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index daedba67c378..aa9f21f28b91 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi 訊號兩格。"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi 訊號三格。"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi 訊號滿格。"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"已連接裝置。"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"開放式網絡"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"安全網絡"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android 作業系統"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 94ebd9852e85..3c65a4d7d5ee 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi 訊號強度兩格。"</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi 訊號強度三格。"</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi 訊號強度滿格。"</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"已連上裝置。"</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"開放式網路"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"安全網路"</string> <string name="process_kernel_label" msgid="950292573930336765">"Android 作業系統"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 92a4b81e80f7..08b04cc4b1bf 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -157,8 +157,7 @@ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Amabha amabili we-Wifi."</string> <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Amabha amathathu we-Wifi."</string> <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Isiginali ye-Wifi igcwele."</string> - <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) --> - <skip /> + <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Ixhume kudivayisi yakho."</string> <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Vula inethiwekhi"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Inethiwekhi evikelekile"</string> <string name="process_kernel_label" msgid="950292573930336765">"I-Android OS"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 49bd9d994088..96876747c082 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -518,6 +518,8 @@ <string name="category_personal">Personal</string> <!-- Header for items under the work user [CHAR LIMIT=30] --> <string name="category_work">Work</string> + <!-- Header for items under the private profile user [CHAR LIMIT=30] --> + <string name="category_private">Private</string> <!-- Header for items under the clone user [CHAR LIMIT=30] --> <string name="category_clone">Clone</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java deleted file mode 100644 index a78440c271c9..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.deviceinfo; - -import android.content.Context; -import android.os.UserManager; - -import com.android.settingslib.Utils; -import com.android.settingslib.core.AbstractPreferenceController; - -public abstract class AbstractSimStatusImeiInfoPreferenceController - extends AbstractPreferenceController { - public AbstractSimStatusImeiInfoPreferenceController(Context context) { - super(context); - } - - @Override - public boolean isAvailable() { - return mContext.getSystemService(UserManager.class).isAdminUser() - && !Utils.isWifiOnly(mContext); - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java index 815293e9c2a8..09f34b3c5a5d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java +++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java @@ -16,17 +16,20 @@ package com.android.settingslib.enterprise; + import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.provider.Settings; -import android.util.Log; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.settingslib.RestrictedLockUtils; -import org.jetbrains.annotations.Nullable; final class SupervisedDeviceActionDisabledByAdminController extends BaseActionDisabledByAdminController { @@ -57,8 +60,13 @@ final class SupervisedDeviceActionDisabledByAdminController @Nullable @Override - public DialogInterface.OnClickListener getPositiveButtonListener(Context context, - RestrictedLockUtils.EnforcedAdmin enforcedAdmin) { + public DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context, + @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) { + if (enforcedAdmin.component == null + || TextUtils.isEmpty(enforcedAdmin.component.getPackageName())) { + return null; + } + final Intent intent = new Intent(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING) .setData(new Uri.Builder() .scheme("policy") @@ -72,7 +80,6 @@ final class SupervisedDeviceActionDisabledByAdminController return null; } return (dialog, which) -> { - Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component); context.startActivity(intent); }; } diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index b03c43cbdee5..4b4caf5a620e 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -52,7 +52,7 @@ android_test { "flag-junit", "mockito-target-minus-junit4", "platform-test-annotations", - "truth-prebuilt", + "truth", "SettingsLibDeviceStateRotationLock", "SettingsLibSettingsSpinner", "SettingsLibUsageProgressBarPreference", diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp index dd9cb9cf7abe..2d875cf7244d 100644 --- a/packages/SettingsLib/tests/robotests/Android.bp +++ b/packages/SettingsLib/tests/robotests/Android.bp @@ -96,6 +96,6 @@ java_library { libs: [ "Robolectric_all-target_upstream", "mockito-robolectric-prebuilt", - "truth-prebuilt", + "truth", ], } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java index 2b1e8080d389..507dcbc1d8a2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java @@ -55,8 +55,7 @@ public class CollapsingCoordinatorLayoutTest { @Test public void onCreate_userAddedChildViewsBeMovedToContentFrame() { CollapsingCoordinatorLayout layout = mActivity.getCollapsingCoordinatorLayout(); - View contentFrameView = - layout.findViewById(com.android.settingslib.widget.R.id.content_frame); + View contentFrameView = layout.findViewById(R.id.content_frame); TextView textView = contentFrameView.findViewById(com.android.settingslib.robotests.R.id.text_hello_world); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java deleted file mode 100644 index 52d243a14e2f..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.deviceinfo; - -import static com.google.common.truth.Truth.assertThat; - -import static org.robolectric.shadow.api.Shadow.extract; - -import android.os.UserManager; -import android.telephony.TelephonyManager; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; - -@RunWith(RobolectricTestRunner.class) -@Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class, - SimStatusImeiInfoPreferenceControllerTest.ShadowTelephonyManager.class}) -public class SimStatusImeiInfoPreferenceControllerTest { - - private AbstractSimStatusImeiInfoPreferenceController mController; - - @Before - public void setUp() { - mController = new AbstractSimStatusImeiInfoPreferenceController( - RuntimeEnvironment.application) { - @Override - public String getPreferenceKey() { - return null; - } - }; - } - - @Test - public void testIsAvailable_isAdminAndHasMobile_shouldReturnTrue() { - ShadowUserManager userManager = - extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); - userManager.setIsAdminUser(true); - ShadowTelephonyManager telephonyManager = - extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class)); - telephonyManager.setDataCapable(true); - - assertThat(mController.isAvailable()).isTrue(); - } - - @Test - public void testIsAvailable_isAdminButNoMobile_shouldReturnFalse() { - ShadowUserManager userManager = - extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); - userManager.setIsAdminUser(true); - ShadowTelephonyManager telephonyManager = - extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class)); - telephonyManager.setDataCapable(false); - - assertThat(mController.isAvailable()).isFalse(); - } - - @Test - public void testIsAvailable_isNotAdmin_shouldReturnFalse() { - ShadowUserManager userManager = - extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); - userManager.setIsAdminUser(false); - - assertThat(mController.isAvailable()).isFalse(); - } - - @Implements(UserManager.class) - public static class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { - - private boolean mAdminUser; - - public void setIsAdminUser(boolean isAdminUser) { - mAdminUser = isAdminUser; - } - - @Implementation - public boolean isAdminUser() { - return mAdminUser; - } - } - - @Implements(TelephonyManager.class) - public static class ShadowTelephonyManager - extends org.robolectric.shadows.ShadowTelephonyManager { - private boolean mDataCapable = false; - private void setDataCapable(boolean capable) { - mDataCapable = capable; - } - - @Implementation - public boolean isDataCapable() { - return mDataCapable; - } - } -} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java index 6195d754f8f0..71545b75989a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java @@ -36,10 +36,10 @@ import android.graphics.drawable.Icon; import android.graphics.drawable.ShapeDrawable; import android.os.Bundle; -import com.android.settingslib.widget.adaptiveicon.R; import com.android.settingslib.drawer.ActivityTile; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.Tile; +import com.android.settingslib.widget.adaptiveicon.R; import org.junit.Before; import org.junit.Test; @@ -105,15 +105,15 @@ public class AdaptiveIconTest { icon.setBackgroundColor(mContext, tile); - assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor( - com.android.settingslib.widget.R.color.homepage_generic_icon_background)); + assertThat(icon.mBackgroundColor).isEqualTo( + mContext.getColor(R.color.homepage_generic_icon_background)); } @Test public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() { final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, - com.android.settingslib.widget.R.color.bt_outline_color); + R.color.bt_outline_color); doReturn(Icon.createWithResource(mContext, com.android.settingslib.R.drawable.ic_system_update)) .when(tile).getIcon(mContext); @@ -121,8 +121,7 @@ public class AdaptiveIconTest { new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); icon.setBackgroundColor(mContext, tile); - assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor( - com.android.settingslib.widget.R.color.bt_outline_color)); + assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor(R.color.bt_outline_color)); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java index ccbe4f03e80c..0ce83c6220ff 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java @@ -58,16 +58,14 @@ public class FooterPreferenceTest { @Test public void setLearnMoreText_shouldSetAsTextInLearnMore() { final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests( - LayoutInflater.from(mContext) - .inflate(com.android.settingslib.widget.R.layout.preference_footer, null)); + LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)); mFooterPreference.setLearnMoreText("Custom learn more"); mFooterPreference.setLearnMoreAction(view -> { /* do nothing */ } /* listener */); mFooterPreference.onBindViewHolder(holder); - assertThat(((TextView) holder.findViewById( - com.android.settingslib.widget.R.id.settingslib_learn_more)).getText().toString()) - .isEqualTo("Custom learn more"); + TextView learnMoreView = (TextView) holder.findViewById(R.id.settingslib_learn_more); + assertThat(learnMoreView.getText().toString()).isEqualTo("Custom learn more"); } @Test @@ -95,8 +93,7 @@ public class FooterPreferenceTest { @Test public void onBindViewHolder_whenTitleIsNull_shouldNotRaiseNpe() { PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests( - LayoutInflater.from(mContext) - .inflate(R.layout.preference_footer, null))); + LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null))); when(viewHolder.findViewById(androidx.core.R.id.title)).thenReturn(null); Throwable actualThrowable = null; @@ -112,10 +109,8 @@ public class FooterPreferenceTest { @Test public void onBindViewHolder_whenLearnMoreIsNull_shouldNotRaiseNpe() { PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests( - LayoutInflater.from(mContext) - .inflate(com.android.settingslib.widget.R.layout.preference_footer, null))); - when(viewHolder.findViewById(com.android.settingslib.widget.R.id.settingslib_learn_more)) - .thenReturn(null); + LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null))); + when(viewHolder.findViewById(R.id.settingslib_learn_more)).thenReturn(null); Throwable actualThrowable = null; try { @@ -130,8 +125,7 @@ public class FooterPreferenceTest { @Test public void onBindViewHolder_whenIconFrameIsNull_shouldNotRaiseNpe() { PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests( - LayoutInflater.from(mContext) - .inflate(com.android.settingslib.widget.R.layout.preference_footer, null))); + LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null))); when(viewHolder.findViewById(R.id.icon_frame)).thenReturn(null); Throwable actualThrowable = null; diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java deleted file mode 100644 index 0b9ba8d044ce..000000000000 --- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.testutils.shadow; - -import static android.os.Build.VERSION_CODES.O; - -import android.app.ActivityManager; -import android.app.IActivityManager; - -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.annotation.Resetter; -import org.robolectric.shadow.api.Shadow; -import org.robolectric.util.ReflectionHelpers; - -@Implements(ActivityManager.class) -public class ShadowActivityManager { - private static int sCurrentUserId = 0; - private static int sUserSwitchedTo = -1; - - @Resetter - public static void reset() { - sCurrentUserId = 0; - sUserSwitchedTo = 0; - } - - @Implementation - protected static int getCurrentUser() { - return sCurrentUserId; - } - - @Implementation - protected boolean switchUser(int userId) { - sUserSwitchedTo = userId; - return true; - } - - @Implementation(minSdk = O) - protected static IActivityManager getService() { - return ReflectionHelpers.createNullProxy(IActivityManager.class); - } - - public boolean getSwitchUserCalled() { - return sUserSwitchedTo != -1; - } - - public int getUserSwitchedTo() { - return sUserSwitchedTo; - } - - public static void setCurrentUser(int userId) { - sCurrentUserId = userId; - } - - public static ShadowActivityManager getShadow() { - return (ShadowActivityManager) Shadow.extract( - RuntimeEnvironment.application.getSystemService(ActivityManager.class)); - } -} diff --git a/packages/SettingsLib/tests/unit/Android.bp b/packages/SettingsLib/tests/unit/Android.bp index 19ab1c69e98c..6d6e2ff8e59b 100644 --- a/packages/SettingsLib/tests/unit/Android.bp +++ b/packages/SettingsLib/tests/unit/Android.bp @@ -31,6 +31,6 @@ android_test { "SettingsLib", "androidx.test.ext.junit", "androidx.test.runner", - "truth-prebuilt", + "truth", ], } diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 92ebe09fa441..f4ca260d4d89 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -64,7 +64,7 @@ android_test { "SettingsLibDeviceStateRotationLock", "SettingsLibDisplayUtils", "platform-test-annotations", - "truth-prebuilt", + "truth", ], libs: [ "android.test.base", diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java index 1d25ac78e7f9..e5dbe5f14cf4 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java @@ -69,6 +69,7 @@ public class GlobalSettings { Settings.Global.PRIVATE_DNS_SPECIFIER, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, Settings.Global.ZEN_DURATION, + Settings.Global.REVERSE_CHARGING_AUTO_ON, Settings.Global.CHARGING_VIBRATION_ENABLED, Settings.Global.AWARE_ALLOWED, Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP, // moved to secure diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 91d2d1bb58e5..c6e9c03d5968 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -143,6 +143,7 @@ public class SecureSettings { Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, + Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, @@ -218,6 +219,7 @@ public class SecureSettings { Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE, Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, Settings.Secure.NOTIFICATION_BUBBLES, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, @@ -244,6 +246,9 @@ public class SecureSettings { Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED, Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED, - Settings.Secure.HUB_MODE_TUTORIAL_STATE + Settings.Secure.HUB_MODE_TUTORIAL_STATE, + Settings.Secure.STYLUS_BUTTONS_ENABLED, + Settings.Secure.STYLUS_HANDWRITING_ENABLED, + Settings.Secure.DEFAULT_NOTE_TASK_PROFILE }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 248c60cb4fe9..fe39c4febc80 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -101,5 +101,10 @@ public class SystemSettings { Settings.System.CAMERA_FLASH_NOTIFICATION, Settings.System.SCREEN_FLASH_NOTIFICATION, Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, + Settings.System.PEAK_REFRESH_RATE, + Settings.System.MIN_REFRESH_RATE, + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, + Settings.System.NOTIFICATION_COOLDOWN_ALL, + Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index ba06185e5663..7e8fe7e09d74 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -171,6 +171,7 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.WIFI_SCAN_THROTTLE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.APP_AUTO_RESTRICTION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.ZEN_DURATION, ANY_INTEGER_VALIDATOR); + VALIDATORS.put(Global.REVERSE_CHARGING_AUTO_ON, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.REQUIRE_PASSWORD_TO_DECRYPT, 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 bec144766438..0727677b1a72 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -212,6 +212,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.VOLUME_HUSH_GESTURE, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put( Secure.ENABLED_NOTIFICATION_LISTENERS, @@ -308,6 +309,10 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE, + new InclusiveIntegerRangeValidator( + Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE, + Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL)); VALIDATORS.put( Secure.ACCESSIBILITY_BUTTON_TARGETS, ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); @@ -390,5 +395,9 @@ public class SecureSettingsValidators { BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DND_CONFIGS_MIGRATED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.HUB_MODE_TUTORIAL_STATE, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.STYLUS_BUTTONS_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.STYLUS_HANDWRITING_ENABLED, + new DiscreteValueValidator(new String[] {"-1", "0", "1"})); + VALIDATORS.put(Secure.DEFAULT_NOTE_TASK_PROFILE, NON_NEGATIVE_INTEGER_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 17ce7c7cc435..eba74ab14f3d 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -21,6 +21,7 @@ import static android.provider.settings.validators.SettingsValidators.ANY_STRING import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_FLOAT_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR; @@ -30,6 +31,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.hardware.display.ColorDisplayManager; import android.os.BatteryManager; +import android.provider.Settings.Global; import android.provider.Settings.System; import android.util.ArrayMap; @@ -120,6 +122,11 @@ public class SystemSettingsValidators { VALIDATORS.put(System.DISPLAY_COLOR_MODE_VENDOR_HINT, ANY_STRING_VALIDATOR); VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR); + VALIDATORS.put( + System.SCREEN_BRIGHTNESS_FOR_ALS, + new InclusiveIntegerRangeValidator( + System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT, + System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM)); VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR); VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR); @@ -231,5 +238,10 @@ public class SystemSettingsValidators { VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR); + VALIDATORS.put(System.PEAK_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR); + VALIDATORS.put(System.MIN_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR); + VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 3c8d4bca2394..f06b31c4e965 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1850,6 +1850,10 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Accessibility .ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED); dumpSetting(s, p, + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE, + SecureSettingsProto.Accessibility + .ACCESSIBILITY_MAGNIFICATION_GESTURE); + dumpSetting(s, p, Settings.Secure.HEARING_AID_RINGTONE_ROUTING, SecureSettingsProto.Accessibility.HEARING_AID_RINGTONE_ROUTING); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 40f7ba667376..46cd725ad582 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -121,6 +121,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.accessibility.util.AccessibilityUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; +import com.android.internal.display.RefreshRateSettingsUtils; import com.android.internal.os.BackgroundThread; import com.android.internal.util.FrameworkStatsLog; import com.android.providers.settings.SettingsState.Setting; @@ -2054,12 +2055,14 @@ public class SettingsProvider extends ContentProvider { final Uri ringtoneUri = Uri.parse(value); // Stream selected ringtone into cache, so it's available for playback // when CE storage is still locked - try (InputStream in = openRingtone(getContext(), ringtoneUri); - OutputStream out = new FileOutputStream(cacheFile)) { - FileUtils.copy(in, out); - } catch (IOException e) { - Slog.w(LOG_TAG, "Failed to cache ringtone: " + e); - } + Binder.withCleanCallingIdentity(() -> { + try (InputStream in = openRingtone(getContext(), ringtoneUri); + OutputStream out = new FileOutputStream(cacheFile)) { + FileUtils.copy(in, out); + } catch (IOException e) { + Slog.w(LOG_TAG, "Failed to cache ringtone: " + e); + } + }); } return true; } @@ -3876,7 +3879,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 222; + private static final int SETTINGS_VERSION = 223; private final int mUserId; @@ -5933,10 +5936,6 @@ public class SettingsProvider extends ContentProvider { if (currentVersion == 218) { // Version 219: Removed - // TODO(b/211737588): Back up the Smooth Display setting - // Future upgrades to the `peak_refresh_rate` and `min_refresh_rate` settings - // should account for the database in a non-upgraded and upgraded (change id: - // Ib2cb2dd100f06f5452083b7606109a486e795a0e) state. currentVersion = 219; } @@ -6002,6 +6001,56 @@ public class SettingsProvider extends ContentProvider { currentVersion = 222; } + // Version 222: Set peak refresh rate and min refresh rate to infinity if it's + // meant to be the highest possible refresh rate. This is needed so that we can + // back up and restore those settings on other devices. Other devices might have + // different highest possible refresh rates. + if (currentVersion == 222) { + final SettingsState systemSettings = getSystemSettingsLocked(userId); + final Setting peakRefreshRateSetting = + systemSettings.getSettingLocked(Settings.System.PEAK_REFRESH_RATE); + final Setting minRefreshRateSetting = + systemSettings.getSettingLocked(Settings.System.MIN_REFRESH_RATE); + float highestRefreshRate = RefreshRateSettingsUtils + .findHighestRefreshRateForDefaultDisplay(getContext()); + + if (!peakRefreshRateSetting.isNull()) { + try { + float peakRefreshRate = + Float.parseFloat(peakRefreshRateSetting.getValue()); + if (Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) { + systemSettings.insertSettingLocked( + Settings.System.PEAK_REFRESH_RATE, + String.valueOf(Float.POSITIVE_INFINITY), + /* tag= */ null, + /* makeDefault= */ false, + SettingsState.SYSTEM_PACKAGE_NAME); + } + } catch (NumberFormatException e) { + // Do nothing. Leave the value as is. + } + } + + if (!minRefreshRateSetting.isNull()) { + try { + float minRefreshRate = + Float.parseFloat(minRefreshRateSetting.getValue()); + if (Math.round(minRefreshRate) == Math.round(highestRefreshRate)) { + systemSettings.insertSettingLocked( + Settings.System.MIN_REFRESH_RATE, + String.valueOf(Float.POSITIVE_INFINITY), + /* tag= */ null, + /* makeDefault= */ false, + SettingsState.SYSTEM_PACKAGE_NAME); + } + } catch (NumberFormatException e) { + // Do nothing. Leave the value as is. + } + } + + currentVersion = 223; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index c0f62313cb39..7bca944033d9 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -98,9 +98,8 @@ public class SettingsBackupTest { Settings.System.VOLUME_VOICE, // deprecated since API 2? Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only - Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities - Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities Settings.System.SCREEN_BRIGHTNESS_FLOAT, + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, Settings.System.WEAR_TTS_PREWARM_ENABLED, Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, @@ -734,7 +733,6 @@ public class SettingsBackupTest { Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, Settings.Secure.CONTENT_CAPTURE_ENABLED, Settings.Secure.DEFAULT_INPUT_METHOD, - Settings.Secure.DEFAULT_NOTE_TASK_PROFILE, Settings.Secure.DEVICE_PAIRED, Settings.Secure.DIALER_DEFAULT_APPLICATION, Settings.Secure.DISABLED_PRINT_SERVICES, @@ -804,8 +802,6 @@ public class SettingsBackupTest { Settings.Secure.SLEEP_TIMEOUT, Settings.Secure.SMS_DEFAULT_APPLICATION, Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q - Settings.Secure.STYLUS_BUTTONS_ENABLED, - Settings.Secure.STYLUS_HANDWRITING_ENABLED, Settings.Secure.TRUST_AGENTS_INITIALIZED, Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index ee05f2d9101b..e40fcb2a633b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -319,6 +319,8 @@ filegroup { "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt", "tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt", "tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt", + "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt", + "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt", /* Bouncer UI tests */ "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt", @@ -365,6 +367,42 @@ filegroup { "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt", "tests/src/com/android/systemui/qs/tiles/base/**/*.kt", "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt", + + /* Authentication */ + "tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt", + "tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt", + + /* Device entry */ + "tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt", + "tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt", + + /* Bouncer scene */ + "tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt", + + /* Lockscreen scene */ + "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt", + + /* Shade scene */ + "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt", + "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt", + + /* Quick Settings scene */ + "tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt", + + /* Flexiglass / Scene framework tests */ + "tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt", + "tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt", + "tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt", + "tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt", + "tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt", + "tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt", + "tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt", ], path: "tests/src", } @@ -428,7 +466,7 @@ android_library { "hamcrest-library", "androidx.test.rules", "testables", - "truth-prebuilt", + "truth", "monet", "libmonet", "dagger2", @@ -545,7 +583,7 @@ android_robolectric_test { "android.test.runner", "android.test.base", "android.test.mock", - "truth-prebuilt", + "truth", ], upstream: true, diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml index f6dcdd3dc0bd..b314c8eaea1d 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml @@ -8,7 +8,7 @@ <string name="a11y_settings_label" msgid="3977714687248445050">"Erabilerraztasun-&#173;ezarpenak"</string> <string name="power_label" msgid="7699720321491287839">"Bateria"</string> <string name="power_utterance" msgid="7444296686402104807">"Bateria kontrolatzeko aukerak"</string> - <string name="recent_apps_label" msgid="6583276995616385847">"Azken aplikazioak"</string> + <string name="recent_apps_label" msgid="6583276995616385847">"Azkenaldiko aplikazioak"</string> <string name="lockscreen_label" msgid="648347953557887087">"Pantaila blokeatua"</string> <string name="quick_settings_label" msgid="2999117381487601865">"Ezarpen bizkorrak"</string> <string name="notifications_label" msgid="6829741046963013567">"Jakinarazpenak"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java index 008732e787f1..96e1e3fa68a7 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java @@ -63,6 +63,7 @@ public class AccessibilityMenuService extends AccessibilityService private static final String TAG = "A11yMenuService"; private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L; + private static final long TAKE_SCREENSHOT_DELAY_MS = 100L; private static final int BRIGHTNESS_UP_INCREMENT_GAMMA = (int) Math.ceil(BrightnessUtils.GAMMA_SPACE_MAX * 0.11f); @@ -301,7 +302,14 @@ public class AccessibilityMenuService extends AccessibilityService } else if (viewTag == ShortcutId.ID_NOTIFICATION_VALUE.ordinal()) { performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS); } else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) { - performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT); + if (Flags.a11yMenuHideBeforeTakingAction()) { + // Delay before taking a screenshot to give time for the UI to close. + mHandler.postDelayed( + () -> performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT), + TAKE_SCREENSHOT_DELAY_MS); + } else { + performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT); + } } if (!Flags.a11yMenuHideBeforeTakingAction()) { diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp index 538ecb3d438d..3fc351c32ec1 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp @@ -32,7 +32,7 @@ android_test { "androidx.test.ext.junit", "compatibility-device-util-axt", "platform-test-annotations", - "truth-prebuilt", + "truth", ], srcs: [ "src/**/*.java", diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 437f8afa8d70..2509cfd4af40 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -13,3 +13,19 @@ flag { description: "Enables all the sysui classic flags that are marked as being in teamfood" bug: "302578396" } + +flag { + name: "notifications_footer_view_refactor" + namespace: "systemui" + description: "Enables the refactored version of the footer view in the notification shade " + "(containing the \"Clear all\" button). Should not bring any behavior changes" + bug: "293167744" +} + +flag { + name: "notification_lifetime_extension_refactor" + namespace: "systemui" + description: "Enables moving notification lifetime extension management from SystemUI to " + "Notification Manager Service" + bug: "299448097" +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 0f2e4bace46d..4aac27932924 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -39,6 +39,7 @@ import android.view.ViewGroup import android.view.WindowManager import android.view.animation.Interpolator import android.view.animation.PathInterpolator +import androidx.annotation.AnyThread import androidx.annotation.BinderThread import androidx.annotation.UiThread import com.android.app.animation.Interpolators @@ -149,6 +150,10 @@ class ActivityLaunchAnimator( override fun onLaunchAnimationProgress(linearProgress: Float) { listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } } + + override fun onLaunchAnimationCancelled() { + listeners.forEach { it.onLaunchAnimationCancelled() } + } } /** @@ -191,6 +196,7 @@ class ActivityLaunchAnimator( "ActivityLaunchAnimator.callback must be set before using this animator" ) val runner = createRunner(controller) + val runnerDelegate = runner.delegate!! val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the @@ -241,12 +247,15 @@ class ActivityLaunchAnimator( // If we expect an animation, post a timeout to cancel it in case the remote animation is // never started. if (willAnimate) { - runner.delegate.postTimeout() + runnerDelegate.postTimeout() // Hide the keyguard using the launch animation instead of the default unlock animation. if (hideKeyguardWithAnimation) { callback.hideKeyguardWithAnimation(runner) } + } else { + // We need to make sure delegate references are dropped to avoid memory leaks. + runner.dispose() } } @@ -344,6 +353,13 @@ class ActivityLaunchAnimator( */ fun onLaunchAnimationEnd() {} + /** + * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after + * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called + * before the cancellation. + */ + fun onLaunchAnimationCancelled() {} + /** Called when an activity launch animation made progress. */ fun onLaunchAnimationProgress(linearProgress: Float) {} } @@ -426,6 +442,39 @@ class ActivityLaunchAnimator( fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {} } + /** + * Invokes [onAnimationComplete] when animation is either cancelled or completed. Delegates all + * events to the passed [delegate]. + */ + @VisibleForTesting + inner class DelegatingAnimationCompletionListener( + private val delegate: Listener?, + private val onAnimationComplete: () -> Unit + ) : Listener { + var cancelled = false + + override fun onLaunchAnimationStart() { + delegate?.onLaunchAnimationStart() + } + + override fun onLaunchAnimationProgress(linearProgress: Float) { + delegate?.onLaunchAnimationProgress(linearProgress) + } + + override fun onLaunchAnimationEnd() { + delegate?.onLaunchAnimationEnd() + if (!cancelled) { + onAnimationComplete.invoke() + } + } + + override fun onLaunchAnimationCancelled() { + cancelled = true + delegate?.onLaunchAnimationCancelled() + onAnimationComplete.invoke() + } + } + @VisibleForTesting inner class Runner( controller: Controller, @@ -436,11 +485,21 @@ class ActivityLaunchAnimator( listener: Listener? = null ) : IRemoteAnimationRunner.Stub() { private val context = controller.launchContainer.context - internal val delegate: AnimationDelegate + + // This is being passed across IPC boundaries and cycles (through PendingIntentRecords, + // etc.) are possible. So we need to make sure we drop any references that might + // transitively cause leaks when we're done with animation. + @VisibleForTesting var delegate: AnimationDelegate? init { delegate = - AnimationDelegate(controller, callback, listener, launchAnimator, disableWmTimeout) + AnimationDelegate( + controller, + callback, + DelegatingAnimationCompletionListener(listener, this::dispose), + launchAnimator, + disableWmTimeout + ) } @BinderThread @@ -451,14 +510,33 @@ class ActivityLaunchAnimator( nonApps: Array<out RemoteAnimationTarget>?, finishedCallback: IRemoteAnimationFinishedCallback? ) { + val delegate = delegate context.mainExecutor.execute { - delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback) + if (delegate == null) { + Log.i(TAG, "onAnimationStart called after completion") + // Animation started too late and timed out already. We need to still + // signal back that we're done with it. + finishedCallback?.onAnimationFinished() + } else { + delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback) + } } } @BinderThread override fun onAnimationCancelled() { - context.mainExecutor.execute { delegate.onAnimationCancelled() } + val delegate = delegate + context.mainExecutor.execute { + delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion") + delegate?.onAnimationCancelled() + } + } + + @AnyThread + fun dispose() { + // Drop references to animation controller once we're done with the animation + // to avoid leaking. + context.mainExecutor.execute { delegate = null } } } @@ -584,6 +662,7 @@ class ActivityLaunchAnimator( ) } controller.onLaunchAnimationCancelled() + listener?.onLaunchAnimationCancelled() return } @@ -821,6 +900,7 @@ class ActivityLaunchAnimator( Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]") } controller.onLaunchAnimationCancelled() + listener?.onLaunchAnimationCancelled() } @UiThread @@ -842,6 +922,7 @@ class ActivityLaunchAnimator( ) } controller.onLaunchAnimationCancelled() + listener?.onLaunchAnimationCancelled() } private fun IRemoteAnimationFinishedCallback.invoke() { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index bd3706e1aff3..00d905652987 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -70,8 +70,10 @@ class ViewHierarchyAnimator { * If a new layout change happens while an animation is already in progress, the animation * is updated to continue from the current values to the new end state. * - * A set of [excludedViews] can be passed. If any dependent view from [rootView] matches an - * entry in this set, changes to that view will not be animated. + * By default, child views whole layout changes are animated as well. However, this can be + * controlled by [animateChildren]. If children are included, a set of [excludedViews] can + * be passed. If any dependent view from [rootView] matches an entry in this set, changes to + * that view will not be animated. * * The animator continues to respond to layout changes until [stopAnimating] is called. * @@ -86,6 +88,7 @@ class ViewHierarchyAnimator { rootView: View, interpolator: Interpolator = DEFAULT_INTERPOLATOR, duration: Long = DEFAULT_DURATION, + animateChildren: Boolean = true, excludedViews: Set<View> = emptySet() ): Boolean { return animate( @@ -93,6 +96,7 @@ class ViewHierarchyAnimator { interpolator, duration, ephemeral = false, + animateChildren = animateChildren, excludedViews = excludedViews ) } @@ -106,6 +110,7 @@ class ViewHierarchyAnimator { rootView: View, interpolator: Interpolator = DEFAULT_INTERPOLATOR, duration: Long = DEFAULT_DURATION, + animateChildren: Boolean = true, excludedViews: Set<View> = emptySet() ): Boolean { return animate( @@ -113,6 +118,7 @@ class ViewHierarchyAnimator { interpolator, duration, ephemeral = true, + animateChildren = animateChildren, excludedViews = excludedViews ) } @@ -122,6 +128,7 @@ class ViewHierarchyAnimator { interpolator: Interpolator, duration: Long, ephemeral: Boolean, + animateChildren: Boolean, excludedViews: Set<View> = emptySet() ): Boolean { if ( @@ -137,7 +144,13 @@ class ViewHierarchyAnimator { } val listener = createUpdateListener(interpolator, duration, ephemeral) - addListener(rootView, listener, recursive = true, excludedViews = excludedViews) + addListener( + rootView, + listener, + recursive = true, + animateChildren = animateChildren, + excludedViews = excludedViews + ) return true } @@ -940,6 +953,7 @@ class ViewHierarchyAnimator { view: View, listener: View.OnLayoutChangeListener, recursive: Boolean = false, + animateChildren: Boolean = true, excludedViews: Set<View> = emptySet() ) { if (excludedViews.contains(view)) return @@ -952,12 +966,13 @@ class ViewHierarchyAnimator { view.addOnLayoutChangeListener(listener) view.setTag(R.id.tag_layout_listener, listener) - if (view is ViewGroup && recursive) { + if (animateChildren && view is ViewGroup && recursive) { for (i in 0 until view.childCount) { addListener( view.getChildAt(i), listener, recursive = true, + animateChildren = animateChildren, excludedViews = excludedViews ) } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt index 88944f10eab9..60c3fd3c4cdb 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -108,7 +108,7 @@ private fun CoroutineScope.animate( ) { val fromScene = layoutImpl.state.transitionState.currentScene val isUserInput = - (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven + (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput ?: false val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec @@ -119,9 +119,23 @@ private fun CoroutineScope.animate( val targetProgress = if (reversed) 0f else 1f val transition = if (reversed) { - OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable) + OneOffTransition( + fromScene = target, + toScene = fromScene, + currentScene = target, + isUserInput, + isUserInputOngoing = false, + animatable, + ) } else { - OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable) + OneOffTransition( + fromScene = fromScene, + toScene = target, + currentScene = target, + isUserInput, + isUserInputOngoing = false, + animatable, + ) } // Change the current layout state to use this new transition. @@ -142,7 +156,8 @@ private class OneOffTransition( override val fromScene: SceneKey, override val toScene: SceneKey, override val currentScene: SceneKey, - override val isUserInputDriven: Boolean, + override val isInitiatedByUserInput: Boolean, + override val isUserInputOngoing: Boolean, private val animatable: Animatable<Float, AnimationVector1D>, ) : TransitionState.Transition { override val progress: Float diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt index a62c9840add1..3bcd920fb02b 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt @@ -48,28 +48,40 @@ import com.android.compose.ui.util.lerp /** An element on screen, that can be composed in one or more scenes. */ internal class Element(val key: ElementKey) { /** - * The last offset assigned to this element, relative to the SceneTransitionLayout containing - * it. + * The last values of this element, coming from any scene. Note that this value will be unstable + * if this element is present in multiple scenes but the shared element animation is disabled, + * given that multiple instances of the element with different states will write to these + * values. You should prefer using [TargetValues.lastValues] in the current scene if it is + * defined. */ - var lastOffset = Offset.Unspecified - - /** The last size assigned to this element. */ - var lastSize = SizeUnspecified - - /** The last alpha assigned to this element. */ - var lastAlpha = 1f + val lastSharedValues = Values() /** The mapping between a scene and the values/state this element has in that scene, if any. */ - val sceneValues = SnapshotStateMap<SceneKey, SceneValues>() + val sceneValues = SnapshotStateMap<SceneKey, TargetValues>() override fun toString(): String { return "Element(key=$key)" } + /** The current values of this element, either in a specific scene or in a shared context. */ + class Values { + /** The offset of the element, relative to the SceneTransitionLayout containing it. */ + var offset = Offset.Unspecified + + /** The size of this element. */ + var size = SizeUnspecified + + /** The alpha of this element. */ + var alpha = AlphaUnspecified + } + /** The target values of this element in a given scene. */ - class SceneValues { - var size by mutableStateOf(SizeUnspecified) - var offset by mutableStateOf(Offset.Unspecified) + class TargetValues { + val lastValues = Values() + + var targetSize by mutableStateOf(SizeUnspecified) + var targetOffset by mutableStateOf(Offset.Unspecified) + val sharedValues = SnapshotStateMap<ValueKey, SharedValue<*>>() } @@ -80,6 +92,7 @@ internal class Element(val key: ElementKey) { companion object { val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE) + val AlphaUnspecified = Float.MIN_VALUE } } @@ -91,7 +104,7 @@ internal fun Modifier.element( scene: Scene, key: ElementKey, ): Modifier { - val sceneValues = remember(scene, key) { Element.SceneValues() } + val sceneValues = remember(scene, key) { Element.TargetValues() } val element = // Get the element associated to [key] if it was already composed in another scene, // otherwise create it and add it to our Map<ElementKey, Element>. This is done inside a @@ -108,6 +121,8 @@ internal fun Modifier.element( element } + val lastSharedValues = element.lastSharedValues + val lastSceneValues = sceneValues.lastValues DisposableEffect(scene, sceneValues, element) { onDispose { @@ -121,13 +136,14 @@ internal fun Modifier.element( } val alpha = - remember(layoutImpl, element, scene) { - derivedStateOf { elementAlpha(layoutImpl, element, scene) } + remember(layoutImpl, element, scene, sceneValues) { + derivedStateOf { elementAlpha(layoutImpl, element, scene, sceneValues) } } val isOpaque by remember(alpha) { derivedStateOf { alpha.value == 1f } } SideEffect { - if (isOpaque && element.lastAlpha != 1f) { - element.lastAlpha = 1f + if (isOpaque) { + lastSharedValues.alpha = 1f + lastSceneValues.alpha = 1f } } @@ -148,7 +164,8 @@ internal fun Modifier.element( Modifier.graphicsLayer { val alpha = alpha.value this.alpha = alpha - element.lastAlpha = alpha + lastSharedValues.alpha = alpha + lastSceneValues.alpha = alpha } } .testTag(key.testTag) @@ -220,7 +237,7 @@ private fun Modifier.modifierTransformations( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, ): Modifier { when (val state = layoutImpl.state.transitionState) { is TransitionState.Idle -> return this @@ -248,7 +265,8 @@ private fun Modifier.modifierTransformations( private fun elementAlpha( layoutImpl: SceneTransitionLayoutImpl, element: Element, - scene: Scene + scene: Scene, + sceneValues: Element.TargetValues, ): Float { return computeValue( layoutImpl, @@ -258,7 +276,11 @@ private fun elementAlpha( transformation = { it.alpha }, idleValue = 1f, currentValue = { 1f }, - lastValue = { element.lastAlpha }, + lastValue = { + sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified } + ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified } + ?: 1f + }, ::lerp, ) .coerceIn(0f, 1f) @@ -269,15 +291,15 @@ private fun IntermediateMeasureScope.measure( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, measurable: Measurable, constraints: Constraints, ): Placeable { // Update the size this element has in this scene when idle. val targetSizeInScene = lookaheadSize - if (targetSizeInScene != sceneValues.size) { + if (targetSizeInScene != sceneValues.targetSize) { // TODO(b/290930950): Better handle when this changes to avoid instant size jumps. - sceneValues.size = targetSizeInScene + sceneValues.targetSize = targetSizeInScene } // Some lambdas called (max once) by computeValue() will need to measure [measurable], in which @@ -292,17 +314,14 @@ private fun IntermediateMeasureScope.measure( layoutImpl, scene, element, - sceneValue = { it.size }, + sceneValue = { it.targetSize }, transformation = { it.size }, idleValue = lookaheadSize, currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() }, lastValue = { - val lastSize = element.lastSize - if (lastSize != Element.SizeUnspecified) { - lastSize - } else { - measurable.measure(constraints).also { maybePlaceable = it }.size() - } + sceneValues.lastValues.size.takeIf { it != Element.SizeUnspecified } + ?: element.lastSharedValues.size.takeIf { it != Element.SizeUnspecified } + ?: measurable.measure(constraints).also { maybePlaceable = it }.size() }, ::lerp, ) @@ -316,7 +335,9 @@ private fun IntermediateMeasureScope.measure( ) ) - element.lastSize = placeable.size() + val size = placeable.size() + element.lastSharedValues.size = size + sceneValues.lastValues.size = size return placeable } @@ -325,7 +346,7 @@ private fun IntermediateMeasureScope.place( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, placeable: Placeable, placementScope: Placeable.PlacementScope, ) { @@ -334,9 +355,9 @@ private fun IntermediateMeasureScope.place( // when idle. val coords = coordinates!! val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords) - if (targetOffsetInScene != sceneValues.offset) { + if (targetOffsetInScene != sceneValues.targetOffset) { // TODO(b/290930950): Better handle when this changes to avoid instant offset jumps. - sceneValues.offset = targetOffsetInScene + sceneValues.targetOffset = targetOffsetInScene } val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero) @@ -345,22 +366,20 @@ private fun IntermediateMeasureScope.place( layoutImpl, scene, element, - sceneValue = { it.offset }, + sceneValue = { it.targetOffset }, transformation = { it.offset }, idleValue = targetOffsetInScene, currentValue = { currentOffset }, lastValue = { - val lastValue = element.lastOffset - if (lastValue.isSpecified) { - lastValue - } else { - currentOffset - } + sceneValues.lastValues.offset.takeIf { it.isSpecified } + ?: element.lastSharedValues.offset.takeIf { it.isSpecified } + ?: currentOffset }, ::lerp, ) - element.lastOffset = targetOffset + element.lastSharedValues.offset = targetOffset + sceneValues.lastValues.offset = targetOffset placeable.place((targetOffset - currentOffset).round()) } } @@ -391,7 +410,7 @@ private inline fun <T> computeValue( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValue: (Element.SceneValues) -> T, + sceneValue: (Element.TargetValues) -> T, transformation: (ElementTransformations) -> PropertyTransformation<T>?, idleValue: T, currentValue: () -> T, diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt index ccdec6ea8c5e..1b79dbdee510 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -52,7 +52,14 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isUserInputDriven: Boolean, + val isInitiatedByUserInput: Boolean, + + /** + * Whether user input is currently driving the transition. For example, if a user is + * dragging a pointer, this emits true. Once they lift their finger, this emits false while + * the transition completes/settles. + */ + val isUserInputOngoing: Flow<Boolean>, ) : ObservableTransitionState() } @@ -73,7 +80,8 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans fromScene = state.fromScene, toScene = state.toScene, progress = snapshotFlow { state.progress }, - isUserInputDriven = state.isUserInputDriven, + isInitiatedByUserInput = state.isInitiatedByUserInput, + isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, ) } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 39173d98538f..58c7bdbf3d71 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -130,10 +130,23 @@ interface SceneScope { sealed interface UserAction /** The user navigated back, either using a gesture or by triggering a KEYCODE_BACK event. */ -object Back : UserAction +data object Back : UserAction /** The user swiped on the container. */ -enum class Swipe : UserAction { +data class Swipe( + val direction: SwipeDirection, + val pointerCount: Int = 1, + val fromEdge: Edge? = null, +) : UserAction { + companion object { + val Left = Swipe(SwipeDirection.Left) + val Up = Swipe(SwipeDirection.Up) + val Right = Swipe(SwipeDirection.Right) + val Down = Swipe(SwipeDirection.Down) + } +} + +enum class SwipeDirection { Up, Down, Left, diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 7a21211c3dde..b9f83c545122 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -70,6 +70,9 @@ sealed interface TransitionState { val progress: Float /** Whether the transition was triggered by user input rather than being programmatic. */ - val isUserInputDriven: Boolean + val isInitiatedByUserInput: Boolean + + /** Whether user input is currently driving the transition. */ + val isUserInputOngoing: Boolean } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt index 1cbfe3057ff0..e275fcaf4572 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -66,7 +66,7 @@ internal fun Modifier.swipeToScene( // swipe in the other direction. val startDragImmediately = state == transition && - transition.isAnimatingOffset && + !transition.isUserInputOngoing && !currentScene.shouldEnableSwipes(orientation.opposite()) // The velocity threshold at which the intent of the user is to swipe up or down. It is the same @@ -126,7 +126,7 @@ private class SwipeTransition(initialScene: Scene) : TransitionState.Transition override val progress: Float get() { - val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset + val offset = if (isUserInputOngoing) dragOffset else offsetAnimatable.value if (distance == 0f) { // This can happen only if fromScene == toScene. error( @@ -137,16 +137,15 @@ private class SwipeTransition(initialScene: Scene) : TransitionState.Transition return offset / distance } - override val isUserInputDriven = true + override val isInitiatedByUserInput = true + + var _isUserInputOngoing by mutableStateOf(false) + override val isUserInputOngoing: Boolean + get() = _isUserInputOngoing /** The current offset caused by the drag gesture. */ var dragOffset by mutableFloatStateOf(0f) - /** - * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture. - */ - var isAnimatingOffset by mutableStateOf(false) - /** The animatable used to animate the offset once the user lifted its finger. */ val offsetAnimatable = Animatable(0f, visibilityThreshold = OffsetVisibilityThreshold) @@ -209,9 +208,11 @@ private fun onDragStarted( transition: SwipeTransition, orientation: Orientation, ) { + transition._isUserInputOngoing = true + if (layoutImpl.state.transitionState == transition) { // This [transition] was already driving the animation: simply take over it. - if (transition.isAnimatingOffset) { + if (!transition.isUserInputOngoing) { // Stop animating and start from where the current offset. Setting the animation job to // `null` will effectively cancel the animation. transition.stopOffsetAnimation() @@ -456,30 +457,29 @@ private fun CoroutineScope.animateOffset( ) { transition.startOffsetAnimation { launch { - if (!transition.isAnimatingOffset) { - transition.offsetAnimatable.snapTo(transition.dragOffset) - } - transition.isAnimatingOffset = true - - transition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) + if (transition.isUserInputOngoing) { + transition.offsetAnimatable.snapTo(transition.dragOffset) + } + transition._isUserInputOngoing = false + + transition.offsetAnimatable.animateTo( + targetOffset, + // TODO(b/290184746): Make this spring spec configurable. + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ), + initialVelocity = initialVelocity, + ) - // Now that the animation is done, the state should be idle. Note that if the state - // was changed since this animation started, some external code changed it and we - // shouldn't do anything here. Note also that this job will be cancelled in the case - // where the user intercepts this swipe. - if (layoutImpl.state.transitionState == transition) { - layoutImpl.state.transitionState = TransitionState.Idle(targetScene) - } + // Now that the animation is done, the state should be idle. Note that if the state + // was changed since this animation started, some external code changed it and we + // shouldn't do anything here. Note also that this job will be cancelled in the case + // where the user intercepts this swipe. + if (layoutImpl.state.transitionState == transition) { + layoutImpl.state.transitionState = TransitionState.Idle(targetScene) } - .also { it.invokeOnCompletion { transition.isAnimatingOffset = false } } + } } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt index d4ed697f1757..95385d51cb25 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt @@ -34,12 +34,12 @@ internal class AnchoredSize( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: IntSize, ): IntSize { fun anchorSizeIn(scene: SceneKey): IntSize { - val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.size + val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.targetSize return if (size != null && size != Element.SizeUnspecified) { size } else { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt index 8a5bd746dced..a1d63193bc73 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt @@ -35,13 +35,13 @@ internal class AnchoredTranslate( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Offset, ): Offset { val anchor = layoutImpl.elements[anchor] ?: return value fun anchorOffsetIn(scene: SceneKey): Offset? { - return anchor.sceneValues[scene]?.offset?.takeIf { it.isSpecified } + return anchor.sceneValues[scene]?.targetOffset?.takeIf { it.isSpecified } } // [element] will move the same amount as [anchor] does. diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt index 5cdce9489772..840800d838db 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt @@ -34,12 +34,12 @@ internal class EdgeTranslate( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Offset ): Offset { val sceneSize = scene.size - val elementSize = sceneValues.size + val elementSize = sceneValues.targetSize if (elementSize == Element.SizeUnspecified) { return value } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt index 0a5ac5413b38..17032dc288e0 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt @@ -30,7 +30,7 @@ internal class Fade( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Float ): Float { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt index 73b366ef57bc..62d67f03f1d0 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt @@ -47,14 +47,14 @@ internal class PunchHole( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, ): Modifier { return drawWithContent { val bounds = layoutImpl.elements[bounds] if ( bounds == null || - bounds.lastSize == Element.SizeUnspecified || - bounds.lastOffset == Offset.Unspecified + bounds.lastSharedValues.size == Element.SizeUnspecified || + bounds.lastSharedValues.offset == Offset.Unspecified ) { drawContent() return@drawWithContent @@ -64,7 +64,7 @@ internal class PunchHole( canvas.withSaveLayer(size.toRect(), Paint()) { drawContent() - val offset = bounds.lastOffset - element.lastOffset + val offset = bounds.lastSharedValues.offset - element.lastSharedValues.offset translate(offset.x, offset.y) { drawHole(bounds) } } } @@ -72,7 +72,7 @@ internal class PunchHole( } private fun DrawScope.drawHole(bounds: Element) { - val boundsSize = bounds.lastSize.toSize() + val boundsSize = bounds.lastSharedValues.size.toSize() if (shape == RectangleShape) { drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut) return diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt index ce754dc76adc..233ae597090b 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt @@ -37,7 +37,7 @@ internal class ScaleSize( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: IntSize, ): IntSize { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt index a65025423aee..2ef8d56c6bc6 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt @@ -59,7 +59,7 @@ internal interface ModifierTransformation : Transformation { layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, ): Modifier } @@ -74,7 +74,7 @@ internal sealed interface PropertyTransformation<T> : Transformation { layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: T, ): T diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt index 8abca61bab20..864b937a3fe0 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt @@ -35,7 +35,7 @@ internal class Translate( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Offset, ): Offset { diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp index 52c63854f62f..8e9c5864ce70 100644 --- a/packages/SystemUI/compose/core/tests/Android.bp +++ b/packages/SystemUI/compose/core/tests/Android.bp @@ -43,7 +43,7 @@ android_test { "androidx.compose.ui_ui-test-junit4", "androidx.compose.ui_ui-test-manifest", - "truth-prebuilt", + "truth", ], kotlincflags: ["-Xjvm-default=all"], diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 2232370f3dc0..53ed2b5d3317 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -122,7 +122,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isTrue() // Release the finger. We should now be animating back to A (currentScene = SceneA) given // that 55dp < positional threshold. @@ -134,7 +135,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isFalse() // Wait for the animation to finish. We should now be in scene A. rule.waitForIdle() @@ -156,7 +158,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isTrue() // Release the finger. We should now be animating to C (currentScene = SceneC) given // that 56dp >= positional threshold. @@ -168,7 +171,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isFalse() // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index a9944f739975..f2b7b3290f96 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -144,15 +144,17 @@ private fun SceneScope.BouncerScene( } val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize() + val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible - when (windowSizeClass.widthSizeClass) { - WindowWidthSizeClass.Expanded -> + when { + windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded -> SideBySide( viewModel = viewModel, dialogFactory = dialogFactory, modifier = childModifier, ) - WindowWidthSizeClass.Medium -> + isFullScreenUserSwitcherEnabled && + windowSizeClass.widthSizeClass == WindowWidthSizeClass.Medium -> Stacked( viewModel = viewModel, dialogFactory = dialogFactory, @@ -442,14 +444,22 @@ private fun SideBySide( label = "offset", ) - UserSwitcher( - viewModel = viewModel, - modifier = - Modifier.fillMaxHeight().weight(1f).graphicsLayer { - translationX = size.width * animatedOffset - alpha = animatedAlpha(animatedOffset) - }, - ) + val userSwitcherModifier = + Modifier.fillMaxHeight().weight(1f).graphicsLayer { + translationX = size.width * animatedOffset + alpha = animatedAlpha(animatedOffset) + } + if (viewModel.isUserSwitcherVisible) { + UserSwitcher( + viewModel = viewModel, + modifier = userSwitcherModifier, + ) + } else { + Box( + modifier = userSwitcherModifier, + ) + } + Box( modifier = Modifier.fillMaxHeight().weight(1f).graphicsLayer { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index c3a3752db374..ee310ab41373 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -36,13 +36,14 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.viewinterop.AndroidView import androidx.core.view.isVisible import com.android.compose.animation.scene.SceneScope -import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.qualifiers.KeyguardRootView import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel +import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.UserAction @@ -99,7 +100,9 @@ constructor( return buildMap { up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) } left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) } - this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade) + this[UserAction.Swipe(fromEdge = Edge.TOP, direction = Direction.DOWN)] = + SceneModel(SceneKey.QuickSettings) + this[UserAction.Swipe(direction = Direction.DOWN)] = SceneModel(SceneKey.Shade) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 1f9c3e6d1ea1..a33eac55ac3e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -23,11 +23,13 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.SceneScope +import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.footer.ui.compose.QuickSettings @@ -37,6 +39,7 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.UserAction import com.android.systemui.scene.ui.composable.ComposableScene +import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.ExpandedShadeHeader import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager @@ -98,12 +101,22 @@ private fun SceneScope.QuickSettingsScene( .clickable(onClick = { viewModel.onContentClicked() }) .padding(start = 16.dp, end = 16.dp, bottom = 48.dp) ) { - ExpandedShadeHeader( - viewModel = viewModel.shadeHeaderViewModel, - createTintedIconManager = createTintedIconManager, - createBatteryMeterViewController = createBatteryMeterViewController, - statusBarIconController = statusBarIconController, - ) + when (LocalWindowSizeClass.current.widthSizeClass) { + WindowWidthSizeClass.Compact -> + ExpandedShadeHeader( + viewModel = viewModel.shadeHeaderViewModel, + createTintedIconManager = createTintedIconManager, + createBatteryMeterViewController = createBatteryMeterViewController, + statusBarIconController = statusBarIconController, + ) + else -> + CollapsedShadeHeader( + viewModel = viewModel.shadeHeaderViewModel, + createTintedIconManager = createTintedIconManager, + createBatteryMeterViewController = createBatteryMeterViewController, + statusBarIconController = statusBarIconController, + ) + } Spacer(modifier = Modifier.height(16.dp)) QuickSettings() } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index 2ee461fca042..f35ea8373db7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier import com.android.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.UserAction @@ -41,7 +42,12 @@ class GoneScene @Inject constructor() : ComposableScene { override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = MutableStateFlow<Map<UserAction, SceneModel>>( mapOf( - UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade), + UserAction.Swipe( + pointerCount = 2, + fromEdge = Edge.TOP, + direction = Direction.DOWN, + ) to SceneModel(SceneKey.QuickSettings), + UserAction.Swipe(direction = Direction.DOWN) to SceneModel(SceneKey.Shade), ) ) .asStateFlow() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index ef012660ad71..0da562bcb3bb 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -35,15 +35,18 @@ import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.motionEventSpy import androidx.compose.ui.input.pointer.pointerInput import com.android.compose.animation.scene.Back +import com.android.compose.animation.scene.Edge as SceneTransitionEdge import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -158,7 +161,8 @@ private fun SceneTransitionObservableTransitionState.toModel(): ObservableTransi fromScene = fromScene.toModel().key, toScene = toScene.toModel().key, progress = progress, - isUserInputDriven = isUserInputDriven, + isInitiatedByUserInput = isInitiatedByUserInput, + isUserInputOngoing = isUserInputOngoing, ) } } @@ -180,12 +184,24 @@ private fun SceneTransitionSceneKey.toModel(): SceneModel { private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction { return when (this) { is UserAction.Swipe -> - when (this.direction) { - Direction.LEFT -> Swipe.Left - Direction.UP -> Swipe.Up - Direction.RIGHT -> Swipe.Right - Direction.DOWN -> Swipe.Down - } + Swipe( + pointerCount = pointerCount, + fromEdge = + when (this.fromEdge) { + null -> null + Edge.LEFT -> SceneTransitionEdge.Left + Edge.TOP -> SceneTransitionEdge.Top + Edge.RIGHT -> SceneTransitionEdge.Right + Edge.BOTTOM -> SceneTransitionEdge.Bottom + }, + direction = + when (this.direction) { + Direction.LEFT -> SwipeDirection.Left + Direction.UP -> SwipeDirection.Up + Direction.RIGHT -> SwipeDirection.Right + Direction.DOWN -> SwipeDirection.Down + } + ) is UserAction.Back -> Back } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index 6629a2598587..591fa76f423f 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf @@ -50,6 +51,7 @@ import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.ValueKey import com.android.compose.animation.scene.animateSharedFloatAsState +import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.settingslib.Utils import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController @@ -98,12 +100,12 @@ fun SceneScope.CollapsedShadeHeader( ShadeHeader.Keys.transitionProgress, ShadeHeader.Elements.FormatPlaceholder ) - val useExpandedFormat by - remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } } val cutoutWidth = LocalDisplayCutout.current.width() val cutoutLocation = LocalDisplayCutout.current.location + val useExpandedFormat = formatProgress.value > 0.5f || cutoutLocation != CutoutLocation.CENTER + // This layout assumes it is globally positioned at (0, 0) and is the // same size as the screen. Layout( @@ -131,6 +133,14 @@ fun SceneScope.CollapsedShadeHeader( { Row(horizontalArrangement = Arrangement.End) { SystemIconContainer { + when (LocalWindowSizeClass.current.widthSizeClass) { + WindowWidthSizeClass.Medium, + WindowWidthSizeClass.Expanded -> + ShadeCarrierGroup( + viewModel = viewModel, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, diff --git a/packages/SystemUI/customization/res/values-af/strings.xml b/packages/SystemUI/customization/res/values-af/strings.xml new file mode 100644 index 000000000000..4c2c6275e6d8 --- /dev/null +++ b/packages/SystemUI/customization/res/values-af/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Verstek vir digitaal"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-am/strings.xml b/packages/SystemUI/customization/res/values-am/strings.xml new file mode 100644 index 000000000000..847d7a541c95 --- /dev/null +++ b/packages/SystemUI/customization/res/values-am/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ዲጂታል ነባሪ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ar/strings.xml b/packages/SystemUI/customization/res/values-ar/strings.xml new file mode 100644 index 000000000000..57d1612b337d --- /dev/null +++ b/packages/SystemUI/customization/res/values-ar/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"رقمية تلقائية"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-as/strings.xml b/packages/SystemUI/customization/res/values-as/strings.xml new file mode 100644 index 000000000000..2f3b64b9ceb6 --- /dev/null +++ b/packages/SystemUI/customization/res/values-as/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটেল ডিফ’ল্ট"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-az/strings.xml b/packages/SystemUI/customization/res/values-az/strings.xml new file mode 100644 index 000000000000..eb52f95a4525 --- /dev/null +++ b/packages/SystemUI/customization/res/values-az/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Rəqəmsal defolt"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..90e6678bdcd6 --- /dev/null +++ b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalni podrazumevani"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-be/strings.xml b/packages/SystemUI/customization/res/values-be/strings.xml new file mode 100644 index 000000000000..f327da2a40a4 --- /dev/null +++ b/packages/SystemUI/customization/res/values-be/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"электронны, стандартны шрыфт"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-bg/strings.xml b/packages/SystemUI/customization/res/values-bg/strings.xml new file mode 100644 index 000000000000..6e3754a62a29 --- /dev/null +++ b/packages/SystemUI/customization/res/values-bg/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Стандартно дигитално"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-bn/strings.xml b/packages/SystemUI/customization/res/values-bn/strings.xml new file mode 100644 index 000000000000..adf1256b6c82 --- /dev/null +++ b/packages/SystemUI/customization/res/values-bn/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটাল ডিফল্ট"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-bs/strings.xml b/packages/SystemUI/customization/res/values-bs/strings.xml new file mode 100644 index 000000000000..8de04ab06503 --- /dev/null +++ b/packages/SystemUI/customization/res/values-bs/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalno zadano"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ca/strings.xml b/packages/SystemUI/customization/res/values-ca/strings.xml new file mode 100644 index 000000000000..967bb3f6d2f5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ca/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminat"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-cs/strings.xml b/packages/SystemUI/customization/res/values-cs/strings.xml new file mode 100644 index 000000000000..45da4d759ad4 --- /dev/null +++ b/packages/SystemUI/customization/res/values-cs/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitální výchozí"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-da/strings.xml b/packages/SystemUI/customization/res/values-da/strings.xml new file mode 100644 index 000000000000..3ffaa8b167c8 --- /dev/null +++ b/packages/SystemUI/customization/res/values-da/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Standard (digital)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-de/strings.xml b/packages/SystemUI/customization/res/values-de/strings.xml new file mode 100644 index 000000000000..64f95e05b245 --- /dev/null +++ b/packages/SystemUI/customization/res/values-de/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital (Standard)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-el/strings.xml b/packages/SystemUI/customization/res/values-el/strings.xml new file mode 100644 index 000000000000..57134b566d08 --- /dev/null +++ b/packages/SystemUI/customization/res/values-el/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Ψηφιακή προεπιλογή"</string> +</resources>
\ No newline at end of file diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl b/packages/SystemUI/customization/res/values-en-rAU/strings.xml index 9f6ff58ed5ce..a6110d5d5b09 100644 --- a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl +++ b/packages/SystemUI/customization/res/values-en-rAU/strings.xml @@ -1,5 +1,7 @@ -/* - * Copyright (c) 2016, The Android Open Source Project +<?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. @@ -13,10 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package android.security.keymaster; - -/* The cpp_header is relative to system/security/keystore/include - * Link against libkeystore_binder to make use of the native implementation of this Parcelable. - */ -parcelable KeyAttestationApplicationId cpp_header "keystore/KeyAttestationApplicationId.h"; +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-en-rCA/strings.xml index f8b843bc032f..79919c07d189 100644 --- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl +++ b/packages/SystemUI/customization/res/values-en-rCA/strings.xml @@ -1,5 +1,7 @@ -/* - * Copyright (c) 2016, The Android Open Source Project +<?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. @@ -13,10 +15,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package android.security.keymaster; - -/* The cpp_header is relative to system/security/keystore/include - * Link against libkeystore_binder to make use of the native implementation of this Parcelable. - */ -parcelable KeyAttestationPackageInfo cpp_header "keystore/KeyAttestationPackageInfo.h"; +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for clock_default_description (5309401440896597541) --> + <skip /> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rGB/strings.xml b/packages/SystemUI/customization/res/values-en-rGB/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rGB/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rIN/strings.xml b/packages/SystemUI/customization/res/values-en-rIN/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rIN/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rXC/strings.xml b/packages/SystemUI/customization/res/values-en-rXC/strings.xml new file mode 100644 index 000000000000..7c540dab2cfe --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rXC/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-es-rUS/strings.xml b/packages/SystemUI/customization/res/values-es-rUS/strings.xml new file mode 100644 index 000000000000..59be786849e6 --- /dev/null +++ b/packages/SystemUI/customization/res/values-es-rUS/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Configuración predeterminada digital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-es/strings.xml b/packages/SystemUI/customization/res/values-es/strings.xml new file mode 100644 index 000000000000..03ef0e5a8cea --- /dev/null +++ b/packages/SystemUI/customization/res/values-es/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminada"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-et/strings.xml b/packages/SystemUI/customization/res/values-et/strings.xml new file mode 100644 index 000000000000..fec793e99619 --- /dev/null +++ b/packages/SystemUI/customization/res/values-et/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitaalne vaikimisi"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-eu/strings.xml b/packages/SystemUI/customization/res/values-eu/strings.xml new file mode 100644 index 000000000000..a70c8085ccf0 --- /dev/null +++ b/packages/SystemUI/customization/res/values-eu/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital lehenetsia"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fa/strings.xml b/packages/SystemUI/customization/res/values-fa/strings.xml new file mode 100644 index 000000000000..6426d5112528 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fa/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"دیجیتال پیشفرض"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fi/strings.xml b/packages/SystemUI/customization/res/values-fi/strings.xml new file mode 100644 index 000000000000..9b19373a3765 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fi/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitaalinen (oletus)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fr-rCA/strings.xml b/packages/SystemUI/customization/res/values-fr-rCA/strings.xml new file mode 100644 index 000000000000..bbd1208b1922 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fr-rCA/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Numérique, par défaut"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fr/strings.xml b/packages/SystemUI/customization/res/values-fr/strings.xml new file mode 100644 index 000000000000..5579a427fe71 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fr/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Numérique par défaut"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-gl/strings.xml b/packages/SystemUI/customization/res/values-gl/strings.xml new file mode 100644 index 000000000000..2da93c6f1e34 --- /dev/null +++ b/packages/SystemUI/customization/res/values-gl/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Predeterminada dixital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-gu/strings.xml b/packages/SystemUI/customization/res/values-gu/strings.xml new file mode 100644 index 000000000000..c578a2e2d322 --- /dev/null +++ b/packages/SystemUI/customization/res/values-gu/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ડિજિટલ ડિફૉલ્ટ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hi/strings.xml b/packages/SystemUI/customization/res/values-hi/strings.xml new file mode 100644 index 000000000000..6080f802af04 --- /dev/null +++ b/packages/SystemUI/customization/res/values-hi/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफ़ॉल्ट"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hr/strings.xml b/packages/SystemUI/customization/res/values-hr/strings.xml new file mode 100644 index 000000000000..0a1440fb683b --- /dev/null +++ b/packages/SystemUI/customization/res/values-hr/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalni zadani"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hu/strings.xml b/packages/SystemUI/customization/res/values-hu/strings.xml new file mode 100644 index 000000000000..32618a869a1f --- /dev/null +++ b/packages/SystemUI/customization/res/values-hu/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitális, alapértelmezett"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hy/strings.xml b/packages/SystemUI/customization/res/values-hy/strings.xml new file mode 100644 index 000000000000..d45afbf256ab --- /dev/null +++ b/packages/SystemUI/customization/res/values-hy/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Թվային, կանխադրված"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-in/strings.xml b/packages/SystemUI/customization/res/values-in/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-in/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-is/strings.xml b/packages/SystemUI/customization/res/values-is/strings.xml new file mode 100644 index 000000000000..5a370d614f0f --- /dev/null +++ b/packages/SystemUI/customization/res/values-is/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Stafræn, sjálfgefið"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-it/strings.xml b/packages/SystemUI/customization/res/values-it/strings.xml new file mode 100644 index 000000000000..2a5087d30669 --- /dev/null +++ b/packages/SystemUI/customization/res/values-it/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitale - predefinito"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-iw/strings.xml b/packages/SystemUI/customization/res/values-iw/strings.xml new file mode 100644 index 000000000000..ddd28f21ad99 --- /dev/null +++ b/packages/SystemUI/customization/res/values-iw/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"דיגיטלי ברירת מחדל"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ja/strings.xml b/packages/SystemUI/customization/res/values-ja/strings.xml new file mode 100644 index 000000000000..744604a17efa --- /dev/null +++ b/packages/SystemUI/customization/res/values-ja/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"デジタル デフォルト"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ka/strings.xml b/packages/SystemUI/customization/res/values-ka/strings.xml new file mode 100644 index 000000000000..88ba1dfc0976 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ka/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ციფრული ნაგულისხმევი"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-kk/strings.xml b/packages/SystemUI/customization/res/values-kk/strings.xml new file mode 100644 index 000000000000..9ee6522c49ce --- /dev/null +++ b/packages/SystemUI/customization/res/values-kk/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Цифрлық әдепкі"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-km/strings.xml b/packages/SystemUI/customization/res/values-km/strings.xml new file mode 100644 index 000000000000..bbc438a69bcd --- /dev/null +++ b/packages/SystemUI/customization/res/values-km/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"លំនាំដើមឌីជីថល"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-kn/strings.xml b/packages/SystemUI/customization/res/values-kn/strings.xml new file mode 100644 index 000000000000..e67319d4021b --- /dev/null +++ b/packages/SystemUI/customization/res/values-kn/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ಡಿಜಿಟಲ್ ಡೀಫಾಲ್ಟ್"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ko/strings.xml b/packages/SystemUI/customization/res/values-ko/strings.xml new file mode 100644 index 000000000000..fa9103b01e66 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ko/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"디지털 기본"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ky/strings.xml b/packages/SystemUI/customization/res/values-ky/strings.xml new file mode 100644 index 000000000000..76cc5e211a40 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ky/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Демейки санариптик"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-lo/strings.xml b/packages/SystemUI/customization/res/values-lo/strings.xml new file mode 100644 index 000000000000..28f50008bd73 --- /dev/null +++ b/packages/SystemUI/customization/res/values-lo/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ດິຈິຕອນຕາມຄ່າເລີ່ມຕົ້ນ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-lt/strings.xml b/packages/SystemUI/customization/res/values-lt/strings.xml new file mode 100644 index 000000000000..2fe731547762 --- /dev/null +++ b/packages/SystemUI/customization/res/values-lt/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Skaitmeninis numatytasis"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-lv/strings.xml b/packages/SystemUI/customization/res/values-lv/strings.xml new file mode 100644 index 000000000000..e0b904a8a1c9 --- /dev/null +++ b/packages/SystemUI/customization/res/values-lv/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitālais pulkstenis — noklusējums"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-mk/strings.xml b/packages/SystemUI/customization/res/values-mk/strings.xml new file mode 100644 index 000000000000..9b95a6e32b31 --- /dev/null +++ b/packages/SystemUI/customization/res/values-mk/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Дигитален стандарден приказ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ml/strings.xml b/packages/SystemUI/customization/res/values-ml/strings.xml new file mode 100644 index 000000000000..7f6be8a59904 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ml/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ഡിജിറ്റൽ ഡിഫോൾട്ട്"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-mn/strings.xml b/packages/SystemUI/customization/res/values-mn/strings.xml new file mode 100644 index 000000000000..38369b6f527d --- /dev/null +++ b/packages/SystemUI/customization/res/values-mn/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Дижитал өгөгдмөл"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-mr/strings.xml b/packages/SystemUI/customization/res/values-mr/strings.xml new file mode 100644 index 000000000000..821ff100ab13 --- /dev/null +++ b/packages/SystemUI/customization/res/values-mr/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डीफॉल्टसह क्लॉक फेस"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ms/strings.xml b/packages/SystemUI/customization/res/values-ms/strings.xml new file mode 100644 index 000000000000..2f61b47a2324 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ms/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital lalai"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-my/strings.xml b/packages/SystemUI/customization/res/values-my/strings.xml new file mode 100644 index 000000000000..3d137ebc3abb --- /dev/null +++ b/packages/SystemUI/customization/res/values-my/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ဒစ်ဂျစ်တယ်နာရီ မူရင်း"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-nb/strings.xml b/packages/SystemUI/customization/res/values-nb/strings.xml new file mode 100644 index 000000000000..6eb4373c448c --- /dev/null +++ b/packages/SystemUI/customization/res/values-nb/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital – standard"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ne/strings.xml b/packages/SystemUI/customization/res/values-ne/strings.xml new file mode 100644 index 000000000000..c5b087744a18 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ne/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफल्ट"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-nl/strings.xml b/packages/SystemUI/customization/res/values-nl/strings.xml new file mode 100644 index 000000000000..4f46ab8b2ba5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-nl/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Standaard digitaal"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-or/strings.xml b/packages/SystemUI/customization/res/values-or/strings.xml new file mode 100644 index 000000000000..a74017f6b689 --- /dev/null +++ b/packages/SystemUI/customization/res/values-or/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ଡିଜିଟାଲ ଡିଫଲ୍ଟ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pa/strings.xml b/packages/SystemUI/customization/res/values-pa/strings.xml new file mode 100644 index 000000000000..a77661a8f56b --- /dev/null +++ b/packages/SystemUI/customization/res/values-pa/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ਡਿਜੀਟਲ ਡਿਫਾਲਟ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pl/strings.xml b/packages/SystemUI/customization/res/values-pl/strings.xml new file mode 100644 index 000000000000..6f5b6f280dd1 --- /dev/null +++ b/packages/SystemUI/customization/res/values-pl/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Cyfrowa domyślna"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pt-rPT/strings.xml b/packages/SystemUI/customization/res/values-pt-rPT/strings.xml new file mode 100644 index 000000000000..c6c3cc046965 --- /dev/null +++ b/packages/SystemUI/customization/res/values-pt-rPT/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Predefinição digital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pt/strings.xml b/packages/SystemUI/customization/res/values-pt/strings.xml new file mode 100644 index 000000000000..bbe435543211 --- /dev/null +++ b/packages/SystemUI/customization/res/values-pt/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital padrão"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ro/strings.xml b/packages/SystemUI/customization/res/values-ro/strings.xml new file mode 100644 index 000000000000..ef163e96f79f --- /dev/null +++ b/packages/SystemUI/customization/res/values-ro/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Implicit digital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ru/strings.xml b/packages/SystemUI/customization/res/values-ru/strings.xml new file mode 100644 index 000000000000..5ee928e0fa37 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ru/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Цифровые часы, стандартный"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-si/strings.xml b/packages/SystemUI/customization/res/values-si/strings.xml new file mode 100644 index 000000000000..caf9610e921e --- /dev/null +++ b/packages/SystemUI/customization/res/values-si/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ඩිජිටල් පෙරනිමිය"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sk/strings.xml b/packages/SystemUI/customization/res/values-sk/strings.xml new file mode 100644 index 000000000000..1843f971d251 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sk/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitálne predvolené"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sl/strings.xml b/packages/SystemUI/customization/res/values-sl/strings.xml new file mode 100644 index 000000000000..12df66f571d3 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sl/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalna (privzeta)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sq/strings.xml b/packages/SystemUI/customization/res/values-sq/strings.xml new file mode 100644 index 000000000000..1fc9f252175f --- /dev/null +++ b/packages/SystemUI/customization/res/values-sq/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Dixhitale e parazgjedhur"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sr/strings.xml b/packages/SystemUI/customization/res/values-sr/strings.xml new file mode 100644 index 000000000000..6b127c9f79e5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sr/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Дигитални подразумевани"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sv/strings.xml b/packages/SystemUI/customization/res/values-sv/strings.xml new file mode 100644 index 000000000000..84ad25c96655 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sv/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital standard"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sw/strings.xml b/packages/SystemUI/customization/res/values-sw/strings.xml new file mode 100644 index 000000000000..e2ec3de573a8 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sw/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Dijitali chaguomsingi"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sw400dp/dimens.xml b/packages/SystemUI/customization/res/values-sw400dp/dimens.xml new file mode 100644 index 000000000000..3c9e0789b5f2 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sw400dp/dimens.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. +*/ +--> +<resources> + <dimen name="presentation_clock_text_size">170dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ta/strings.xml b/packages/SystemUI/customization/res/values-ta/strings.xml new file mode 100644 index 000000000000..f4eea07b0aa5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ta/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"டிஜிட்டல் இயல்பு"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-te/strings.xml b/packages/SystemUI/customization/res/values-te/strings.xml new file mode 100644 index 000000000000..c7c77d527e7f --- /dev/null +++ b/packages/SystemUI/customization/res/values-te/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"డిజిటల్ ఆటోమేటిక్ సెట్టింగ్"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-th/strings.xml b/packages/SystemUI/customization/res/values-th/strings.xml new file mode 100644 index 000000000000..61d880eb1ad9 --- /dev/null +++ b/packages/SystemUI/customization/res/values-th/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ดิจิทัลแบบเริ่มต้น"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-tl/strings.xml b/packages/SystemUI/customization/res/values-tl/strings.xml new file mode 100644 index 000000000000..a3484a7b6d2a --- /dev/null +++ b/packages/SystemUI/customization/res/values-tl/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital na default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-tr/strings.xml b/packages/SystemUI/customization/res/values-tr/strings.xml new file mode 100644 index 000000000000..a90e9852ef2f --- /dev/null +++ b/packages/SystemUI/customization/res/values-tr/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Dijital varsayılan"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-uk/strings.xml b/packages/SystemUI/customization/res/values-uk/strings.xml new file mode 100644 index 000000000000..ee9b77b905a2 --- /dev/null +++ b/packages/SystemUI/customization/res/values-uk/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Цифровий, стандартний"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ur/strings.xml b/packages/SystemUI/customization/res/values-ur/strings.xml new file mode 100644 index 000000000000..06a6a7cd01fd --- /dev/null +++ b/packages/SystemUI/customization/res/values-ur/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ڈیجیٹل ڈیفالٹ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-uz/strings.xml b/packages/SystemUI/customization/res/values-uz/strings.xml new file mode 100644 index 000000000000..6b31b048b4a1 --- /dev/null +++ b/packages/SystemUI/customization/res/values-uz/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Raqamli soat, standart"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-vi/strings.xml b/packages/SystemUI/customization/res/values-vi/strings.xml new file mode 100644 index 000000000000..830b6e2b16a1 --- /dev/null +++ b/packages/SystemUI/customization/res/values-vi/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Mặt số mặc định"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zh-rCN/strings.xml b/packages/SystemUI/customization/res/values-zh-rCN/strings.xml new file mode 100644 index 000000000000..747567e9fb65 --- /dev/null +++ b/packages/SystemUI/customization/res/values-zh-rCN/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"默认数字"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zh-rHK/strings.xml b/packages/SystemUI/customization/res/values-zh-rHK/strings.xml new file mode 100644 index 000000000000..c19cc68ff150 --- /dev/null +++ b/packages/SystemUI/customization/res/values-zh-rHK/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"數碼時鐘 (預設)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zh-rTW/strings.xml b/packages/SystemUI/customization/res/values-zh-rTW/strings.xml new file mode 100644 index 000000000000..6fcd313a291d --- /dev/null +++ b/packages/SystemUI/customization/res/values-zh-rTW/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"數位預設"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zu/strings.xml b/packages/SystemUI/customization/res/values-zu/strings.xml new file mode 100644 index 000000000000..c87c250ae1b9 --- /dev/null +++ b/packages/SystemUI/customization/res/values-zu/strings.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. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Okuzenzakalelayo kwedijithali"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index 8eb8132b07b9..c574d1fc674b 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -17,6 +17,7 @@ --> <resources> <!-- Clock maximum font size (dp is intentional, to prevent any further scaling) --> + <dimen name="presentation_clock_text_size">50dp</dimen> <dimen name="large_clock_text_size">150dp</dimen> <dimen name="small_clock_text_size">86dp</dimen> diff --git a/packages/SystemUI/customization/res/values/strings.xml b/packages/SystemUI/customization/res/values/strings.xml new file mode 100644 index 000000000000..897c842b6d4b --- /dev/null +++ b/packages/SystemUI/customization/res/values/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- default clock face name [CHAR LIMIT=NONE]--> + <string name="clock_default_name">Default</string> + + <!-- default clock face description [CHAR LIMIT=NONE]--> + <string name="clock_default_description">Digital default</string> +</resources>
\ No newline at end of file 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 b28920c590c5..b076b2cacf08 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 @@ -65,7 +65,13 @@ class DefaultClockController( protected var onSecondaryDisplay: Boolean = false override val events: DefaultClockEvents - override val config = ClockConfig(DEFAULT_CLOCK_ID) + override val config: ClockConfig by lazy { + ClockConfig( + DEFAULT_CLOCK_ID, + resources.getString(R.string.clock_default_name), + resources.getString(R.string.clock_default_description) + ) + } init { val parent = FrameLayout(ctx) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index 949641a7f75e..dd52e39488ac 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -25,7 +25,6 @@ import com.android.systemui.plugins.ClockProvider import com.android.systemui.plugins.ClockSettings private val TAG = DefaultClockProvider::class.simpleName -const val DEFAULT_CLOCK_NAME = "Default Clock" const val DEFAULT_CLOCK_ID = "DEFAULT" /** Provides the default system clock */ @@ -35,8 +34,7 @@ class DefaultClockProvider( val resources: Resources, val hasStepClockAnimation: Boolean = false ) : ClockProvider { - override fun getClocks(): List<ClockMetadata> = - listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME)) + override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID)) override fun createClock(settings: ClockSettings): ClockController { if (settings.clockId != DEFAULT_CLOCK_ID) { 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 e2f4793b8f91..485c27e16150 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -192,15 +192,18 @@ enum class ClockTickRate(val value: Int) { /** Some data about a clock design */ data class ClockMetadata( val clockId: ClockId, - val name: String, -) { - constructor(clockId: ClockId) : this(clockId, clockId) {} -} +) /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ data class ClockConfig( val id: String, + /** Localized name of the clock */ + val name: String, + + /** Localized accessibility description for the clock */ + val description: String, + /** Transition to AOD should move smartspace like large clock instead of small clock */ val useAlternateSmartspaceAODTransition: Boolean = false, diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml new file mode 100644 index 000000000000..218735229a5d --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.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. + ~ + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:id="@+id/alternate_bouncer" + android:focusable="true" + android:clickable="true" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.systemui.scrim.ScrimView + android:id="@+id/alternate_bouncer_scrim" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:importantForAccessibility="no" + sysui:ignoreRightInset="true" + /> +</FrameLayout> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml index 593f507f3c88..f68ab8110b6d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml @@ -16,22 +16,30 @@ */ --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" 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_width="0dp" + android:layout_height="0dp" android:layout_gravity="center" - android:orientation="vertical"> + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + <include android:id="@+id/keyguard_clock_container" layout="@layout/keyguard_clock_switch" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content"/> </com.android.keyguard.KeyguardStatusView> -</FrameLayout> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index 69f533c3bd47..67b4e4bc322b 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml @@ -125,7 +125,7 @@ <string name="clock_title_bubble" msgid="2204559396790593213">"বাবল"</string> <string name="clock_title_analog" msgid="8409262532900918273">"অ্যানালগ"</string> <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"চালিয়ে যেতে আপনার ডিভাইস আনলক করুন"</string> - <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"পরে ইনস্টল আপডেট করতে পিন লিখুন"</string> + <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"পরে আপডেট ইনস্টল করতে পিন লিখুন"</string> <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"পরে আপডেট ইনস্টল করতে পাসওয়ার্ড লিখুন"</string> <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"পরে আপডেট ইনস্টল করতে প্যাটার্ন আঁকুন"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"ডিভাইস আপডেট করা হয়েছে। চালিয়ে যেতে পিন লিখুন।"</string> diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml index e075d850ba65..573638bcc135 100644 --- a/packages/SystemUI/res-keyguard/values-cs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml @@ -125,9 +125,9 @@ <string name="clock_title_bubble" msgid="2204559396790593213">"Bublina"</string> <string name="clock_title_analog" msgid="8409262532900918273">"Analogové"</string> <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Pokud chcete pokračovat, odemkněte zařízení"</string> - <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Pokud aktualizaci chcete nainstalovat později, zadejte PIN"</string> - <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Pokud aktualizaci chcete nainstalovat později, zadejte heslo"</string> - <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Pokud aktualizaci chcete nainstalovat později, zadejte gesto"</string> + <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Zadejte PIN a aktualizaci nainstalujte později"</string> + <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Zadejte heslo a aktualizaci nainstalujte později"</string> + <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Zadejte gesto a aktualizaci nainstalujte později"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Zařízení bylo aktualizováno. Pokud chcete pokračovat, zadejte PIN."</string> <string name="kg_prompt_after_update_password" msgid="153703052501352094">"Zařízení bylo aktualizováno. Pokud chcete pokračovat, zadejte heslo."</string> <string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Zařízení bylo aktualizováno. Pokud chcete pokračovat, zadejte gesto."</string> diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml index 117f7a92cf01..5c5f264fe508 100644 --- a/packages/SystemUI/res-keyguard/values-de/strings.xml +++ b/packages/SystemUI/res-keyguard/values-de/strings.xml @@ -125,9 +125,9 @@ <string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string> <string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string> <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Gerät entsperren, um fortzufahren"</string> - <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Gib deine PIN ein, um das Update später zu installieren"</string> - <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Gib dein Passwort ein, um das Update später zu installieren"</string> - <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Zeichne dein Muster, um das Update später zu installieren"</string> + <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"PIN eingeben, um Update später zu installieren"</string> + <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Passwort eingeben, um Update später zu installieren"</string> + <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Muster zeichnen, um Update später zu installieren"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Gerät aktualisiert. Gib deine PIN ein, um fortzufahren."</string> <string name="kg_prompt_after_update_password" msgid="153703052501352094">"Gerät aktualisiert. Gib dein Passwort ein, um fortzufahren."</string> <string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Gerät aktualisiert. Zeichne dein Muster, um fortzufahren."</string> diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml index cd7637c1b1bd..3a01da53dfce 100644 --- a/packages/SystemUI/res-keyguard/values-el/strings.xml +++ b/packages/SystemUI/res-keyguard/values-el/strings.xml @@ -125,9 +125,9 @@ <string name="clock_title_bubble" msgid="2204559396790593213">"Συννεφάκι"</string> <string name="clock_title_analog" msgid="8409262532900918273">"Αναλογικό"</string> <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Ξεκλειδώστε τη συσκευή σας για να συνεχίσετε"</string> - <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Εισαγάγετε το PIN για να εγκαταστήσετε την ενημέρωση αργότερα"</string> + <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Εισαγωγή PIN για εγκατάσταση ενημέρωσης αργότερα"</string> <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Εισαγ. τον κωδ. πρόσβασης για να εγκαταστήσετε την ενημέρωση αργότερα"</string> - <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Σχεδιάστε το μοτίβο για να εγκαταστήσετε την ενημέρωση αργότερα"</string> + <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Σχεδιάστε το μοτίβο για εγκατάσταση της ενημέρωσης αργότερα"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Η συσκευή ενημερώθηκε. Εισαγάγετε το PIN για να συνεχίσετε."</string> <string name="kg_prompt_after_update_password" msgid="153703052501352094">"Η συσκευή ενημερώθηκε. Εισαγάγ. τον κωδ. πρόσβασης για να συνεχίσετε."</string> <string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Η συσκευή ενημερώθηκε. Σχεδιάστε το μοτίβο για να συνεχίσετε."</string> diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index 4815815a1dc9..ae3f04a290e4 100644 --- a/packages/SystemUI/res-keyguard/values-fa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml @@ -125,9 +125,9 @@ <string name="clock_title_bubble" msgid="2204559396790593213">"حباب"</string> <string name="clock_title_analog" msgid="8409262532900918273">"آنالوگ"</string> <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"برای ادامه، قفل دستگاهتان را باز کنید"</string> - <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"برای نصب بهروزرسانی در فرصتی دیگر، پین را وارد کنید"</string> - <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"برای نصب بهروزرسانی در فرصتی دیگر، گذرواژه را وارد کنید"</string> - <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"برای نصب بهروزرسانی در فرصتی دیگر، الگو را وارد کنید"</string> + <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"پین را وارد کنید و بهروزرسانی را در فرصتی دیگر انجام دهید"</string> + <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"گذرواژه را وارد کنید و بهروزرسانی را در فرصتی دیگر انجام دهید"</string> + <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"الگو را وارد کنید و بهروزرسانی را در فرصتی دیگر انجام دهید"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"دستگاه بهروز شد. برای ادامه، پین را وارد کنید."</string> <string name="kg_prompt_after_update_password" msgid="153703052501352094">"دستگاه بهروز شد. برای ادامه، گذرواژه را وارد کنید."</string> <string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"دستگاه بهروز شد. برای ادامه، الگو را وارد کنید."</string> diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml index 02d41d8a22be..050df9983725 100644 --- a/packages/SystemUI/res-keyguard/values-fi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml @@ -127,7 +127,7 @@ <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Jatka avaamalla laitteen lukitus"</string> <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Jos haluat asentaa päivityksen myöhemmin, lisää PIN-koodi"</string> <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Jos haluat asentaa päivityksen myöhemmin, lisää salasana"</string> - <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Jos haluat asentaa päivityksen myöhemmin, piirrä kuvio"</string> + <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Salli päivitys myöhemmin piirtämällä kuvio"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Laite päivitetty. Jatka lisäämällä PIN-koodi."</string> <string name="kg_prompt_after_update_password" msgid="153703052501352094">"Laite päivitetty. Jatka lisäämällä salasana."</string> <string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Laite päivitetty. Jatka piirtämällä kuvio."</string> diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml index e5be788f7676..4309b56adc88 100644 --- a/packages/SystemUI/res-keyguard/values-ro/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml @@ -127,7 +127,7 @@ <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Deblochează dispozitivul pentru a continua"</string> <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Introdu codul PIN pentru a instala actualizarea mai târziu"</string> <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Introdu parola pentru a instala actualizarea mai târziu"</string> - <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Desenează modelul pentru a instala actualizarea mai târziu"</string> + <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Desenează pentru a instala actualizarea mai târziu"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Dispozitivul s-a actualizat. Introdu codul PIN pentru a continua."</string> <string name="kg_prompt_after_update_password" msgid="153703052501352094">"Dispozitivul s-a actualizat. Introdu parola pentru a continua."</string> <string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Dispozitivul s-a actualizat. Desenează modelul pentru a continua."</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml index 59261a3d414b..4c65832162ba 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml @@ -125,9 +125,9 @@ <string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string> <string name="clock_title_analog" msgid="8409262532900918273">"指针"</string> <string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"解锁设备才能继续操作"</string> - <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"需要输入 PIN 码才能稍后安装更新"</string> - <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"需要输入密码才能稍后安装更新"</string> - <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"需要绘制解锁图案才能稍后安装更新"</string> + <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"请输入 PIN 码,系统稍后会安装更新"</string> + <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"请输入密码,系统稍后会安装更新"</string> + <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"请绘制解锁图案,系统稍后会安装更新"</string> <string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"设备已更新。您需要输入 PIN 码才能继续。"</string> <string name="kg_prompt_after_update_password" msgid="153703052501352094">"设备已更新。您需要输入密码才能继续。"</string> <string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"设备已更新。您需要绘制解锁图案才能继续。"</string> diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml index 1c7e9977afe5..6d779438ad6c 100644 --- a/packages/SystemUI/res/layout/bluetooth_device_item.xml +++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml @@ -27,7 +27,6 @@ <ImageView android:id="@+id/bluetooth_device_icon" - android:contentDescription="@string/accessibility_bluetooth_device_icon" android:layout_width="24dp" android:layout_height="24dp" app:layout_constraintStart_toStartOf="parent" @@ -39,8 +38,12 @@ android:layout_width="0dp" android:id="@+id/bluetooth_device_name" style="@style/BluetoothTileDialog.DeviceName" + android:textDirection="locale" + android:textAlignment="gravity" android:paddingStart="20dp" android:paddingTop="10dp" + android:maxLines="1" + android:ellipsize="end" app:layout_constraintWidth_percent="0.7" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" @@ -55,6 +58,8 @@ style="@style/BluetoothTileDialog.DeviceSummary" android:paddingStart="20dp" android:paddingBottom="10dp" + android:maxLines="1" + android:ellipsize="end" app:layout_constraintWidth_percent="0.7" app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name" app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" @@ -66,6 +71,7 @@ android:id="@+id/gear_icon" android:layout_width="0dp" android:layout_height="0dp" + android:contentDescription="@string/accessibility_bluetooth_device_settings_gear" app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name" app:layout_constraintEnd_toEndOf="@+id/gear_icon_image" app:layout_constraintTop_toTopOf="parent" diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml index 16aeb951822c..5d986e00deed 100644 --- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml +++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml @@ -27,6 +27,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="24dp" + android:maxLines="1" android:ellipsize="end" android:gravity="center_vertical|center_horizontal" android:text="@string/quick_settings_bluetooth_label" @@ -58,9 +59,12 @@ style="@style/BluetoothTileDialog.Device" android:layout_width="0dp" android:layout_height="64dp" + android:maxLines="1" + android:ellipsize="end" android:gravity="center_vertical" android:layout_marginTop="4dp" android:text="@string/turn_on_bluetooth" + android:clickable="false" android:textAppearance="@style/TextAppearance.Dialog.Body.Message" android:textSize="16sp" app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle" @@ -84,53 +88,17 @@ app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title" app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" /> - <androidx.constraintlayout.widget.Group - android:id="@+id/pair_new_device_layout_group" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="gone" - app:constraint_referenced_ids="ic_add,pair_new_device_text" /> - - <ImageView - android:id="@+id/ic_add" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="36dp" - android:gravity="center_vertical" - android:importantForAccessibility="no" - android:src="@drawable/ic_add" - app:layout_constraintBottom_toTopOf="@id/device_list" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toStartOf="@id/pair_new_device_text" - app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title" - android:tint="?android:attr/textColorPrimary" /> - - <TextView - android:id="@+id/pair_new_device_text" - style="@style/BluetoothTileDialog.Device" - android:layout_width="0dp" - android:layout_height="@dimen/bluetooth_dialog_device_height" - android:gravity="center_vertical" - android:layout_marginStart="0dp" - android:paddingStart="20dp" - android:text="@string/pair_new_bluetooth_devices" - android:textSize="14sp" - android:textAppearance="@style/TextAppearance.Dialog.Title" - app:layout_constraintBottom_toTopOf="@id/device_list" - app:layout_constraintStart_toEndOf="@+id/ic_add" - app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title" - app:layout_constraintEnd_toEndOf="parent" /> - <androidx.recyclerview.widget.RecyclerView android:id="@+id/device_list" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="20dp" android:nestedScrollingEnabled="false" android:overScrollMode="never" android:scrollbars="vertical" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/pair_new_device_text" + app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle" app:layout_constraintBottom_toTopOf="@+id/see_all_text" /> <androidx.constraintlayout.widget.Group @@ -148,7 +116,7 @@ android:importantForAccessibility="no" android:gravity="center_vertical" android:src="@drawable/ic_arrow_forward" - app:layout_constraintBottom_toTopOf="@+id/done_button" + app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@id/see_all_text" app:layout_constraintTop_toBottomOf="@id/device_list" /> @@ -157,18 +125,59 @@ android:id="@+id/see_all_text" style="@style/BluetoothTileDialog.Device" android:layout_width="0dp" - android:layout_height="@dimen/bluetooth_dialog_device_height" + android:layout_height="64dp" + android:maxLines="1" + android:ellipsize="end" android:gravity="center_vertical" android:layout_marginStart="0dp" android:paddingStart="20dp" android:text="@string/see_all_bluetooth_devices" android:textSize="14sp" android:textAppearance="@style/TextAppearance.Dialog.Title" - app:layout_constraintBottom_toTopOf="@+id/done_button" + app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text" app:layout_constraintStart_toEndOf="@+id/ic_arrow" app:layout_constraintTop_toBottomOf="@id/device_list" app:layout_constraintEnd_toEndOf="parent" /> + <androidx.constraintlayout.widget.Group + android:id="@+id/pair_new_device_layout_group" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + app:constraint_referenced_ids="ic_add,pair_new_device_text" /> + + <ImageView + android:id="@+id/ic_add" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="36dp" + android:gravity="center_vertical" + android:importantForAccessibility="no" + android:src="@drawable/ic_add" + app:layout_constraintBottom_toTopOf="@id/done_button" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/pair_new_device_text" + app:layout_constraintTop_toBottomOf="@id/see_all_text" + android:tint="?android:attr/textColorPrimary" /> + + <TextView + android:id="@+id/pair_new_device_text" + style="@style/BluetoothTileDialog.Device" + android:layout_width="0dp" + android:layout_height="64dp" + android:maxLines="1" + android:ellipsize="end" + android:gravity="center_vertical" + android:layout_marginStart="0dp" + android:paddingStart="20dp" + android:text="@string/pair_new_bluetooth_devices" + android:textSize="14sp" + android:textAppearance="@style/TextAppearance.Dialog.Title" + app:layout_constraintBottom_toTopOf="@id/done_button" + app:layout_constraintStart_toEndOf="@+id/ic_add" + app:layout_constraintTop_toBottomOf="@id/see_all_text" + app:layout_constraintEnd_toEndOf="parent" /> + <Button android:id="@+id/done_button" style="@style/Widget.Dialog.Button" @@ -184,5 +193,5 @@ android:text="@string/inline_done_button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/see_all_text" /> + app:layout_constraintTop_toBottomOf="@id/pair_new_device_text" /> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml index ec006c553b94..16eba220cf5d 100644 --- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml +++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml @@ -403,7 +403,7 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="start|center_vertical" - android:orientation="vertical"> + android:orientation="horizontal"> <Button android:id="@+id/apm_button" android:layout_width="wrap_content" @@ -414,12 +414,7 @@ style="@style/Widget.Dialog.Button.BorderButton" android:clickable="true" android:focusable="true"/> - </LinearLayout> - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical"> <Button android:id="@+id/share_wifi_button" android:layout_width="wrap_content" @@ -430,8 +425,14 @@ android:ellipsize="end" android:clickable="true" android:focusable="true" - android:layout_alignParentLeft="true" android:visibility="gone"/> + </LinearLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_gravity="end|center_vertical"> <Button android:id="@+id/done_button" android:layout_width="wrap_content" @@ -441,9 +442,8 @@ android:maxLines="1" android:ellipsize="end" android:clickable="true" - android:focusable="true" - android:layout_alignParentRight="true"/> - </RelativeLayout> + android:focusable="true"/> + </LinearLayout> </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml index b00908fd2bfa..c1bac3151049 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml @@ -15,7 +15,7 @@ --> <!-- Extends Framelayout --> -<com.android.systemui.statusbar.notification.row.FooterView +<com.android.systemui.statusbar.notification.footer.ui.view.FooterView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" @@ -76,4 +76,4 @@ /> </androidx.constraintlayout.widget.ConstraintLayout> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> -</com.android.systemui.statusbar.notification.row.FooterView> +</com.android.systemui.statusbar.notification.footer.ui.view.FooterView> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index d4b73a4e3de0..acee4258b64d 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -130,6 +130,11 @@ android:inflatedId="@+id/multi_shade" android:layout="@layout/multi_shade" /> + <include layout="@layout/alternate_bouncer" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="invisible" /> + <com.android.systemui.biometrics.AuthRippleView android:id="@+id/auth_ripple" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 937e97a5fc2b..24846d9ae16e 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stel versteknotasapp in Instellings"</string> <string name="install_app" msgid="5066668100199613936">"Installeer app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Sinkroniseer wedersyds na eksterne skerm?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Sinkroniseer skerm wedersyds"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Maak toe"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofoon en kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Onlangse appgebruik"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Sien onlangse toegang"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index de2fda491f78..23def2894368 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"በቅንብሮች ውስጥ ነባሪ የማስታወሻዎች መተግበሪያን ያቀናብሩ"</string> <string name="install_app" msgid="5066668100199613936">"መተግበሪያን ጫን"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ወደ ውጫዊ ማሳያ ይንጸባረቅ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ማሳያን አንጸባርቅ"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"አሰናብት"</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> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 79b671ccd4e9..80d63a23fd94 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"يمكنك ضبط تطبيق تدوين الملاحظات التلقائي في \"الإعدادات\"."</string> <string name="install_app" msgid="5066668100199613936">"تثبيت التطبيق"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"هل تريد بث محتوى جهازك على الشاشة الخارجية؟"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"بث المحتوى على الشاشة"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"إغلاق"</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> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index ac30f182f521..c84577373919 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ছেটিঙত টোকাৰ ডিফ’ল্ট এপ্ ছেট কৰক"</string> <string name="install_app" msgid="5066668100199613936">"এপ্টো ইনষ্টল কৰক"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"বাহ্যিক ডিছপ্লে’লৈ মিৰ’ৰ কৰিবনে?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ডিছপ্লে’ মিৰ’ৰ কৰক"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"অগ্ৰাহ্য কৰক"</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> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 3ca61dc4f575..5aa26bac0687 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlarda defolt qeydlər tətbiqi ayarlayın"</string> <string name="install_app" msgid="5066668100199613936">"Tətbiqi quraşdırın"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Xarici displeyə əks etdirilsin?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Displeyi əks etdirin"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"İmtina edin"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon və kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Son tətbiq istifadəsi"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Son girişə baxın"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index b51d4b355b19..cd36884e4740 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Podesite podrazumevanu aplikaciju za beleške u Podešavanjima"</string> <string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li da preslikate na spoljnji ekran?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavno koristila aplikacija"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Prikaži nedavni pristup"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 35a84b0afee1..1b03c8ba09f6 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайце ў Наладах стандартную праграму для нататак"</string> <string name="install_app" msgid="5066668100199613936">"Усталяваць праграму"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Адлюстраваць на знешнім дысплеі?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Адлюстраваць дысплэй"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Закрыць"</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> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6b212e565a59..4ed1ad944045 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартно приложение за бележки от настройките"</string> <string name="install_app" msgid="5066668100199613936">"Инсталиране на приложението"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се дублира ли на външния екран?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Огледално копиране на дисплея"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Отхвърляне"</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> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 5b5ac73430e3..426d38d68a0e 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"\'সেটিংস\' থেকে ডিফল্ট নোট নেওয়ার অ্যাপ সেট করুন"</string> <string name="install_app" msgid="5066668100199613936">"অ্যাপ ইনস্টল করুন"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"এক্সটার্নাল ডিসপ্লে আয়না?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ডিসপ্লে দেখান"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"বাতিল করুন"</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> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 33330b737edc..4eed7b8965ba 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u Postavkama"</string> <string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Preslikati na vanjski ekran?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavno korištenje aplikacije"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Prikaži nedavni pristup"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 45bb34253a86..b55a79ad8dd7 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defineix l\'aplicació de notes predeterminada a Configuració"</string> <string name="install_app" msgid="5066668100199613936">"Instal·la l\'aplicació"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vols replicar-ho a la pantalla externa?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Duplica la pantalla"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Ignora"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Micròfon i càmera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Ús recent de l\'aplicació"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Mostra l\'accés recent"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 0d7d23a34ef7..05b04191f6c7 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Výchozí aplikaci pro poznámky nastavíte v Nastavení"</string> <string name="install_app" msgid="5066668100199613936">"Nainstalovat aplikaci"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Zavřít"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon a fotoaparát"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedávné použití aplikacemi"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobrazit nedávný přístup"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 4f6b88ec74b3..5971034b991c 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Angiv standardapp til noter i Indstillinger"</string> <string name="install_app" msgid="5066668100199613936">"Installer app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du spejle til ekstern skærm?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Spejl skærm"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Luk"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon og kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Seneste brug af apps"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Se seneste adgang"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 07aa14f400dc..c773f71d7786 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standard-Notizen-App in den Einstellungen einrichten"</string> <string name="install_app" msgid="5066668100199613936">"App installieren"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Auf externen Bildschirm spiegeln?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Bildschirm spiegeln"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Schließen"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon & Kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Kürzliche App-Nutzung"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Kürzliche Zugriffe ansehen"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 2a8e51cfbc7f..b6c95aa50bfd 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ορίστε την προεπιλεγμένη εφαρμογή σημειώσεων στις Ρυθμίσεις"</string> <string name="install_app" msgid="5066668100199613936">"Εγκατάσταση εφαρμογής"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Κατοπτρισμός σε εξωτερική οθόνη;"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Κατοπτρισμός οθόνης"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Παράβλεψη"</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> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 8647adb2934f..6c2b2307fccf 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -1183,10 +1183,8 @@ <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="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</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> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 8647adb2934f..6c2b2307fccf 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -1183,10 +1183,8 @@ <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="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</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> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 8647adb2934f..6c2b2307fccf 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -1183,10 +1183,8 @@ <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="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</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> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index d3ea858c3137..52514d22f927 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la app de notas predeterminada en Configuración"</string> <string name="install_app" msgid="5066668100199613936">"Instalar app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Quieres duplicar a la pantalla externa?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Duplicar pantalla"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Descartar"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Micrófono y cámara"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso reciente en apps"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver accesos recientes"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 284fad4d5b89..4a3c06412802 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la aplicación de notas predeterminada en Ajustes"</string> <string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Replicar en pantalla externa?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Replicar pantalla"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Cerrar"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Micrófono y cámara"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso reciente en aplicaciones"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver acceso reciente"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 2b79ee5f09c1..08b489d11d7e 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -1183,10 +1183,8 @@ <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="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kas peegeldada välisekraanile?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Ekraani peegeldamine"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Loobu"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon ja kaamera"</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> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 68698ea2cc14..19495bccb4f8 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -676,8 +676,8 @@ <string name="group_system_go_back" msgid="8838454003680364227">"Atzera: itzuli aurreko egoerara (atzera egiteko botoia)"</string> <string name="group_system_access_home_screen" msgid="1857344316928441909">"Atzitu hasierako pantaila"</string> <string name="group_system_overview_open_apps" msgid="6897128761003265350">"Ikusi irekitako aplikazioen ikuspegi orokorra"</string> - <string name="group_system_cycle_forward" msgid="9202444850838205990">"Joan azken aplikazioetako batetik bestera (aurrera)"</string> - <string name="group_system_cycle_back" msgid="5163464503638229131">"Joan azken aplikazioetako batetik bestera (atzera)"</string> + <string name="group_system_cycle_forward" msgid="9202444850838205990">"Joan azkenaldian erabilitako aplikazio batetik bestera (aurrera)"</string> + <string name="group_system_cycle_back" msgid="5163464503638229131">"Joan azkenaldian erabilitako aplikazio batetik bestera (atzera)"</string> <string name="group_system_access_all_apps_search" msgid="488070738028991753">"Atzitu aplikazio guztien zerrenda eta bilatu (adibidez, bilatzeko aukeraren edo Exekutatzeko tresna aplikazioaren bidez)"</string> <string name="group_system_hide_reshow_taskbar" msgid="3809304065624351131">"Ezkutatu eta erakutsi (berriro) zereginen barra"</string> <string name="group_system_access_system_settings" msgid="7961639365383008053">"Atzitu sistemaren ezarpenak"</string> @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ezarri oharren aplikazio lehenetsia ezarpenetan"</string> <string name="install_app" msgid="5066668100199613936">"Instalatu aplikazioa"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kanpoko pantailan islatu nahi duzu?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Islatu pantaila"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Baztertu"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofonoa eta kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Aplikazioen azken erabilera"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ikusi azkenaldiko sarbidea"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 719924037534..68f6c1d9dd88 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"برنامه پیشفرض یادداشت را در «تنظیمات» تنظیم کنید"</string> <string name="install_app" msgid="5066668100199613936">"نصب برنامه"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"در نمایشگر خارجی پخش شود؟"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"بازتاباندن صفحهنمایش"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"بستن"</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> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 073ea60b28f4..934abad4b225 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Aseta oletusmuistiinpanosovellus Asetuksista"</string> <string name="install_app" msgid="5066668100199613936">"Asenna sovellus"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Peilataanko ulkoiselle näytölle?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Peilaa näyttö"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Ohita"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofoni ja kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Sovellusten viimeaikainen käyttö"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Katso viimeaikainen käyttö"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 52bcd7652af3..fe90569169d9 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir l\'application de prise de notes par défaut dans les Paramètres"</string> <string name="install_app" msgid="5066668100199613936">"Installer l\'application"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer l\'écran sur un moniteur externe?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone et appareil photo"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente par les applications"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Afficher l\'accès récent"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 4362d3c227fb..27a4bb6a0e1f 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir une appli de notes par défaut dans les paramètres"</string> <string name="install_app" msgid="5066668100199613936">"Installer l\'appli"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirroring sur écran externe ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Micro et caméra"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente par les applis"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Consulter les accès récents"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 1b836a8ea688..2056c2fba14b 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Establece a aplicación de notas predeterminada en Configuración"</string> <string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Queres proxectar contido nunha pantalla externa?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Replicar pantalla"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Pechar"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Micrófono e cámara"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente por parte de aplicacións"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver acceso recente"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 132ee2e73513..84be50a481e8 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"સેટિંગમાં નોંધની ડિફૉલ્ટ ઍપ સેટ કરો"</string> <string name="install_app" msgid="5066668100199613936">"ઍપ ઇન્સ્ટૉલ કરો"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"શું બાહ્ય ડિસ્પ્લે પર મિરર કરીએ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"મિરર ડિસ્પ્લે"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"છોડી દો"</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> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index f942de505920..2b0019f5274e 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग में जाकर, नोट लेने की सुविधा देने वाले ऐप्लिकेशन को डिफ़ॉल्ट के तौर पर सेट करें"</string> <string name="install_app" msgid="5066668100199613936">"ऐप्लिकेशन इंस्टॉल करें"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाहरी डिसप्ले को अन्य डिवाइस पर दिखाना है?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"मिरर डिसप्ले"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"खारिज करें"</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> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index f7c68e160b63..23899fcb2110 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u postavkama"</string> <string name="install_app" msgid="5066668100199613936">"Instalacija"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li zrcaliti na vanjski zaslon?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Zrcaljenje zaslona"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavna upotreba aplikacije"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Pogledajte nedavni pristup"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index c1b38d904c4d..e5b17d9c6517 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Állítson be alapértelmezett jegyzetkészítő alkalmazást a Beállításokban"</string> <string name="install_app" msgid="5066668100199613936">"Alkalmazás telepítése"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tükrözi a kijelzőt a külső képernyőre?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Kijelző tükrözése"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Elvetés"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon és kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Legutóbbi alkalmazáshasználat"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Legutóbbi hozzáférés"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 7139a1f844da..4a1c291e3d74 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Կարգավորեք նշումների կանխադրված հավելված Կարգավորումներում"</string> <string name="install_app" msgid="5066668100199613936">"Տեղադրել հավելվածը"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Հայելապատճենե՞լ արտաքին էկրանին"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Հայելապատճենել էկրանը"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Փակել"</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> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 44aafde6f132..18a76686154f 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setel aplikasi catatan default di Setelan"</string> <string name="install_app" msgid="5066668100199613936">"Instal aplikasi"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Cerminkan ke layar eksternal?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Cerminkan layar"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Tutup"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon & Kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Penggunaan aplikasi baru-baru ini"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Lihat akses terbaru"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 1fda572ae10e..70bed4665e17 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stilltu sjálfgefið glósuforrit í stillingunum"</string> <string name="install_app" msgid="5066668100199613936">"Setja upp forrit"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spegla yfir á ytri skjá?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Spegla skjá"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Hunsa"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Hljóðnemi og myndavél"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nýlega notað af forriti"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Sjá nýlegan aðgang"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index ed8885f6e360..baffdb9778cd 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Imposta l\'app per le note predefinita nelle Impostazioni"</string> <string name="install_app" msgid="5066668100199613936">"Installa app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vuoi eseguire il mirroring al display esterno?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Esegui il mirroring del display"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Chiudi"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfono e fotocamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente da app"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Vedi accesso recente"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 51f5452d843f..968a98287275 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"צריך להגדיר את אפליקציית ברירת המחדל לפתקים ב\'הגדרות\'"</string> <string name="install_app" msgid="5066668100199613936">"התקנת האפליקציה"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"לשקף למסך חיצוני?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"תצוגת מראה"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"סגירה"</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> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index e77aed9b1be7..798d42add510 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"[設定] でデフォルトのメモアプリを設定してください"</string> <string name="install_app" msgid="5066668100199613936">"アプリをインストール"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"外部ディスプレイにミラーリングしますか?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ディスプレイをミラーリングする"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"閉じる"</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> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 8d7deec249f8..f21a2a42536b 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"დააყენეთ ნაგულისხმევი შენიშვნების აპი პარამეტრებში"</string> <string name="install_app" msgid="5066668100199613936">"აპის ინსტალაცია"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"აირეკლოს გარე ეკრანზე?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ეკრანის არეკვლა"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"დახურვა"</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> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index fc4fe8a2a26d..746c02ed780f 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден әдепкі жазба қолданбасын орнатыңыз."</string> <string name="install_app" msgid="5066668100199613936">"Қолданбаны орнату"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Сыртқы экран арқылы да көрсету керек пе?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Айна дисплей"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Қабылдамау"</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> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 10c291dec4ab..9f3b99121b19 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"កំណត់កម្មវិធីកំណត់ចំណាំលំនាំដើមនៅក្នុងការកំណត់"</string> <string name="install_app" msgid="5066668100199613936">"ដំឡើងកម្មវិធី"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"បញ្ចាំងទៅឧបករណ៍បញ្ចាំងខាងក្រៅឬ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"បញ្ចាំងទៅផ្ទាំងអេក្រង់"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ច្រានចោល"</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> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 77169809b914..79eef7295bc0 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಡೀಫಾಲ್ಟ್ ಟಿಪ್ಪಣಿಗಳ ಆ್ಯಪ್ ಅನ್ನು ಸೆಟ್ ಮಾಡಿ"</string> <string name="install_app" msgid="5066668100199613936">"ಆ್ಯಪ್ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ಬಾಹ್ಯ ಡಿಸ್ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಬೇಕೆ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ಮಿರರ್ ಡಿಸ್ಪ್ಲೇ"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ವಜಾಗೊಳಿಸಿ"</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> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index c2fbf093160c..7985e4c7e3af 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"설정에서 기본 메모 앱 설정"</string> <string name="install_app" msgid="5066668100199613936">"앱 설치"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"외부 디스플레이로 미러링하시겠습니까?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"디스플레이 미러링"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"닫기"</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> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 8ab4cb7d9128..ded1f752ef87 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден демейки кыска жазуулар колдонмосун тууралаңыз"</string> <string name="install_app" msgid="5066668100199613936">"Колдонмону орнотуу"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Тышкы экранга чыгарасызбы?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Тышкы экран"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Жабуу"</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> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index a1585f2d69d7..1e61b8a85fb1 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ຕັ້ງຄ່າແອັບຈົດບັນທຶກເລີ່ມຕົ້ນໃນການຕັ້ງຄ່າ"</string> <string name="install_app" msgid="5066668100199613936">"ຕິດຕັ້ງແອັບ"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ສາຍໃສ່ຈໍສະແດງຜົນພາຍນອກບໍ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ຈໍສະແດງຜົນແບບສະທ້ອນ"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ປິດໄວ້"</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> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index c5f649c82dc0..06c1369d29ad 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nustatykite numatytąją užrašų programą Nustatymuose"</string> <string name="install_app" msgid="5066668100199613936">"Įdiegti programą"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Bendrinti ekrano vaizdą išoriniame ekrane?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Bendrinti ekrano vaizdą"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Atsisakyti"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofonas ir fotoaparatas"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Pastarasis programos naudojimas"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Žr. pastarąją prieigą"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index d49360919a32..fdf30285cee4 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Iestatījumos iestatiet noklusējuma piezīmju lietotni."</string> <string name="install_app" msgid="5066668100199613936">"Instalēt lietotni"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vai spoguļot ārējā displejā?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Spoguļot displeju"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Nerādīt"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofons un kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nesen izmantoja lietotnes"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Skatīt neseno piekļuvi"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 46193b3a6855..80ef7bb64d07 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Поставете стандардна апликација за белешки во „Поставки“"</string> <string name="install_app" msgid="5066668100199613936">"Инсталирајте ја апликацијата"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се синхронизира на надворешниот екран?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Отфрли"</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> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index d63153882e72..5b1c80f37a18 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ക്രമീകരണത്തിൽ കുറിപ്പുകൾക്കുള്ള ഡിഫോൾട്ട് ആപ്പ് സജ്ജീകരിക്കുക"</string> <string name="install_app" msgid="5066668100199613936">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യൂ"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ബാഹ്യ ഡിസ്പ്ലേയിലേക്ക് മിറർ ചെയ്യണോ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"മിറർ ഡിസ്പ്ലേ"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ഡിസ്മിസ് ചെയ്യുക"</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> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index b16b22a4e041..3f65931f276e 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Тохиргоонд тэмдэглэлийн өгөгдмөл апп тохируулна уу"</string> <string name="install_app" msgid="5066668100199613936">"Аппыг суулгах"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Гадны дэлгэцэд тусгал үүсгэх үү?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Дэлгэцийн тусгал үүсгэх"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Хаах"</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> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 16fac23db53f..238aacab4d32 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग्ज मध्ये डीफॉल्ट टिपा अॅप सेट करा"</string> <string name="install_app" msgid="5066668100199613936">"अॅप इंस्टॉल करा"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेवर मिरर करायचे आहे का?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर करा"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"डिसमिस करा"</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> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 7cafdcde5e79..8c764f1e7f75 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Tetapkan apl nota lalai dalam Tetapan"</string> <string name="install_app" msgid="5066668100199613936">"Pasang apl"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Paparkan pada paparan luaran?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Segerakkan paparan"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Ketepikan"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon & Kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Penggunaan apl terbaharu"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Lihat akses terbaharu"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 620124eb5cec..883b9e95144a 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ဆက်တင်များတွင် မူရင်းမှတ်စုများအက်ပ် သတ်မှတ်ပါ"</string> <string name="install_app" msgid="5066668100199613936">"အက်ပ် ထည့်သွင်းရန်"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ပြင်ပဖန်သားပြင်သို့ စကရင်ပွားမလား။"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ဖန်သားပြင်ကို စကရင်ပွားရန်"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ပယ်ရန်"</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> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 722cc12d05f4..53a4c527cca6 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Du kan velge en standardapp for notater i Innstillinger"</string> <string name="install_app" msgid="5066668100199613936">"Installer appen"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du speile til en ekstern skjerm?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Speil skjermen"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Lukk"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon og kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nylig appbruk"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Se nylig tilgang"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 74e5c6f328f8..3b31b3adfd64 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिङमा गई नोट बनाउने डिफल्ट एप तोक्नुहोस्"</string> <string name="install_app" msgid="5066668100199613936">"एप इन्स्टल गर्नुहोस्"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेमा मिरर गर्ने हो?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर गर्नुहोस्"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"खारेज गर्नुहोस्"</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> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 6d97a9ffc805..664af5ec84eb 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standaard notitie-app instellen in Instellingen"</string> <string name="install_app" msgid="5066668100199613936">"App installeren"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spiegelen naar extern scherm?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Scherm spiegelen"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Sluiten"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfoon en camera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app-gebruik"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Recente toegang bekijken"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index a6e14f990820..db9e0c5debd8 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ସେଟିଂସରେ ଡିଫଲ୍ଟ ନୋଟ୍ସ ଆପ ସେଟ କରନ୍ତୁ"</string> <string name="install_app" msgid="5066668100199613936">"ଆପ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେକୁ ମିରର କରିବେ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ଡିସପ୍ଲେ ମିରର କରନ୍ତୁ"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ଖାରଜ କରନ୍ତୁ"</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> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index faca7b13eae1..591c5e781382 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਨੋਟ ਐਪ ਨੂੰ ਸੈੱਟ ਕਰੋ"</string> <string name="install_app" msgid="5066668100199613936">"ਐਪ ਸਥਾਪਤ ਕਰੋ"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ਕੀ ਬਾਹਰੀ ਡਿਸਪਲੇ \'ਤੇ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰਨਾ ਹੈ?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ਡਿਸਪਲੇ ਨੂੰ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰੋ"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ਖਾਰਜ ਕਰੋ"</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> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 3bb539a21f6f..195122113140 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ustaw domyślną aplikację do obsługi notatek w Ustawieniach"</string> <string name="install_app" msgid="5066668100199613936">"Zainstaluj aplikację"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Powielić na wyświetlaczu zewnętrznym?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Powielaj obraz"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Zamknij"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i aparat"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Aplikacje korzystające w ostatnim czasie"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobacz ostatni dostęp"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 8f93b73c3f85..693a3a1b7030 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string> <string name="install_app" msgid="5066668100199613936">"Instalar o app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfone e câmera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente do app"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Consultar acessos recentes"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 02ee09836610..c4f1f6790907 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Predefina a app de notas nas Definições"</string> <string name="install_app" msgid="5066668100199613936">"Instalar app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para o ecrã externo?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Espelhar ecrã"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorar"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfone e câmara"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilização recente da app"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver acesso recente"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 8f93b73c3f85..693a3a1b7030 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string> <string name="install_app" msgid="5066668100199613936">"Instalar o app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfone e câmera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente do app"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Consultar acessos recentes"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 14cdeaccdc5c..a942332e2472 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -1183,10 +1183,8 @@ <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="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Oglindești pe ecranul extern?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Afișare în oglindă"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Închide"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfon și cameră"</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> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index c5379c20b7ba..68601d6a71ce 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартное приложение для заметок в настройках."</string> <string name="install_app" msgid="5066668100199613936">"Установить приложение"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублировать на внешний дисплей?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Дублировать дисплей"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Закрыть"</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> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index d53f0eba6e4d..86cb3c324143 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"සැකසීම් තුළ පෙරනිමි සටහන් යෙදුම සකසන්න"</string> <string name="install_app" msgid="5066668100199613936">"යෙදුම ස්ථාපනය කරන්න"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"බාහිර සංදර්ශකයට දර්පණය කරන්න ද?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"සංදර්ශකය දර්පණය කරන්න"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"අස් කරන්න"</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> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 7fa0d077918f..56b9f7549038 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavte predvolenú aplikáciu na poznámky v Nastaveniach"</string> <string name="install_app" msgid="5066668100199613936">"Inštalovať aplikáciu"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Chcete zrkadliť na externú obrazovku?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Zrkadliť obrazovku"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Zavrieť"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofón a fotoaparát"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedávne využitie aplikácie"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobraziť nedávny prístup"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 87c18f4cabed..b72f750eae7c 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavite privzeto aplikacijo za zapiske v nastavitvah."</string> <string name="install_app" msgid="5066668100199613936">"Namesti aplikacijo"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite zrcaliti v zunanji zaslon?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Zrcali zaslon"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Opusti"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon in fotoaparat"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavna uporaba v aplikacijah"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ogled nedavnih dostopov"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 3e1471a68af2..92f6f9c27e77 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Cakto aplikacionin e parazgjedhur të shënimeve te \"Cilësimet\""</string> <string name="install_app" msgid="5066668100199613936">"Instalo aplikacionin"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Të pasqyrohet në ekranin e jashtëm?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Pasqyro ekranin"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Hiq"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofoni dhe kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Përdorimi i fundit i aplikacionit"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Shiko qasjen e fundit"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 07897d720128..7c0d9be76436 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Подесите подразумевану апликацију за белешке у Подешавањима"</string> <string name="install_app" msgid="5066668100199613936">"Инсталирај апликацију"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Желите ли да пресликате на спољњи екран?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Одбаци"</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> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index aee168e24329..323ce11f853d 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ställ in en standardapp för anteckningar i inställningarna"</string> <string name="install_app" msgid="5066668100199613936">"Installera appen"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vill du spegla till extern skärm?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Spegla skärm"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorera"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon och kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Senaste appanvändning"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Se senaste åtkomst"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index be3baddbe912..304910a46526 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Teua programu chaguomsingi ya madokezo katika Mipangilio"</string> <string name="install_app" msgid="5066668100199613936">"Sakinisha programu"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ungependa kuonyesha kwenye skrini ya nje?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Akisi skrini"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Ondoa"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Maikrofoni na Kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Matumizi ya programu hivi majuzi"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Angalia ufikiaji wa majuzi"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 785c4d5af8b6..3ac5e2e7c8cc 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"குறிப்பு எடுப்பதற்கான இயல்புநிலை ஆப்ஸை அமைப்புகளில் அமையுங்கள்"</string> <string name="install_app" msgid="5066668100199613936">"ஆப்ஸை நிறுவுங்கள்"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"வெளிப்புறக் காட்சிக்கு மிரர் செய்யவா?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"டிஸ்பிளேயை மிரர் செய்"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"வேண்டாம்"</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> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 96d77b7237ba..0526f95a8e84 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"సెట్టింగ్లలో ఆటోమేటిక్గా ఉండేలా ఒక నోట్స్ యాప్ను సెట్ చేసుకోండి"</string> <string name="install_app" msgid="5066668100199613936">"యాప్ను ఇన్స్టాల్ చేయండి"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"బాహ్య డిస్ప్లేను మిర్రర్ చేయాలా?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"మిర్రర్ డిస్ప్లే"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"విస్మరించండి"</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> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index f8fbfbe3c7d3..8949360f9508 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"กำหนดแอปการจดบันทึกเริ่มต้นในการตั้งค่า"</string> <string name="install_app" msgid="5066668100199613936">"ติดตั้งแอป"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"มิเรอร์ไปยังจอแสดงผลภายนอกไหม"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"มิเรอร์จอแสดงผล"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"ปิด"</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> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 435398db067d..e5c11df40e99 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Magtakda ng default na app sa pagtatala sa Mga Setting"</string> <string name="install_app" msgid="5066668100199613936">"I-install ang app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"I-mirror sa external na display?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"I-mirror ang display"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"I-dismiss"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikropono at Camera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Kamakailang paggamit ng app"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Tingnan ang kamakailang access"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index d0ddbec98faf..3552d9214fb0 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlar\'ı kullanarak varsayılan notlar ayarlayın"</string> <string name="install_app" msgid="5066668100199613936">"Uygulamayı yükle"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Harici ekrana yansıtılsın mı?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Ekranı yansıt"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Kapat"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon ve Kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Son uygulama kullanımı"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Son erişimi göster"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 1489dc77a9f1..2e6aea9d59d3 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Призначте стандартний додаток для нотаток у налаштуваннях"</string> <string name="install_app" msgid="5066668100199613936">"Установити додаток"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублювати на зовнішньому екрані?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Дублювати екран"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Закрити"</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> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 9d69a5e13319..2e2fc418116d 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ترتیبات میں ڈیفالٹ نوٹس ایپ سیٹ کریں"</string> <string name="install_app" msgid="5066668100199613936">"ایپ انسٹال کریں"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"بیرونی ڈسپلے پر مرر کریں؟"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"ڈسپلے کو دو طرفہ مطابقت پذیر بنائیں"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"برخاست کریں"</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> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 31b216b8977f..a8654d5604b1 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standart qaydlar ilovasini Sozlamalar orqali tanlang"</string> <string name="install_app" msgid="5066668100199613936">"Ilovani oʻrnatish"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tashqi displeyda aks ettirilsinmi?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Displeyni akslantirish"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Yopish"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon va kamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Ilovadan oxirgi foydalanish"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Oxirgi ruxsatni koʻrish"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 814e0a6087eb..54437456188f 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Đặt ứng dụng ghi chú mặc định trong phần Cài đặt"</string> <string name="install_app" msgid="5066668100199613936">"Cài đặt ứng dụng"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Đồng bộ hoá hai chiều sang màn hình ngoài?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Phản chiếu màn hình"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Đóng"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Micrô và máy ảnh"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Hoạt động sử dụng gần đây của ứng dụng"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Xem hoạt động truy cập gần đây"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 8b2caf028b5e..44c163b03bcb 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在设置中设置默认记事应用"</string> <string name="install_app" msgid="5066668100199613936">"安装应用"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"镜像到外接显示屏?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"镜像显示"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"关闭"</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> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 8a160c366f74..f45b2fe1fc39 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設筆記應用程式"</string> <string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要鏡像投射至外部顯示屏嗎?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</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> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 38b81a0a4dee..5a5bb9d19c3f 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設記事應用程式"</string> <string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要以鏡像方式投放至外部螢幕嗎?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</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> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index fc704172a295..8b6bfae60199 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -1183,10 +1183,8 @@ <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setha i-app yamanothi azenzakalelayo Kumsethingi"</string> <string name="install_app" msgid="5066668100199613936">"Faka i-app"</string> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Fanisa nesibonisi sangaphandle?"</string> - <!-- no translation found for mirror_display (2515262008898122928) --> - <skip /> - <!-- no translation found for dismiss_dialog (2195508495854675882) --> - <skip /> + <string name="mirror_display" msgid="2515262008898122928">"Isibonisi sokufanisa"</string> + <string name="dismiss_dialog" msgid="2195508495854675882">"Chitha"</string> <string name="privacy_dialog_title" msgid="7839968133469098311">"Imakrofoni Nekhamera"</string> <string name="privacy_dialog_summary" msgid="2458769652125995409">"Ukusetshenziswa kwakamuva kwe-app"</string> <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Bona ukufinyelela kwakamuva"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 18f24ec6293e..1add90ff4083 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -354,9 +354,6 @@ <!-- Whether to show activity indicators in the status bar --> <bool name="config_showActivity">false</bool> - <!-- Whether or not the button to clear all notifications will be shown. --> - <bool name="config_enableNotificationsClearAll">true</bool> - <!-- Whether or not to show the notification shelf that houses the icons of notifications that have been scrolled off-screen. --> <bool name="config_showNotificationShelf">true</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0ee5da22a31b..5a83c7d2dc2a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -846,12 +846,17 @@ <!-- Amount the button should shake when it's not long-pressed for long enough. --> <dimen name="keyguard_affordance_shake_amplitude">8dp</dimen> - <dimen name="keyguard_affordance_horizontal_offset">32dp</dimen> + <dimen name="keyguard_affordance_horizontal_offset">16dp</dimen> <dimen name="keyguard_affordance_vertical_offset">32dp</dimen> <!-- Value should be at least sum of 'keyguard_affordance_width' + 'keyguard_affordance_horizontal_offset' --> <dimen name="keyguard_indication_area_padding">82dp</dimen> + <!-- The width/padding of the communal tutorial indicator on keyguard. --> + <dimen name="communal_tutorial_indicator_fixed_width">168dp</dimen> + <dimen name="communal_tutorial_indicator_padding">24dp</dimen> + <dimen name="communal_tutorial_indicator_horizontal_offset">32dp</dimen> + <!-- The width/height of the unlock icon view on keyguard. --> <dimen name="keyguard_lock_height">42dp</dimen> <dimen name="keyguard_lock_padding">20dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 81101d828c86..85b986486099 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -222,6 +222,7 @@ <item type="id" name="lock_icon" /> <item type="id" name="lock_icon_bg" /> <item type="id" name="burn_in_layer" /> + <item type="id" name="communal_tutorial_indicator" /> <!-- Privacy dialog --> <item type="id" name="privacy_dialog_close_app_button" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index a2637d5e55c3..321594f41479 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1043,6 +1043,9 @@ <!-- Indication on the keyguard that is shown when the device is dock charging. [CHAR LIMIT=80]--> <string name="keyguard_indication_charging_time_dock"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Charging • Full in <xliff:g id="charging_time_left" example="4 hr, 2 min">%1$s</xliff:g></string> + <!-- Indicator shown to start the communal tutorial. [CHAR LIMIT=100] --> + <string name="communal_tutorial_indicator_text">Click on the arrow button to start the communal tutorial</string> + <!-- Related to user switcher --><skip/> <!-- Accessibility label for the button that opens the user switcher. --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt new file mode 100644 index 000000000000..57a49c83ae17 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt @@ -0,0 +1,24 @@ +/* + * 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.dagger.qualifiers + +import java.lang.annotation.Documented +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy.RUNTIME +import javax.inject.Qualifier + +/** Annotates a class that is display specific. */ +@Qualifier @Documented @Retention(RUNTIME) annotation class DisplaySpecific diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java index 5d036fbe5e52..b44bf395930e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java @@ -71,7 +71,6 @@ public class FloatingRotationButton implements RotationButton { private AnimatedVectorDrawable mAnimatedDrawable; private boolean mIsShowing; - private boolean mCanShow = true; private int mDisplayRotation; private boolean mIsTaskbarVisible = false; @@ -150,7 +149,7 @@ public class FloatingRotationButton implements RotationButton { @Override public boolean show() { - if (!mCanShow || mIsShowing) { + if (mIsShowing) { return false; } @@ -222,14 +221,6 @@ public class FloatingRotationButton implements RotationButton { } @Override - public void setCanShowRotationButton(boolean canShow) { - mCanShow = canShow; - if (!mCanShow) { - hide(); - } - } - - @Override public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) { mIsTaskbarVisible = taskbarVisible; mIsTaskbarStashed = taskbarStashed; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java index 89f71ebf3dce..42dda0a4da4f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java @@ -36,7 +36,6 @@ public interface RotationButton { default boolean isVisible() { return false; } - default void setCanShowRotationButton(boolean canShow) {} default void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {} default void updateIcon(int lightIconColor, int darkIconColor) { } default void setOnClickListener(View.OnClickListener onClickListener) { } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java index c0749885846f..1e896142f718 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java @@ -283,12 +283,28 @@ public class RotationButtonController { } public void setRotationLockedAtAngle(int rotationSuggestion, String caller) { - RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isRotationLocked(), + final Boolean isLocked = isRotationLocked(); + if (isLocked == null) { + // Ignore if we can't read the setting for the current user + return; + } + RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isLocked, /* rotation= */ rotationSuggestion, caller); } - public boolean isRotationLocked() { - return RotationPolicy.isRotationLocked(mContext); + /** + * @return whether rotation is currently locked, or <code>null</code> if the setting couldn't + * be read + */ + public Boolean isRotationLocked() { + try { + return RotationPolicy.isRotationLocked(mContext); + } catch (SecurityException e) { + // TODO(b/279561841): RotationPolicy uses the current user to resolve the setting which + // may change before the rotation watcher can be unregistered + Log.e(TAG, "Failed to get isRotationLocked", e); + return null; + } } public void setRotateSuggestionButtonState(boolean visible) { @@ -462,7 +478,11 @@ public class RotationButtonController { // If the screen rotation changes while locked, potentially update lock to flow with // new screen rotation and hide any showing suggestions. - boolean rotationLocked = isRotationLocked(); + Boolean rotationLocked = isRotationLocked(); + if (rotationLocked == null) { + // Ignore if we can't read the setting for the current user + return; + } // The isVisible check makes the rotation button disappear when we are not locked // (e.g. for tabletop auto-rotate). if (rotationLocked || mRotationButton.isVisible()) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 0094820f0dad..a6e04cec5f86 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -23,6 +23,7 @@ import android.view.SurfaceControl; import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; +import com.android.internal.os.IResultReceiver; import com.android.systemui.shared.recents.model.ThumbnailData; public class RecentsAnimationControllerCompat { @@ -89,11 +90,16 @@ public class RecentsAnimationControllerCompat { * @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous * app. */ - public void finish(boolean toHome, boolean sendUserLeaveHint) { + public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) { try { - mAnimationController.finish(toHome, sendUserLeaveHint); + mAnimationController.finish(toHome, sendUserLeaveHint, finishCb); } catch (RemoteException e) { Log.e(TAG, "Failed to finish recents animation", e); + try { + finishCb.send(0, null); + } catch (Exception ex) { + // Local call, can ignore + } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java index 1e7222de91d9..88b9c020e9a5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java @@ -233,6 +233,11 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner runner.onAnimationCancelled(); finishRunnable.run(); } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean aborted) + throws RemoteException { + } }; } } diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 2f3c1f263782..01a75d9e0f16 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -34,6 +34,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.customization.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.DisplaySpecific import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags.DOZING_MIGRATION_1 @@ -79,7 +80,7 @@ constructor( private val batteryController: BatteryController, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val configurationController: ConfigurationController, - @Main private val resources: Resources, + @DisplaySpecific private val resources: Resources, private val context: Context, @Main private val mainExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, @@ -205,19 +206,19 @@ constructor( private var isRegistered = false private var disposableHandle: DisposableHandle? = null private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING) - + private var largeClockOnSecondaryDisplay = false private fun updateColors() { if (regionSamplingEnabled) { clock?.let { clock -> smallRegionSampler?.let { - smallClockIsDark = it.currentRegionDarkness().isDark - clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark) + val isRegionDark = it.currentRegionDarkness().isDark + clock.smallClock.events.onRegionDarknessChanged(isRegionDark) } largeRegionSampler?.let { - largeClockIsDark = it.currentRegionDarkness().isDark - clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark) + val isRegionDark = it.currentRegionDarkness().isDark + clock.largeClock.events.onRegionDarknessChanged(isRegionDark) } } return @@ -225,12 +226,12 @@ constructor( val isLightTheme = TypedValue() context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true) - smallClockIsDark = isLightTheme.data == 0 - largeClockIsDark = isLightTheme.data == 0 + val isRegionDark = isLightTheme.data == 0 clock?.run { - smallClock.events.onRegionDarknessChanged(smallClockIsDark) - largeClock.events.onRegionDarknessChanged(largeClockIsDark) + Log.i(TAG, "Region isDark: $isRegionDark") + smallClock.events.onRegionDarknessChanged(isRegionDark) + largeClock.events.onRegionDarknessChanged(isRegionDark) } } protected open fun createRegionSampler( @@ -260,9 +261,6 @@ constructor( get() = isKeyguardVisible && dozeAmount < DOZE_TICKRATE_THRESHOLD private var cachedWeatherData: WeatherData? = null - private var smallClockIsDark = true - private var largeClockIsDark = true - private val configListener = object : ConfigurationController.ConfigurationListener { override fun onThemeChanged() { @@ -381,6 +379,19 @@ constructor( ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) } + /** + * Sets this clock as showing in a secondary display. + * + * Not that this is not necessarily needed, as we could get the displayId from [Context] + * directly and infere [largeClockOnSecondaryDisplay] from the id being different than the + * default display one. However, if we do so, current screenshot tests would not work, as they + * pass an activity context always from the default display. + */ + fun setLargeClockOnSecondaryDisplay(onSecondaryDisplay: Boolean) { + largeClockOnSecondaryDisplay = onSecondaryDisplay + updateFontSizes() + } + private fun updateTimeListeners() { smallTimeListener?.stop() largeTimeListener?.stop() @@ -403,9 +414,15 @@ constructor( smallClock.events.onFontSettingChanged( resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat() ) - largeClock.events.onFontSettingChanged( - resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat() - ) + largeClock.events.onFontSettingChanged(getLargeClockSizePx()) + } + } + + private fun getLargeClockSizePx(): Float { + return if (largeClockOnSecondaryDisplay) { + resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat() + } else { + resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat() } } diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt index d677a15862de..dec7d7992596 100644 --- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt +++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt @@ -68,10 +68,13 @@ constructor( clock = requireViewById(R.id.clock) keyguardStatusViewController = - keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply { - setDisplayedOnSecondaryDisplay() - init() - } + keyguardStatusViewComponentFactory + .build(clock, display) + .keyguardStatusViewController + .apply { + setDisplayedOnSecondaryDisplay() + init() + } } /** [ConnectedDisplayKeyguardPresentation] factory. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index ff8e489528b7..3c8301fe1b26 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -84,7 +84,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final DumpManager mDumpManager; private final ClockEventController mClockEventController; private final LogBuffer mLogBuffer; - private FrameLayout mSmallClockFrame; // top aligned clock private FrameLayout mLargeClockFrame; // centered clock @@ -265,6 +264,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (mShownOnSecondaryDisplay) { mView.setLargeClockOnSecondaryDisplay(true); + mClockEventController.setLargeClockOnSecondaryDisplay(true); displayClock(LARGE, /* animate= */ false); hideSliceViewAndNotificationIconContainer(); return; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 9c015fea5cf7..8a6f101b6c6c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -419,7 +419,7 @@ public class KeyguardDisplayManager { mClock.post(mMoveTextRunnable); mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory - .build(findViewById(R.id.clock)) + .build(findViewById(R.id.clock), getDisplay()) .getKeyguardClockSwitchController(); mKeyguardClockSwitchController.setOnlyClock(true); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 51c0676876b9..50be97ec1af9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -139,7 +139,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { case PROMPT_REASON_USER_REQUEST: return R.string.kg_prompt_after_user_lockdown_password; case PROMPT_REASON_PREPARE_FOR_UPDATE: - return R.string.kg_prompt_unattended_update_password; + return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_TRUSTAGENT_EXPIRED: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 714ba81fc664..57151ae32db0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -321,7 +321,7 @@ public class KeyguardPatternViewController resId = R.string.kg_prompt_after_user_lockdown_pattern; break; case PROMPT_REASON_PREPARE_FOR_UPDATE: - resId = R.string.kg_prompt_unattended_update_pattern; + resId = R.string.kg_prompt_reason_timeout_pattern; break; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: resId = R.string.kg_prompt_reason_timeout_pattern; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 9d6d0332b96b..681aa70402bd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -123,7 +123,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView case PROMPT_REASON_USER_REQUEST: return R.string.kg_prompt_after_user_lockdown_pin; case PROMPT_REASON_PREPARE_FOR_UPDATE: - return R.string.kg_prompt_unattended_update_pin; + return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_TRUSTAGENT_EXPIRED: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index f9cc03eea288..d8486029a903 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -18,7 +18,6 @@ package com.android.keyguard; import static java.util.Collections.emptySet; -import android.animation.LayoutTransition; import android.content.Context; import android.graphics.Canvas; import android.os.Build; @@ -79,14 +78,6 @@ public class KeyguardStatusView extends GridLayout { mKeyguardSlice = findViewById(R.id.keyguard_slice_view); mMediaHostContainer = findViewById(R.id.status_view_media_container); - if (mMediaHostContainer != null) { - LayoutTransition mediaLayoutTransition = new LayoutTransition(); - ((ViewGroup) mMediaHostContainer).setLayoutTransition(mediaLayoutTransition); - mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); - mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); - mediaLayoutTransition.disableTransitionType(LayoutTransition.APPEARING); - mediaLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING); - } updateDark(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 67b705222977..79642bdae1ce 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -23,7 +23,6 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CL import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.animation.Animator; -import android.animation.LayoutTransition; import android.animation.ValueAnimator; import android.annotation.Nullable; import android.content.res.Configuration; @@ -50,14 +49,15 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.keyguard.KeyguardClockSwitch.ClockSize; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; +import com.android.systemui.animation.ViewHierarchyAnimator; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; -import com.android.systemui.power.shared.model.ScreenPowerState; import com.android.systemui.plugins.ClockController; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.power.shared.model.ScreenPowerState; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; @@ -175,27 +175,10 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV return; } - final LayoutTransition mediaLayoutTransition = - ((ViewGroup) mediaHostContainer).getLayoutTransition(); - if (mediaLayoutTransition == null) return; - - mediaLayoutTransition.enableTransitionType(LayoutTransition.CHANGING); + ViewHierarchyAnimator.Companion.animateNextUpdate(mediaHostContainer, + Interpolators.STANDARD, /* duration= */ 500L, + /* animateChildren= */ false); }); - - mediaHostContainer.addOnLayoutChangeListener( - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - final LayoutTransition mediaLayoutTransition = - ((ViewGroup) mediaHostContainer).getLayoutTransition(); - if (mediaLayoutTransition == null) return; - if (!mediaLayoutTransition.isTransitionTypeEnabled( - LayoutTransition.CHANGING)) { - return; - } - // Note: when this is called, the LayoutTransition is already been set up. - // Disables the LayoutTransition until it's explicitly enabled again. - mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGING); - } - ); } mDumpManager.registerDumpable(getInstanceName(), this); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 3f5ec7d020da..3bf148276eab 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -189,6 +189,8 @@ import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.Assert; import com.android.systemui.util.settings.SecureSettings; +import dalvik.annotation.optimization.NeverCompile; + import com.google.android.collect.Lists; import java.io.PrintWriter; @@ -4430,6 +4432,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } @SuppressLint("MissingPermission") + @NeverCompile @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("KeyguardUpdateMonitor state:"); diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index 1d2d77f16b75..40d0be1173fa 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -18,6 +18,7 @@ package com.android.keyguard; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; @@ -68,6 +69,7 @@ public class LockIconView extends FrameLayout implements Dumpable { private boolean mUseBackground = false; private float mDozeAmount = 0f; + @SuppressLint("ClickableViewAccessibility") public LockIconView(Context context, AttributeSet attrs) { super(context, attrs); mSensorRect = new RectF(); diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index a81069a1f7db..83da80f4123a 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -25,9 +25,11 @@ import static com.android.keyguard.LockIconView.ICON_UNLOCK; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; +import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -56,11 +58,11 @@ import androidx.annotation.VisibleForTesting; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -72,12 +74,16 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; +import dagger.Lazy; + import java.io.PrintWriter; import java.util.Objects; import java.util.function.Consumer; @@ -99,6 +105,8 @@ public class LockIconViewController implements Dumpable { private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); + private static final long FADE_OUT_DURATION_MS = 250L; + private final long mLongPressTimeout; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final KeyguardViewController mKeyguardViewController; @@ -124,6 +132,8 @@ public class LockIconViewController implements Dumpable { @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; @NonNull private final KeyguardInteractor mKeyguardInteractor; @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate; + @NonNull private final Lazy<BouncerInteractor> mBouncerInteractor; + @NonNull private final SceneContainerFlags mSceneContainerFlags; // Tracks the velocity of a touch to help filter out the touches that move too fast. private VelocityTracker mVelocityTracker; @@ -200,7 +210,9 @@ public class LockIconViewController implements Dumpable { @NonNull KeyguardInteractor keyguardInteractor, @NonNull FeatureFlags featureFlags, PrimaryBouncerInteractor primaryBouncerInteractor, - Context context + Context context, + Lazy<BouncerInteractor> bouncerInteractor, + SceneContainerFlags sceneContainerFlags ) { mStatusBarStateController = statusBarStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -229,6 +241,8 @@ public class LockIconViewController implements Dumpable { dumpManager.registerDumpable(TAG, this); mResources = resources; mContext = context; + mBouncerInteractor = bouncerInteractor; + mSceneContainerFlags = sceneContainerFlags; mAccessibilityDelegate = new View.AccessibilityDelegate() { private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint = @@ -253,6 +267,7 @@ public class LockIconViewController implements Dumpable { } /** Sets the LockIconView to the controller and rebinds any that depend on it. */ + @SuppressLint("ClickableViewAccessibility") public void setLockIconView(LockIconView lockIconView) { mView = lockIconView; mView.setImageDrawable(mIcon); @@ -302,6 +317,8 @@ public class LockIconViewController implements Dumpable { if (lockIconView.isAttachedToWindow()) { registerCallbacks(); } + + lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent)); } private void registerCallbacks() { @@ -388,6 +405,16 @@ public class LockIconViewController implements Dumpable { mView.updateIcon(ICON_LOCK, true); mView.setContentDescription(mLockedLabel); mView.setVisibility(View.VISIBLE); + } else if (mIsDozing && mFeatureFlags.isEnabled(NEW_AOD_TRANSITION)) { + mView.animate() + .alpha(0f) + .setDuration(FADE_OUT_DURATION_MS) + .withEndAction(() -> { + mView.clearIcon(); + mView.setVisibility(View.INVISIBLE); + mView.setContentDescription(null); + }) + .start(); } else { mView.clearIcon(); mView.setVisibility(View.INVISIBLE); @@ -622,19 +649,18 @@ public class LockIconViewController implements Dumpable { }; /** - * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true. + * Handles the touch if {@link #isActionable()} is true. * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon * area for {@link #mLongPressTimeout} ms. * * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}. */ - public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) { - if (!onInterceptTouchEvent(event)) { + private boolean onTouchEvent(MotionEvent event) { + if (!actionableDownEventStartedOnView(event)) { cancelTouches(); return false; } - mOnGestureDetectedRunnable = onGestureDetectedRunnable; switch(event.getActionMasked()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: @@ -666,10 +692,10 @@ public class LockIconViewController implements Dumpable { mVelocityTracker.addMovement(event); // Compute pointer velocity in pixels per second. mVelocityTracker.computeCurrentVelocity(1000); - float velocity = UdfpsController.computePointerSpeed(mVelocityTracker, + float velocity = computePointerSpeed(mVelocityTracker, mActivePointerId); if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS - && UdfpsController.exceedsVelocityThreshold(velocity)) { + && exceedsVelocityThreshold(velocity)) { Log.v(TAG, "lock icon long-press rescheduled due to " + "high pointer velocity=" + velocity); mLongPressCancelRunnable.run(); @@ -688,11 +714,24 @@ public class LockIconViewController implements Dumpable { } /** - * Intercepts the touch if the onDown event and current event are within this lock icon view's - * bounds. + * Calculate the pointer speed given a velocity tracker and the pointer id. + * This assumes that the velocity tracker has already been passed all relevant motion events. + */ + private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { + final float vx = tracker.getXVelocity(pointerId); + final float vy = tracker.getYVelocity(pointerId); + return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0)); + } + + /** + * Whether the velocity exceeds the acceptable UDFPS debouncing threshold. */ - public boolean onInterceptTouchEvent(MotionEvent event) { - if (!inLockIconArea(event) || !isActionable()) { + private static boolean exceedsVelocityThreshold(float velocity) { + return velocity > 750f; + } + + private boolean actionableDownEventStartedOnView(MotionEvent event) { + if (!isActionable()) { return false; } @@ -703,7 +742,8 @@ public class LockIconViewController implements Dumpable { return mDownDetected; } - private void onLongPress() { + @VisibleForTesting + protected void onLongPress() { cancelTouches(); if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) { Log.v(TAG, "lock icon long-press rejected by the falsing manager."); @@ -716,14 +756,15 @@ public class LockIconViewController implements Dumpable { mAuthRippleController.showUnlockRipple(FINGERPRINT); } updateVisibility(); - if (mOnGestureDetectedRunnable != null) { - mOnGestureDetectedRunnable.run(); - } // play device entry haptic (consistent with UDFPS controller longpress) vibrateOnLongPress(); - mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); + if (mSceneContainerFlags.isEnabled()) { + mBouncerInteractor.get().showOrUnlockDevice(null); + } else { + mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); + } } @@ -738,12 +779,6 @@ public class LockIconViewController implements Dumpable { } } - private boolean inLockIconArea(MotionEvent event) { - mView.getHitRect(mSensorTouchLocation); - return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) - && mView.getVisibility() == View.VISIBLE; - } - private boolean isActionable() { if (mIsBouncerShowing) { Log.v(TAG, "lock icon long-press ignored, bouncer already showing."); @@ -821,6 +856,19 @@ public class LockIconViewController implements Dumpable { } }; + /** + * Whether the lock icon will handle a touch while dozing. + */ + public boolean willHandleTouchWhileDozing(MotionEvent event) { + // is in lock icon area + mView.getHitRect(mSensorTouchLocation); + final boolean inLockIconArea = + mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) + && mView.getVisibility() == View.VISIBLE; + + return inLockIconArea && actionableDownEventStartedOnView(event); + } + private final View.OnClickListener mA11yClickListener = v -> onLongPress(); private final AccessibilityManager.AccessibilityStateChangeListener diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt new file mode 100644 index 000000000000..1f145d887381 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt @@ -0,0 +1,42 @@ +package com.android.keyguard.dagger + +import android.content.Context +import android.content.res.Resources +import android.view.Display +import com.android.systemui.dagger.qualifiers.DisplaySpecific +import com.android.systemui.util.kotlin.getOrNull +import dagger.BindsOptionalOf +import dagger.Module +import dagger.Provides +import java.util.Optional + +/** + * Binds display specific context and resources. + * + * When a [Display] is available in the scope, binds a [DisplaySpecific] [Context] and [Resources]. + * When not available, the default display context and resources are used. + */ +@Module +abstract class KeyguardDisplayModule { + + @BindsOptionalOf abstract fun optionalDisplay(): Display + + companion object { + @Provides + @DisplaySpecific + fun getDisplayContext(context: Context, optionalDisplay: Optional<Display>): Context { + val display = optionalDisplay.getOrNull() ?: return context + return if (context.displayId == display.displayId) { + context + } else { + context.createDisplayContext(display) + } + } + + @Provides + @DisplaySpecific + fun getDisplayResources(@DisplaySpecific context: Context): Resources { + return context.resources + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java index d342377da49b..f3014f1ecc58 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java @@ -16,6 +16,8 @@ package com.android.keyguard.dagger; +import android.view.Display; + import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; @@ -28,13 +30,17 @@ import dagger.Subcomponent; * * TODO: unify this with {@link KeyguardStatusBarViewComponent} */ -@Subcomponent(modules = {KeyguardStatusViewModule.class}) +@Subcomponent(modules = {KeyguardStatusViewModule.class, KeyguardDisplayModule.class}) @KeyguardStatusViewScope public interface KeyguardStatusViewComponent { /** Simple factory for {@link KeyguardStatusViewComponent}. */ @Subcomponent.Factory interface Factory { - KeyguardStatusViewComponent build(@BindsInstance KeyguardStatusView presentation); + /** Creates {@link KeyguardStatusViewComponent} for a given display. */ + KeyguardStatusViewComponent build( + @BindsInstance KeyguardStatusView presentation, + @BindsInstance Display display + ); } /** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */ diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 018038423d4e..7739021bad33 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -87,7 +87,6 @@ import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -130,14 +129,14 @@ import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakReporter; import com.android.systemui.util.sensors.AsyncSensorManager; +import dagger.Lazy; + import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; -import dagger.Lazy; - /** * Class to handle ugly dependencies throughout sysui until we determine the * long-term dependency injection solution. @@ -298,7 +297,6 @@ public class Dependency { @Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController; @Inject Lazy<StatusBarStateController> mStatusBarStateController; @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager; - @Inject Lazy<NotificationGutsManager> mNotificationGutsManager; @Inject Lazy<NotificationMediaManager> mNotificationMediaManager; @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager; @Inject Lazy<SmartReplyConstants> mSmartReplyConstants; @@ -498,7 +496,6 @@ public class Dependency { mProviders.put(NotificationLockscreenUserManager.class, mNotificationLockscreenUserManager::get); mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get); - mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get); mProviders.put(NotificationRemoteInputManager.class, mNotificationRemoteInputManager::get); mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get); diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 4541384aaa37..b573fadea15e 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -28,6 +28,7 @@ import com.android.systemui.res.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.settings.UserTracker; @@ -46,6 +47,7 @@ import dagger.assisted.AssistedInject; /** * Manages notification when a guest session is resumed. */ +@SysUISingleton public class GuestResumeSessionReceiver { @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 4af2c740ddc9..6faee8cd1c4f 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -83,7 +83,7 @@ import com.android.systemui.decor.RoundedCornerDecorProviderFactory; import com.android.systemui.decor.RoundedCornerResDelegateImpl; import com.android.systemui.decor.ScreenDecorCommand; import com.android.systemui.log.ScreenDecorationsLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.res.R; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; @@ -93,6 +93,8 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.ThreadFactory; import com.android.systemui.util.settings.SecureSettings; +import dalvik.annotation.optimization.NeverCompile; + import kotlin.Pair; import java.io.PrintWriter; @@ -163,7 +165,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable { ScreenDecorHwcLayer mScreenDecorHwcLayer; private WindowManager mWindowManager; private int mRotation; - private SettingObserver mColorInversionSetting; + private UserSettingObserver mColorInversionSetting; @Nullable private DelayableExecutor mExecutor; private Handler mHandler; @@ -684,7 +686,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable { // Watch color inversion and invert the overlay as needed. if (mColorInversionSetting == null) { - mColorInversionSetting = new SettingObserver(mSecureSettings, mHandler, + mColorInversionSetting = new UserSettingObserver(mSecureSettings, mHandler, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, mUserTracker.getUserId()) { @Override @@ -1088,6 +1090,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable { } } + @NeverCompile @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("ScreenDecorations state:"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt deleted file mode 100644 index ca4b8efa98a5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt +++ /dev/null @@ -1,54 +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.systemui.biometrics - -/** - * Interface for controlling the on finger down & on finger up events. - */ -interface AlternateUdfpsTouchProvider { - - /** - * onPointerDown: - * - * This operation is used to notify the Fingerprint HAL that - * a fingerprint has been detected on the device's screen. - * - * See fingerprint/ISession#onPointerDown for more details. - */ - fun onPointerDown(pointerId: Long, x: Int, y: Int, minor: Float, major: Float) - - /** - * onPointerUp: - * - * This operation can be invoked when the HAL is performing any one of: ISession#authenticate, - * ISession#enroll, ISession#detectInteraction. This operation is used to indicate - * that a fingerprint that was previously down, is now up. - * - * See fingerprint/ISession#onPointerUp for more details. - */ - fun onPointerUp(pointerId: Long) - - /** - * onUiReady: - * - * This operation is used by the callee to notify the Fingerprint HAL that SystemUI is - * correctly configured for the fingerprint capture. - * - * See fingerprint/ISession#onUiReady for more details. - */ - fun onUiReady() -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index bdad41348c95..395f68c7c0e6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -37,9 +37,6 @@ public abstract class UdfpsAnimationView extends FrameLayout { private float mDialogSuggestedAlpha = 1f; private float mNotificationShadeExpansion = 0f; - // Used for Udfps ellipse detection when flag is true, set by AnimationViewController - boolean mUseExpandedOverlay = false; - // mAlpha takes into consideration the status bar expansion amount and dialog suggested alpha private int mAlpha; boolean mPauseAuth; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 79d9c1ba70bc..c9e4cbe30a2c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -32,7 +32,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.graphics.Point; import android.graphics.Rect; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.SensorProperties; @@ -54,7 +53,6 @@ import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.VelocityTracker; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -84,7 +82,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter; @@ -102,7 +99,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; -import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.SystemClock; import kotlin.Unit; @@ -110,7 +106,6 @@ import kotlin.Unit; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; -import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; @@ -136,13 +131,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { private static final String TAG = "UdfpsController"; private static final long AOD_SEND_FINGER_UP_DELAY_MILLIS = 1000; - // Minimum required delay between consecutive touch logs in milliseconds. - private static final long MIN_TOUCH_LOG_INTERVAL = 50; private static final long MIN_UNCHANGED_INTERACTION_LOG_INTERVAL = 50; - // This algorithm checks whether the touch is within the sensor's bounding box. - private static final int BOUNDING_BOX_TOUCH_CONFIG_ID = 0; - private final Context mContext; private final Execution mExecution; private final FingerprintManager mFingerprintManager; @@ -175,8 +165,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @Nullable private final TouchProcessor mTouchProcessor; @NonNull private final SessionTracker mSessionTracker; @NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor; - @NonNull private final SecureSettings mSecureSettings; - @NonNull private final UdfpsUtils mUdfpsUtils; @NonNull private final InputManager mInputManager; @NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; private final boolean mIgnoreRefreshRate; @@ -187,11 +175,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { @VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams(); // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this. @Nullable private Runnable mAuthControllerUpdateUdfpsLocation; - @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider; @Nullable private UdfpsDisplayModeProvider mUdfpsDisplayMode; - // Tracks the velocity of a touch to help filter out the touches that move too fast. - @Nullable private VelocityTracker mVelocityTracker; // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. private int mActivePointerId = -1; // Whether a pointer has been pilfered for current gesture @@ -259,8 +244,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { final int touchConfigId = mContext.getResources().getInteger( com.android.internal.R.integer.config_selected_udfps_touch_detection); pw.println("mSensorProps=(" + mSensorProps + ")"); - pw.println("Using new touch detection framework: " + mFeatureFlags.isEnabled( - Flags.UDFPS_NEW_TOUCH_DETECTION)); pw.println("touchConfigId: " + touchConfigId); } @@ -272,7 +255,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay( new UdfpsControllerOverlay( mContext, - mFingerprintManager, mInflater, mWindowManager, mAccessibilityManager, @@ -286,7 +268,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mKeyguardStateController, mUnlockedScreenOffAnimationController, mUdfpsDisplayMode, - mSecureSettings, requestId, reason, callback, @@ -299,7 +280,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mFeatureFlags, mPrimaryBouncerInteractor, mAlternateBouncerInteractor, - mUdfpsUtils, mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels ))); @@ -376,13 +356,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { * Debug to run onUiReady */ public void debugOnUiReady(int sensorId) { - if (UdfpsController.this.mAlternateTouchProvider != null) { - UdfpsController.this.mAlternateTouchProvider.onUiReady(); - } else { - final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L; - UdfpsController.this.mFingerprintManager.onUdfpsUiEvent( - FingerprintManager.UDFPS_UI_READY, requestId, sensorId); - } + final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L; + UdfpsController.this.mFingerprintManager.onUdfpsUiEvent( + FingerprintManager.UDFPS_UI_READY, requestId, sensorId); } } @@ -423,23 +399,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mUdfpsDisplayMode = udfpsDisplayMode; } - /** - * Calculate the pointer speed given a velocity tracker and the pointer id. - * This assumes that the velocity tracker has already been passed all relevant motion events. - */ - public static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { - final float vx = tracker.getXVelocity(pointerId); - final float vy = tracker.getYVelocity(pointerId); - return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0)); - } - - /** - * Whether the velocity exceeds the acceptable UDFPS debouncing threshold. - */ - public static boolean exceedsVelocityThreshold(float velocity) { - return velocity > 750f; - } - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -457,38 +416,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { } }; - /** - * Forwards touches to the udfps controller / view - */ - public boolean onTouch(MotionEvent event) { - if (mOverlay == null || mOverlay.isHiding()) { - return false; - } - // TODO(b/225068271): may not be correct but no way to get the id yet - return onTouch(mOverlay.getRequestId(), event, false); - } - - /** - * @param x coordinate - * @param y coordinate - * @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else, - * calculate from the display dimensions in portrait orientation - */ - private boolean isWithinSensorArea(UdfpsView udfpsView, float x, float y, - boolean relativeToUdfpsView) { - if (relativeToUdfpsView) { - // TODO: move isWithinSensorArea to UdfpsController. - return udfpsView.isWithinSensorArea(x, y); - } - - if (mOverlay == null || mOverlay.getAnimationViewController() == null) { - return false; - } - - return !mOverlay.getAnimationViewController().shouldPauseAuth() - && mOverlayParams.getSensorBounds().contains((int) x, (int) y); - } - private void tryDismissingKeyguard() { if (!mOnFingerDown) { playStartHaptic(); @@ -497,15 +424,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mAttemptedToDismissKeyguard = true; } - @VisibleForTesting - boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { - if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - return newOnTouch(requestId, event, fromUdfpsView); - } else { - return oldOnTouch(requestId, event, fromUdfpsView); - } - } - private int getBiometricSessionType() { if (mOverlay == null) { return -1; @@ -566,7 +484,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } } - private boolean newOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { + private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { if (!fromUdfpsView) { Log.e(TAG, "ignoring the touch injected from outside of UdfpsView"); return false; @@ -580,7 +498,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { + mOverlay.getRequestId()); return false; } - if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f && !mAlternateBouncerInteractor.isVisibleState()) || mPrimaryBouncerInteractor.isInTransit()) { @@ -654,8 +571,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { break; case UNCHANGED: - if (!isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(), - true) && mActivePointerId == MotionEvent.INVALID_POINTER_ID + if (mActivePointerId == MotionEvent.INVALID_POINTER_ID && mAlternateBouncerInteractor.isVisibleState()) { // No pointer on sensor, forward to keyguard if alternateBouncer is visible mKeyguardViewManager.onTouch(event); @@ -667,7 +583,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { logBiometricTouch(processedTouch.getEvent(), data); // Always pilfer pointers that are within sensor area or when alternate bouncer is showing - if (isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(), true) + if (mActivePointerId != MotionEvent.INVALID_POINTER_ID || mAlternateBouncerInteractor.isVisibleState()) { shouldPilfer = true; } @@ -680,146 +596,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { mPointerPilfered = true; } - return processedTouch.getTouchData().isWithinBounds(mOverlayParams.getNativeSensorBounds()); - } - - private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { - if (mOverlay == null) { - Log.w(TAG, "ignoring onTouch with null overlay"); - return false; - } - if (!mOverlay.matchesRequestId(requestId)) { - Log.w(TAG, "ignoring stale touch event: " + requestId + " current: " - + mOverlay.getRequestId()); - return false; - } - - final UdfpsView udfpsView = mOverlay.getOverlayView(); - boolean handled = false; - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_HOVER_ENTER: - Trace.beginSection("UdfpsController.onTouch.ACTION_DOWN"); - // To simplify the lifecycle of the velocity tracker, make sure it's never null - // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP. - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } else { - // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new - // ACTION_DOWN, in that case we should just reuse the old instance. - mVelocityTracker.clear(); - } - - final boolean withinSensorArea = - isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); - if (withinSensorArea) { - Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); - Log.v(TAG, "onTouch | action down"); - // The pointer that causes ACTION_DOWN is always at index 0. - // We need to persist its ID to track it during ACTION_MOVE that could include - // data for many other pointers because of multi-touch support. - mActivePointerId = event.getPointerId(0); - mVelocityTracker.addMovement(event); - handled = true; - mAcquiredReceived = false; - } - if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) { - Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN"); - tryDismissingKeyguard(); - } - - Trace.endSection(); - break; - - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_HOVER_MOVE: - Trace.beginSection("UdfpsController.onTouch.ACTION_MOVE"); - final int idx = mActivePointerId == -1 - ? event.getPointerId(0) - : event.findPointerIndex(mActivePointerId); - if (idx == event.getActionIndex()) { - final boolean actionMoveWithinSensorArea = - isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), - fromUdfpsView); - if ((fromUdfpsView || actionMoveWithinSensorArea) - && shouldTryToDismissKeyguard()) { - Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE"); - tryDismissingKeyguard(); - break; - } - // Map the touch to portrait mode if the device is in landscape mode. - final Point scaledTouch = mUdfpsUtils.getTouchInNativeCoordinates( - idx, event, mOverlayParams); - if (actionMoveWithinSensorArea) { - if (mVelocityTracker == null) { - // touches could be injected, so the velocity tracker may not have - // been initialized (via ACTION_DOWN). - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(event); - // Compute pointer velocity in pixels per second. - mVelocityTracker.computeCurrentVelocity(1000); - // Compute pointer speed from X and Y velocities. - final float v = computePointerSpeed(mVelocityTracker, mActivePointerId); - final float minor = event.getTouchMinor(idx); - final float major = event.getTouchMajor(idx); - final boolean exceedsVelocityThreshold = exceedsVelocityThreshold(v); - final String touchInfo = String.format( - "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b", - minor, major, v, exceedsVelocityThreshold); - final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime; - - if (!mOnFingerDown && !mAcquiredReceived && !exceedsVelocityThreshold) { - final float scale = mOverlayParams.getScaleFactor(); - float scaledMinor = minor / scale; - float scaledMajor = major / scale; - onFingerDown(requestId, scaledTouch.x, scaledTouch.y, scaledMinor, - scaledMajor); - - Log.v(TAG, "onTouch | finger down: " + touchInfo); - mTouchLogTime = mSystemClock.elapsedRealtime(); - handled = true; - } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) { - Log.v(TAG, "onTouch | finger move: " + touchInfo); - mTouchLogTime = mSystemClock.elapsedRealtime(); - } - } else { - Log.v(TAG, "onTouch | finger outside"); - onFingerUp(requestId, udfpsView); - // Maybe announce for accessibility. - mFgExecutor.execute(() -> { - if (mOverlay == null) { - Log.e(TAG, "touch outside sensor area received" - + "but serverRequest is null"); - return; - } - mOverlay.onTouchOutsideOfSensorArea(scaledTouch); - }); - } - } - Trace.endSection(); - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_HOVER_EXIT: - Trace.beginSection("UdfpsController.onTouch.ACTION_UP"); - mActivePointerId = -1; - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - Log.v(TAG, "onTouch | finger up"); - mAttemptedToDismissKeyguard = false; - onFingerUp(requestId, udfpsView); - mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION); - Trace.endSection(); - break; - - default: - // Do nothing. - } - return handled; + return mActivePointerId != MotionEvent.INVALID_POINTER_ID; } private boolean shouldTryToDismissKeyguard() { @@ -859,15 +636,12 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull SystemUIDialogManager dialogManager, @NonNull LatencyTracker latencyTracker, @NonNull ActivityLaunchAnimator activityLaunchAnimator, - @NonNull Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider, @NonNull @BiometricsBackground Executor biometricsExecutor, @NonNull PrimaryBouncerInteractor primaryBouncerInteractor, @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor, @NonNull SessionTracker sessionTracker, @NonNull AlternateBouncerInteractor alternateBouncerInteractor, - @NonNull SecureSettings secureSettings, @NonNull InputManager inputManager, - @NonNull UdfpsUtils udfpsUtils, @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate, @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) { @@ -900,7 +674,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mLatencyTracker = latencyTracker; mActivityLaunchAnimator = activityLaunchAnimator; - mAlternateTouchProvider = alternateTouchProvider.map(Provider::get).orElse(null); mSensorProps = new FingerprintSensorPropertiesInternal( -1 /* sensorId */, SensorProperties.STRENGTH_CONVENIENCE, @@ -912,13 +685,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { mBiometricExecutor = biometricsExecutor; mPrimaryBouncerInteractor = primaryBouncerInteractor; mAlternateBouncerInteractor = alternateBouncerInteractor; - mSecureSettings = secureSettings; - mUdfpsUtils = udfpsUtils; mInputManager = inputManager; mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate; - mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) - ? singlePointerTouchProcessor : null; + mTouchProcessor = singlePointerTouchProcessor; mSessionTracker = sessionTracker; mDumpManager.registerDumpable(TAG, this); @@ -1172,16 +942,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { } private void dispatchOnUiReady(long requestId) { - if (mAlternateTouchProvider != null) { - mBiometricExecutor.execute(() -> { - mAlternateTouchProvider.onUiReady(); - mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); - }); - } else { - mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_READY, requestId, - mSensorProps.sensorId); - mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); - } + mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_READY, requestId, + mSensorProps.sensorId); + mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); } private void onFingerDown( @@ -1241,24 +1004,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { } } mOnFingerDown = true; - if (mAlternateTouchProvider != null) { - mBiometricExecutor.execute(() -> { - mAlternateTouchProvider.onPointerDown(requestId, (int) x, (int) y, minor, major); - }); - mFgExecutor.execute(() -> { - if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) { - mKeyguardUpdateMonitor.onUdfpsPointerDown((int) requestId); - } - }); - } else { - if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y, - minor, major, orientation, time, gestureStart, isAod); - } else { - mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, (int) x, - (int) y, minor, major); - } - } + mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y, + minor, major, orientation, time, gestureStart, isAod); Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0); final UdfpsView view = mOverlay.getOverlayView(); if (view != null && isOptical()) { @@ -1305,23 +1052,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { mActivePointerId = -1; mAcquiredReceived = false; if (mOnFingerDown) { - if (mAlternateTouchProvider != null) { - mBiometricExecutor.execute(() -> { - mAlternateTouchProvider.onPointerUp(requestId); - }); - mFgExecutor.execute(() -> { - if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) { - mKeyguardUpdateMonitor.onUdfpsPointerUp((int) requestId); - } - }); - } else { - if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x, - y, minor, major, orientation, time, gestureStart, isAod); - } else { - mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId); - } - } + mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x, + y, minor, major, orientation, time, gestureStart, isAod); for (Callback cb : mCallbacks) { cb.onFingerUp(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 34a0d8a46e52..7130bfb462e3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -20,7 +20,6 @@ import android.annotation.SuppressLint import android.annotation.UiThread import android.content.Context import android.graphics.PixelFormat -import android.graphics.Point import android.graphics.Rect import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD @@ -29,7 +28,6 @@ import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTING import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR import android.hardware.biometrics.BiometricOverlayConstants.ShowReason -import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.os.Build import android.os.RemoteException @@ -54,7 +52,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels @@ -65,7 +62,6 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.settings.SecureSettings import kotlinx.coroutines.ExperimentalCoroutinesApi import javax.inject.Provider @@ -83,7 +79,6 @@ const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui" @UiThread class UdfpsControllerOverlay @JvmOverloads constructor( private val context: Context, - fingerprintManager: FingerprintManager, private val inflater: LayoutInflater, private val windowManager: WindowManager, private val accessibilityManager: AccessibilityManager, @@ -97,7 +92,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val keyguardStateController: KeyguardStateController, private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider, - private val secureSettings: SecureSettings, val requestId: Long, @ShowReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, @@ -107,7 +101,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val isDebuggable: Boolean = Build.IS_DEBUGGABLE, - private val udfpsUtils: UdfpsUtils, private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate, private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>, ) { @@ -134,10 +127,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY // Avoid announcing window title. accessibilityTitle = " " - - if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY - } + inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY } /** If the overlay is currently showing. */ @@ -206,7 +196,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor( overlayTouchListener!! ) overlayTouchListener?.onTouchExplorationStateChanged(true) - useExpandedOverlay = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) } } catch (e: RuntimeException) { Log.e(TAG, "showUdfpsOverlay | failed to add window", e) @@ -331,25 +320,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor( return wasShowing } - /** - * This function computes the angle of touch relative to the sensor and maps - * the angle to a list of help messages which are announced if accessibility is enabled. - * - */ - fun onTouchOutsideOfSensorArea(scaledTouch: Point) { - val theStr = - udfpsUtils.onTouchOutsideOfSensorArea( - touchExplorationEnabled, - context, - scaledTouch.x, - scaledTouch.y, - overlayParams - ) - if (theStr != null) { - animationViewController?.doAnnounceForAccessibility(theStr) - } - } - /** Cancel this request. */ fun cancel() { try { @@ -367,10 +337,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor( ): WindowManager.LayoutParams { val paddingX = animation?.paddingX ?: 0 val paddingY = animation?.paddingY ?: 0 - if (!featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) && animation != null && - animation.listenForTouchesOutsideView()) { - flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - } val isEnrollment = when (requestReason) { REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true @@ -379,19 +345,15 @@ class UdfpsControllerOverlay @JvmOverloads constructor( // Use expanded overlay unless touchExploration enabled var rotatedBounds = - if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) { - Rect(overlayParams.sensorBounds) - } else { - Rect( - 0, - 0, - overlayParams.naturalDisplayWidth, - overlayParams.naturalDisplayHeight - ) - } - } else { + if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) { Rect(overlayParams.sensorBounds) + } else { + Rect( + 0, + 0, + overlayParams.naturalDisplayWidth, + overlayParams.naturalDisplayHeight + ) } val rot = overlayParams.rotation @@ -399,9 +361,9 @@ class UdfpsControllerOverlay @JvmOverloads constructor( if (!shouldRotate(animation)) { Log.v( TAG, "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) + - " animation=$animation" + - " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" + - " isOccluded=${keyguardStateController.isOccluded}" + " animation=$animation" + + " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" + + " isOccluded=${keyguardStateController.isOccluded}" ) } else { Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot)) @@ -412,14 +374,12 @@ class UdfpsControllerOverlay @JvmOverloads constructor( rot ) - if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - RotationUtils.rotateBounds( - sensorBounds, - overlayParams.naturalDisplayWidth, - overlayParams.naturalDisplayHeight, - rot - ) - } + RotationUtils.rotateBounds( + sensorBounds, + overlayParams.naturalDisplayWidth, + overlayParams.naturalDisplayHeight, + rot + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt index 8cc15dadffd2..afe37d496150 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt @@ -43,10 +43,6 @@ class UdfpsKeyguardView( return fingerprintDrawablePlaceHolder } - fun useExpandedOverlay(useExpandedOverlay: Boolean) { - mUseExpandedOverlay = useExpandedOverlay - } - fun isVisible(): Boolean { return visible } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 8ce98a92adbd..3d5be6fb9f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -19,7 +19,6 @@ package com.android.systemui.biometrics import android.animation.ValueAnimator import android.content.res.Configuration import android.util.MathUtils -import android.view.MotionEvent import android.view.View import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle @@ -27,16 +26,15 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.res.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.stack.StackStateAnimator @@ -80,8 +78,6 @@ open class UdfpsKeyguardViewControllerLegacy( ), UdfpsKeyguardViewControllerAdapter { private val uniqueIdentifier = this.toString() - private val useExpandedOverlay: Boolean = - featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) private var showingUdfpsBouncer = false private var udfpsRequested = false private var qsExpansion = 0f @@ -192,24 +188,6 @@ open class UdfpsKeyguardViewControllerLegacy( updateAlpha() updatePauseAuth() } - - /** - * Forward touches to the UdfpsController. This allows the touch to start from outside - * the sensor area and then slide their finger into the sensor area. - */ - override fun onTouch(event: MotionEvent) { - // Don't forward touches if the shade has already started expanding. - if (transitionToFullShadeProgress != 0f) { - return - } - - // Forwarding touches not needed with expanded overlay - if (useExpandedOverlay) { - return - } else { - udfpsController.onTouch(event) - } - } } private val occludingAppBiometricUI: OccludingAppBiometricUI = @@ -294,7 +272,6 @@ open class UdfpsKeyguardViewControllerLegacy( keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this activityLaunchAnimator.addListener(activityLaunchAnimatorListener) - view.mUseExpandedOverlay = useExpandedOverlay view.startIconAsyncInflate { val animationViewInternal: View = view.requireViewById(R.id.udfps_animation_view_internal) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java index 36a42f945235..95e3a76c11d8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java @@ -298,45 +298,34 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { pw.println(" mUdfpsRequested=" + mUdfpsRequested); pw.println(" mInterpolatedDarkAmount=" + mInterpolatedDarkAmount); pw.println(" mAnimationType=" + mAnimationType); - pw.println(" mUseExpandedOverlay=" + mUseExpandedOverlay); } private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener = new AsyncLayoutInflater.OnInflateFinishedListener() { - @Override - public void onInflateFinished(View view, int resid, ViewGroup parent) { - mFullyInflated = true; - mAodFp = view.findViewById(R.id.udfps_aod_fp); - mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp); - mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg); - - updatePadding(); - updateColor(); - updateAlpha(); - - if (mUseExpandedOverlay) { - final LayoutParams lp = (LayoutParams) view.getLayoutParams(); - lp.width = mSensorBounds.width(); - lp.height = mSensorBounds.height(); - RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds)); - lp.setMarginsRelative( - (int) relativeToView.left, - (int) relativeToView.top, - (int) relativeToView.right, - (int) relativeToView.bottom - ); - parent.addView(view, lp); - } else { - parent.addView(view); - } - - // requires call to invalidate to update the color - mLockScreenFp.addValueCallback( - new KeyPath("**"), LottieProperty.COLOR_FILTER, - frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, - PorterDuff.Mode.SRC_ATOP) - ); - mOnFinishInflateRunnable.run(); - } - }; + @Override + public void onInflateFinished(View view, int resid, ViewGroup parent) { + mFullyInflated = true; + mAodFp = view.findViewById(R.id.udfps_aod_fp); + mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp); + mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg); + + updatePadding(); + updateColor(); + updateAlpha(); + + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + lp.width = mSensorBounds.width(); + lp.height = mSensorBounds.height(); + RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds)); + lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top, + (int) relativeToView.right, (int) relativeToView.bottom); + parent.addView(view, lp); + + // requires call to invalidate to update the color + mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER, + frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, + PorterDuff.Mode.SRC_ATOP)); + mOnFinishInflateRunnable.run(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt index 6ce6172c9faa..76bcd6e2863b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt @@ -19,14 +19,12 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint -import android.graphics.PointF import android.graphics.Rect import android.graphics.RectF import android.util.AttributeSet import android.util.Log import android.view.MotionEvent import android.widget.FrameLayout -import com.android.systemui.res.R import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.doze.DozeReceiver @@ -39,10 +37,6 @@ class UdfpsView( context: Context, attrs: AttributeSet? ) : FrameLayout(context, attrs), DozeReceiver { - - // Use expanded overlay when feature flag is true, set by UdfpsViewController - var useExpandedOverlay: Boolean = false - // sensorRect may be bigger than the sensor. True sensor dimensions are defined in // overlayParams.sensorBounds var sensorRect = Rect() @@ -53,14 +47,6 @@ class UdfpsView( textSize = 32f } - private val sensorTouchAreaCoefficient: Float = - context.theme.obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0, 0).use { a -> - require(a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) { - "UdfpsView must contain sensorTouchAreaCoefficient" - } - a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f) - } - /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */ var animationViewController: UdfpsAnimationViewController<*>? = null @@ -94,22 +80,8 @@ class UdfpsView( override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) - val paddingX = animationViewController?.paddingX ?: 0 - val paddingY = animationViewController?.paddingY ?: 0 - // Updates sensor rect in relation to the overlay view - if (useExpandedOverlay) { - animationViewController?.onSensorRectUpdated(RectF(sensorRect)) - } else { - sensorRect.set( - paddingX, - paddingY, - (overlayParams.sensorBounds.width() + paddingX), - (overlayParams.sensorBounds.height() + paddingY) - ) - - animationViewController?.onSensorRectUpdated(RectF(sensorRect)) - } + animationViewController?.onSensorRectUpdated(RectF(sensorRect)) } override fun onAttachedToWindow() { @@ -131,22 +103,6 @@ class UdfpsView( } } - fun isWithinSensorArea(x: Float, y: Float): Boolean { - // The X and Y coordinates of the sensor's center. - val translation = animationViewController?.touchTranslation ?: PointF(0f, 0f) - val cx = sensorRect.centerX() + translation.x - val cy = sensorRect.centerY() + translation.y - // Radii along the X and Y axes. - val rx = (sensorRect.right - sensorRect.left) / 2.0f - val ry = (sensorRect.bottom - sensorRect.top) / 2.0f - - return x > cx - rx * sensorTouchAreaCoefficient && - x < cx + rx * sensorTouchAreaCoefficient && - y > cy - ry * sensorTouchAreaCoefficient && - y < cy + ry * sensorTouchAreaCoefficient && - !(animationViewController?.shouldPauseAuth() ?: false) - } - fun configureDisplay(onDisplayConfigured: Runnable) { isDisplayConfigured = true animationViewController?.onDisplayConfiguring() diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt index 943216ebd0d6..b2a7607cb323 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt @@ -17,6 +17,8 @@ package com.android.systemui.bouncer.data.repository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -24,11 +26,18 @@ import kotlinx.coroutines.flow.asStateFlow /** Provides access to bouncer-related application state. */ @SysUISingleton -class BouncerRepository @Inject constructor() { +class BouncerRepository +@Inject +constructor( + flags: FeatureFlagsClassic, +) { private val _message = MutableStateFlow<String?>(null) /** The user-facing message to show in the bouncer. */ val message: StateFlow<String?> = _message.asStateFlow() + /** Whether the user switcher should be displayed within the bouncer UI on large screens. */ + val isUserSwitcherVisible: Boolean = flags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER) + fun setMessage(message: String?) { _message.value = message } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 0c0236999e39..4ce1422b91e8 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -96,6 +96,9 @@ constructor( /** Whether the pattern should be visible for the currently-selected user. */ val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible + /** Whether the user switcher should be displayed within the bouncer UI on large screens. */ + val isUserSwitcherVisible: Boolean = repository.isUserSwitcherVisible + init { if (flags.isEnabled()) { // Clear the message if moved from throttling to no-longer throttling. diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 2cb98d879e69..ef0609a99e05 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -99,6 +99,8 @@ class BouncerViewModel( initialValue = emptyList(), ) + val isUserSwitcherVisible: Boolean = bouncerInteractor.isUserSwitcherVisible + private val isInputEnabled: StateFlow<Boolean> = bouncerInteractor.isThrottled .map { !it } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt index 4efc21b41e6a..4b78e9eeb265 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt @@ -35,10 +35,8 @@ import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit * The input is guaranteed to always contain a initial [ClearAll] token as a sentinel, thus clients * can always assume there is a 'ClearAll' watermark available. */ -data class PinInputViewModel -internal constructor( - @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal val input: List<EntryToken> +data class PinInputViewModel( + @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) val input: List<EntryToken> ) { init { require(input.firstOrNull() is ClearAll) { "input does not begin with a ClearAll token" } @@ -132,8 +130,7 @@ sealed interface EntryToken : Comparable<EntryToken> { val sequenceNumber: Int /** The pin bouncer [input] as digits 0-9. */ - data class Digit - internal constructor(val input: Int, override val sequenceNumber: Int = nextSequenceNumber++) : + data class Digit(val input: Int, override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken { init { check(input in 0..9) @@ -144,8 +141,7 @@ sealed interface EntryToken : Comparable<EntryToken> { * Marker to indicate the input is completely cleared, and subsequent [EntryToken]s mark a new * pin entry. */ - data class ClearAll - internal constructor(override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken + data class ClearAll(override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken override fun compareTo(other: EntryToken): Int = compareValuesBy(this, other, EntryToken::sequenceNumber) diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java index c0ee71cf0dc8..0dfaf0f4318d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java @@ -18,8 +18,15 @@ package com.android.systemui.classifier; import android.view.MotionEvent; +import javax.inject.Inject; + /** */ public class FalsingCollectorFake implements FalsingCollector { + + @Inject + public FalsingCollectorFake() { + } + @Override public void onSuccessfulUnlock() { } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt index f6e02969e4c2..53b6879db3d7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt @@ -15,7 +15,10 @@ interface CommunalRepository { class CommunalRepositoryImpl @Inject constructor( - featureFlags: FeatureFlagsClassic, + private val featureFlags: FeatureFlagsClassic, ) : CommunalRepository { - override val isCommunalEnabled = featureFlags.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) + override val isCommunalEnabled: Boolean + get() = + featureFlags.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && + featureFlags.isEnabled(Flags.COMMUNAL_HUB) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt new file mode 100644 index 000000000000..9a9b0e29cbc4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt @@ -0,0 +1,144 @@ +/* + * 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.provider.Settings +import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED +import android.provider.Settings.Secure.HubModeTutorialState +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +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 com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext + +/** + * Repository for the current state of hub mode tutorial. Valid states are defined in + * [HubModeTutorialState]. + */ +interface CommunalTutorialRepository { + /** Emits the tutorial state stored in Settings */ + val tutorialSettingState: StateFlow<Int> + + /** Update the tutorial state */ + suspend fun setTutorialState(@HubModeTutorialState state: Int) +} + +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class CommunalTutorialRepositoryImpl +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, + userRepository: UserRepository, + private val secureSettings: SecureSettings, + private val userTracker: UserTracker, + @CommunalLog logBuffer: LogBuffer, +) : CommunalTutorialRepository { + + companion object { + private const val TAG = "CommunalTutorialRepository" + } + + private data class SettingsState( + @HubModeTutorialState val hubModeTutorialState: Int? = null, + ) + + private val logger = Logger(logBuffer, TAG) + + private val settingsState: Flow<SettingsState> = + userRepository.selectedUserInfo + .flatMapLatest { observeSettings() } + .shareIn(scope = applicationScope, started = SharingStarted.WhileSubscribed()) + + /** Emits the state of tutorial state in settings */ + override val tutorialSettingState: StateFlow<Int> = + settingsState + .map { it.hubModeTutorialState } + .filterNotNull() + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = HUB_MODE_TUTORIAL_NOT_STARTED + ) + + private fun observeSettings(): Flow<SettingsState> = + secureSettings + .observerFlow( + userId = userTracker.userId, + names = + arrayOf( + Settings.Secure.HUB_MODE_TUTORIAL_STATE, + ) + ) + // Force an update + .onStart { emit(Unit) } + .map { readFromSettings() } + + private suspend fun readFromSettings(): SettingsState = + withContext(backgroundDispatcher) { + val userId = userTracker.userId + val hubModeTutorialState = + secureSettings.getIntForUser( + Settings.Secure.HUB_MODE_TUTORIAL_STATE, + HUB_MODE_TUTORIAL_NOT_STARTED, + userId, + ) + val settingsState = SettingsState(hubModeTutorialState) + logger.d({ "Communal tutorial state for user $int1 in settings: $str1" }) { + int1 = userId + str1 = settingsState.hubModeTutorialState.toString() + } + + settingsState + } + + override suspend fun setTutorialState(state: Int): Unit = + withContext(backgroundDispatcher) { + val userId = userTracker.userId + if (tutorialSettingState.value == state) { + return@withContext + } + logger.d({ "Update communal tutorial state to $int1 for user $int2" }) { + int1 = state + int2 = userId + } + secureSettings.putIntForUser( + Settings.Secure.HUB_MODE_TUTORIAL_STATE, + state, + userId, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryModule.kt new file mode 100644 index 000000000000..69b0a27c55a8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryModule.kt @@ -0,0 +1,27 @@ +/* + * 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 dagger.Binds +import dagger.Module + +@Module +interface CommunalTutorialRepositoryModule { + @Binds + fun communalTutorialRepository(impl: CommunalTutorialRepositoryImpl): CommunalTutorialRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 9fb8da3e76af..04bb6ae75e60 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -28,11 +28,13 @@ import kotlinx.coroutines.flow.Flow class CommunalInteractor @Inject constructor( - communalRepository: CommunalRepository, + private val communalRepository: CommunalRepository, widgetRepository: CommunalWidgetRepository, ) { + /** Whether communal features are enabled. */ - val isCommunalEnabled: Boolean = communalRepository.isCommunalEnabled + val isCommunalEnabled: Boolean + get() = communalRepository.isCommunalEnabled /** 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/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt new file mode 100644 index 000000000000..276df4eb68ec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt @@ -0,0 +1,48 @@ +/* + * 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 android.provider.Settings +import com.android.systemui.communal.data.repository.CommunalTutorialRepository +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged + +/** Encapsulates business-logic related to communal tutorial state. */ +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class CommunalTutorialInteractor +@Inject +constructor( + communalTutorialRepository: CommunalTutorialRepository, + keyguardInteractor: KeyguardInteractor, +) { + /** An observable for whether the tutorial is available. */ + val isTutorialAvailable: Flow<Boolean> = + combine( + keyguardInteractor.isKeyguardVisible, + communalTutorialRepository.tutorialSettingState, + ) { isKeyguardVisible, tutorialSettingState -> + isKeyguardVisible && + tutorialSettingState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED + } + .distinctUntilChanged() +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt new file mode 100644 index 000000000000..dab6819e1028 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt @@ -0,0 +1,66 @@ +/* + * 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 android.widget.TextView +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.launch + +/** View binder for communal tutorial indicator shown on keyguard. */ +object CommunalTutorialIndicatorViewBinder { + fun bind( + view: TextView, + viewModel: CommunalTutorialIndicatorViewModel, + ): DisposableHandle { + val disposableHandle = + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.showIndicator.collect { isVisible -> + updateView( + view = view, + isIndicatorVisible = isVisible, + ) + } + } + } + } + + return disposableHandle + } + + private fun updateView( + isIndicatorVisible: Boolean, + view: TextView, + ) { + if (!isIndicatorVisible) { + view.isGone = true + return + } + + if (!view.isVisible) { + view.isVisible = true + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt new file mode 100644 index 000000000000..027cc96350f5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt @@ -0,0 +1,130 @@ +/* + * 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.content.res.Resources +import android.graphics.Typeface +import android.graphics.Typeface.NORMAL +import android.view.Gravity +import android.view.View +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.content.res.ResourcesCompat +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder +import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.shared.model.KeyguardSection +import com.android.systemui.keyguard.ui.view.layout.sections.removeView +import com.android.systemui.res.R +import javax.inject.Inject +import kotlinx.coroutines.DisposableHandle + +class CommunalTutorialIndicatorSection +@Inject +constructor( + @Main private val resources: Resources, + private val communalTutorialIndicatorViewModel: CommunalTutorialIndicatorViewModel, + private val communalInteractor: CommunalInteractor, +) : KeyguardSection() { + private var communalTutorialIndicatorHandle: DisposableHandle? = null + + override fun addViews(constraintLayout: ConstraintLayout) { + if (!communalInteractor.isCommunalEnabled) { + return + } + val padding = + constraintLayout.resources.getDimensionPixelSize( + R.dimen.communal_tutorial_indicator_padding + ) + val view = + TextView(constraintLayout.context).apply { + id = R.id.communal_tutorial_indicator + visibility = View.GONE + background = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_bg, + context.theme + ) + foreground = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_selected_border, + context.theme + ) + gravity = Gravity.CENTER_VERTICAL + typeface = Typeface.create("google-sans", NORMAL) + text = constraintLayout.context.getString(R.string.communal_tutorial_indicator_text) + setPadding(padding, padding, padding, padding) + } + constraintLayout.addView(view) + } + + override fun bindData(constraintLayout: ConstraintLayout) { + if (!communalInteractor.isCommunalEnabled) { + return + } + communalTutorialIndicatorHandle = + CommunalTutorialIndicatorViewBinder.bind( + constraintLayout.requireViewById(R.id.communal_tutorial_indicator), + communalTutorialIndicatorViewModel, + ) + } + + override fun applyConstraints(constraintSet: ConstraintSet) { + if (!communalInteractor.isCommunalEnabled) { + return + } + val tutorialIndicatorId = R.id.communal_tutorial_indicator + val width = resources.getDimensionPixelSize(R.dimen.communal_tutorial_indicator_fixed_width) + val horizontalOffsetMargin = + resources.getDimensionPixelSize(R.dimen.communal_tutorial_indicator_horizontal_offset) + + constraintSet.apply { + constrainWidth(tutorialIndicatorId, width) + constrainHeight(tutorialIndicatorId, WRAP_CONTENT) + connect( + tutorialIndicatorId, + ConstraintSet.RIGHT, + ConstraintSet.PARENT_ID, + ConstraintSet.RIGHT, + horizontalOffsetMargin + ) + connect( + tutorialIndicatorId, + ConstraintSet.TOP, + ConstraintSet.PARENT_ID, + ConstraintSet.TOP + ) + connect( + tutorialIndicatorId, + ConstraintSet.BOTTOM, + ConstraintSet.PARENT_ID, + ConstraintSet.BOTTOM + ) + } + } + + override fun removeViews(constraintLayout: ConstraintLayout) { + communalTutorialIndicatorHandle?.dispose() + constraintLayout.removeView(R.id.communal_tutorial_indicator) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt new file mode 100644 index 000000000000..eaf95508cf12 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.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.viewmodel + +import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** View model for communal tutorial indicator on keyguard */ +class CommunalTutorialIndicatorViewModel +@Inject +constructor( + communalTutorialInteractor: CommunalTutorialInteractor, +) { + /** An observable for whether the tutorial indicator view should be visible. */ + val showIndicator: Flow<Boolean> = communalTutorialInteractor.isTutorialAvailable +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt index 8e3b5109339c..436b8cb851ab 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt @@ -22,7 +22,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.qs.SettingObserver +import com.android.systemui.qs.UserSettingObserver import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.settings.SecureSettings import javax.inject.Inject @@ -64,7 +64,8 @@ constructor( .flatMapLatest { userInfo -> conflatedCallbackFlow { val observer = - object : SettingObserver(secureSettings, null, setting, userInfo.id) { + object : + UserSettingObserver(secureSettings, null, setting, userInfo.id) { override fun handleValueChanged( value: Int, observedChange: Boolean diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index 046ccf165d07..a90980fddfb0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -18,7 +18,6 @@ package com.android.systemui.dagger; import com.android.systemui.globalactions.ShutdownUiModule; import com.android.systemui.keyguard.CustomizationProvider; -import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; @@ -33,7 +32,6 @@ import dagger.Subcomponent; DependencyProvider.class, NotificationInsetsModule.class, QsFrameTranslateModule.class, - ShadeModule.class, ShutdownUiModule.class, SystemUIBinder.class, SystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 9e5fd5572dbc..1dd4abfa0767 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -21,12 +21,9 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; import android.hardware.SensorPrivacyManager; -import android.os.Handler; -import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardViewController; import com.android.systemui.battery.BatterySaverModule; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; @@ -34,7 +31,6 @@ import com.android.systemui.media.dagger.MediaModule; import com.android.systemui.navigationbar.NavigationBarControllerModule; import com.android.systemui.navigationbar.gestural.GestureModule; import com.android.systemui.plugins.qs.QSFactory; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.dagger.PowerModule; import com.android.systemui.qs.dagger.QSModule; import com.android.systemui.qs.tileimpl.QSFactoryImpl; @@ -45,7 +41,7 @@ import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; -import com.android.systemui.shade.ShadeExpansionStateManager; +import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyboardShortcutsModule; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -53,20 +49,13 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule; import com.android.systemui.statusbar.events.StatusBarEventsModule; -import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; -import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.phone.DozeServiceHost; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.HeadsUpModule; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule; -import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.AospPolicyModule; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl; import com.android.systemui.statusbar.policy.SensorPrivacyController; @@ -100,11 +89,13 @@ import javax.inject.Named; BatterySaverModule.class, CollapsedStatusBarFragmentStartableModule.class, GestureModule.class, + HeadsUpModule.class, MediaModule.class, MultiUserUtilsModule.class, NavigationBarControllerModule.class, PowerModule.class, QSModule.class, + ShadeModule.class, ReferenceScreenshotModule.class, RotationLockModule.class, SceneContainerFrameworkModule.class, @@ -161,38 +152,6 @@ public abstract class ReferenceSystemUIModule { return true; } - @SysUISingleton - @Provides - static HeadsUpManagerPhone provideHeadsUpManagerPhone( - Context context, - HeadsUpManagerLogger headsUpManagerLogger, - StatusBarStateController statusBarStateController, - KeyguardBypassController bypassController, - GroupMembershipManager groupManager, - VisualStabilityProvider visualStabilityProvider, - ConfigurationController configurationController, - @Main Handler handler, - AccessibilityManagerWrapper accessibilityManagerWrapper, - UiEventLogger uiEventLogger, - ShadeExpansionStateManager shadeExpansionStateManager) { - return new HeadsUpManagerPhone( - context, - headsUpManagerLogger, - statusBarStateController, - bypassController, - groupManager, - visualStabilityProvider, - configurationController, - handler, - accessibilityManagerWrapper, - uiEventLogger, - shadeExpansionStateManager - ); - } - - @Binds - abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); - @Provides @SysUISingleton static Recents provideRecents(Context context, RecentsImplementation recentsImplementation, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index d89332d6d686..5d6949b3e87f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -37,6 +37,7 @@ import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable +import com.android.systemui.keyguard.ui.binder.AlternateBouncerBinder import com.android.systemui.keyguard.ui.binder.KeyguardDismissActionBinder import com.android.systemui.keyguard.ui.binder.KeyguardDismissBinder import com.android.systemui.log.SessionTracker @@ -92,6 +93,11 @@ abstract class SystemUICoreStartableModule { @ClassKey(AuthController::class) abstract fun bindAuthController(service: AuthController): CoreStartable + @Binds + @IntoMap + @ClassKey(AlternateBouncerBinder::class) + abstract fun bindAlternateBouncerBinder(impl: AlternateBouncerBinder): CoreStartable + /** Inject into BiometricNotificationService */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 4b6ad6d9be03..7d4e1a1011db 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -33,7 +33,6 @@ import com.android.systemui.aconfig.AConfigModule; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; import com.android.systemui.authentication.AuthenticationModule; -import com.android.systemui.biometrics.AlternateUdfpsTouchProvider; import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider; import com.android.systemui.biometrics.FingerprintReEnrollNotification; import com.android.systemui.biometrics.UdfpsDisplayModeProvider; @@ -56,6 +55,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FlagsModule; import com.android.systemui.keyboard.KeyboardModule; +import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule; import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.log.dagger.MonitorLog; @@ -181,6 +181,7 @@ import javax.inject.Named; FalsingModule.class, FlagsModule.class, FooterActionsModule.class, + KeyEventRepositoryModule.class, KeyboardModule.class, KeyguardBlueprintModule.class, LetterboxModule.class, @@ -298,9 +299,6 @@ public abstract class SystemUIModule { abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider(); @BindsOptionalOf - abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider(); - - @BindsOptionalOf abstract FingerprintInteractiveToAuthProvider optionalFingerprintInteractiveToAuthProvider(); @BindsOptionalOf diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt index 5b85ad01301b..1e29e1fa3197 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt @@ -2,7 +2,7 @@ package com.android.systemui.deviceentry.data.repository import com.android.internal.widget.LockPatternUtils import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging -import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -51,7 +51,7 @@ interface DeviceEntryRepository { * When this is `false`, an automatically-triggered face unlock shouldn't automatically dismiss * the lockscreen. */ - fun isBypassEnabled(): Boolean + val isBypassEnabled: StateFlow<Boolean> } /** Encapsulates application state for device entry. */ @@ -68,7 +68,7 @@ constructor( ) : DeviceEntryRepository { override val isUnlocked = - ConflatedCallbackFlow.conflatedCallbackFlow { + conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { override fun onUnlockedChanged() { @@ -112,7 +112,24 @@ constructor( } } - override fun isBypassEnabled() = keyguardBypassController.bypassEnabled + override val isBypassEnabled: StateFlow<Boolean> = + conflatedCallbackFlow { + val listener = + object : KeyguardBypassController.OnBypassStateChangedListener { + override fun onBypassStateChanged(isEnabled: Boolean) { + trySend(isEnabled) + } + } + keyguardBypassController.registerOnBypassStateChangedListener(listener) + awaitClose { + keyguardBypassController.unregisterOnBypassStateChangedListener(listener) + } + } + .stateIn( + applicationScope, + SharingStarted.Eagerly, + initialValue = keyguardBypassController.bypassEnabled, + ) companion object { private const val TAG = "DeviceEntryRepositoryImpl" diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 5612c9a488ff..e96e318fd59d 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -106,5 +106,5 @@ constructor( * authentication challenge via face unlock or fingerprint sensor can automatically bypass the * lock screen. */ - fun isBypassEnabled() = repository.isBypassEnabled() + val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java index 126a1b5e7115..694695017efd 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java @@ -31,7 +31,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.os.Bundle; -import android.os.UserHandle; import android.util.Log; import androidx.annotation.NonNull; @@ -48,6 +47,7 @@ import java.util.ArrayList; import java.util.Map; import java.util.Objects; import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import javax.inject.Inject; @@ -55,15 +55,15 @@ import javax.inject.Named; /** * Concrete implementation of the a Flag manager that returns default values for debug builds - * + * <p> * Flags can be set (or unset) via the following adb command: - * + * <p> * adb shell cmd statusbar flag <id> <on|off|toggle|erase> - * + * <p> * Alternatively, you can change flags via a broadcast intent: - * + * <p> * adb shell am broadcast -a com.android.systemui.action.SET_FLAG --ei id <id> [--ez value <0|1>] - * + * <p> * To restore a flag back to its default, leave the `--ez value <0|1>` off of the command. */ @SysUISingleton @@ -77,9 +77,9 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { private final SystemPropertiesHelper mSystemProperties; private final ServerFlagReader mServerFlagReader; private final Map<String, Flag<?>> mAllFlags; - private final Map<String, Boolean> mBooleanFlagCache = new TreeMap<>(); - private final Map<String, String> mStringFlagCache = new TreeMap<>(); - private final Map<String, Integer> mIntFlagCache = new TreeMap<>(); + private final Map<String, Boolean> mBooleanFlagCache = new ConcurrentHashMap<>(); + private final Map<String, String> mStringFlagCache = new ConcurrentHashMap<>(); + private final Map<String, Integer> mIntFlagCache = new ConcurrentHashMap<>(); private final Restarter mRestarter; private final ServerFlagReader.ChangeListener mOnPropertiesChanged = @@ -160,87 +160,92 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { private boolean isEnabledInternal(@NotNull BooleanFlag flag) { String name = flag.getName(); - if (!mBooleanFlagCache.containsKey(name)) { - mBooleanFlagCache.put(name, - readBooleanFlagInternal(flag, flag.getDefault())); + + Boolean value = mBooleanFlagCache.get(name); + if (value == null) { + value = readBooleanFlagInternal(flag, flag.getDefault()); + mBooleanFlagCache.put(name, value); } - return mBooleanFlagCache.get(name); + return value; } @Override public boolean isEnabled(@NonNull ResourceBooleanFlag flag) { String name = flag.getName(); - if (!mBooleanFlagCache.containsKey(name)) { - mBooleanFlagCache.put(name, - readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId()))); + Boolean value = mBooleanFlagCache.get(name); + if (value == null) { + value = readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId())); + mBooleanFlagCache.put(name, value); } - return mBooleanFlagCache.get(name); + return value; } @Override public boolean isEnabled(@NonNull SysPropBooleanFlag flag) { String name = flag.getName(); - if (!mBooleanFlagCache.containsKey(name)) { - // Use #readFlagValue to get the default. That will allow it to fall through to - // teamfood if need be. - mBooleanFlagCache.put( - name, + Boolean value = mBooleanFlagCache.get(name); + if (value == null) { + value = readBooleanFlagInternal(flag, mSystemProperties.getBoolean( flag.getName(), readBooleanFlagInternal(flag, flag.getDefault()))); + mBooleanFlagCache.put(name, value); } - - return mBooleanFlagCache.get(name); + return value; } @NonNull @Override public String getString(@NonNull StringFlag flag) { String name = flag.getName(); - if (!mStringFlagCache.containsKey(name)) { - mStringFlagCache.put(name, - readFlagValueInternal(name, flag.getDefault(), StringFlagSerializer.INSTANCE)); + String value = mStringFlagCache.get(name); + if (value == null) { + value = readFlagValueInternal(name, flag.getDefault(), StringFlagSerializer.INSTANCE); + mStringFlagCache.put(name, value); } - return mStringFlagCache.get(name); + return value; } @NonNull @Override public String getString(@NonNull ResourceStringFlag flag) { String name = flag.getName(); - if (!mStringFlagCache.containsKey(name)) { - mStringFlagCache.put(name, - readFlagValueInternal(name, mResources.getString(flag.getResourceId()), - StringFlagSerializer.INSTANCE)); + String value = mStringFlagCache.get(name); + if (value == null) { + value = readFlagValueInternal( + name, + mResources.getString(flag.getResourceId()), + StringFlagSerializer.INSTANCE); + mStringFlagCache.put(name, value); } - - return mStringFlagCache.get(name); + return value; } @Override public int getInt(@NonNull IntFlag flag) { String name = flag.getName(); - if (!mIntFlagCache.containsKey(name)) { - mIntFlagCache.put(name, - readFlagValueInternal(name, flag.getDefault(), IntFlagSerializer.INSTANCE)); + Integer value = mIntFlagCache.get(name); + if (value == null) { + value = readFlagValueInternal(name, flag.getDefault(), IntFlagSerializer.INSTANCE); + mIntFlagCache.put(name, value); } - return mIntFlagCache.get(name); + return value; } @Override public int getInt(@NonNull ResourceIntFlag flag) { String name = flag.getName(); - if (!mIntFlagCache.containsKey(name)) { - mIntFlagCache.put(name, - readFlagValueInternal(name, mResources.getInteger(flag.getResourceId()), - IntFlagSerializer.INSTANCE)); + Integer value = mIntFlagCache.get(name); + if (value == null) { + value = readFlagValueInternal( + name, mResources.getInteger(flag.getResourceId()), IntFlagSerializer.INSTANCE); + mIntFlagCache.put(name, value); } - - return mIntFlagCache.get(name); + return value; } /** Specific override for Boolean flags that checks against the teamfood list.*/ @@ -313,8 +318,7 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { Log.w(TAG, "Failed to set flag " + name + " to " + value); return; } - mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), data, - UserHandle.USER_CURRENT); + mGlobalSettings.putString(mFlagManager.nameToSettingsKey(name), data); } <T> void eraseFlag(Flag<T> flag) { @@ -348,8 +352,7 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { /** Works just like {@link #eraseFlag(String)} except that it doesn't restart SystemUI. */ private void eraseInternal(String name) { // We can't actually "erase" things from settings, but we can set them to empty! - mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), "", - UserHandle.USER_CURRENT); + mGlobalSettings.putString(mFlagManager.nameToSettingsKey(name), ""); Log.i(TAG, "Erase name " + name); } @@ -531,11 +534,22 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("can override: true"); + pw.println("booleans: " + mBooleanFlagCache.size()); - mBooleanFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": " + value)); + // Sort our flags for dumping + TreeMap<String, Boolean> dumpBooleanMap = new TreeMap<>(mBooleanFlagCache); + dumpBooleanMap.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": " + value)); + pw.println("Strings: " + mStringFlagCache.size()); - mStringFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key + // Sort our flags for dumping + TreeMap<String, String> dumpStringMap = new TreeMap<>(mStringFlagCache); + dumpStringMap.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": [length=" + value.length() + "] \"" + value + "\"")); + + pw.println("Integers: " + mIntFlagCache.size()); + // Sort our flags for dumping + TreeMap<String, Integer> dumpIntMap = new TreeMap<>(mIntFlagCache); + dumpIntMap.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": " + value)); } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java index e3cc2b02177c..bf9018a1f99c 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java @@ -177,6 +177,13 @@ public class FlagCommand implements Command { || (flag instanceof SysPropFlag); } + private Boolean isTeamfoodFlag(Flag<?> flag) { + if (!(flag instanceof BooleanFlag)) { + return null; + } + return flag.getTeamfood(); + } + private boolean isBooleanFlagEnabled(Flag<?> flag) { if (flag instanceof ReleasedFlag) { return mFeatureFlags.isEnabled((ReleasedFlag) flag); @@ -232,11 +239,13 @@ public class FlagCommand implements Command { for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) { pw.print(" "); } - pw.println(" Value"); + pw.print(" Value "); + pw.println(" Teamfood?"); for (int i = 0; i < longestFieldName; i++) { pw.print("="); } - pw.println(" ========"); + pw.println(" ======= ==========="); + for (String fieldName : fields.keySet()) { Flag<?> flag = fields.get(fieldName); if (!mAllFlags.containsKey(flag.getName())) { @@ -249,7 +258,19 @@ public class FlagCommand implements Command { } pw.print(" "); if (isBooleanFlag(flag)) { - pw.println(isBooleanFlagEnabled(flag)); + boolean enabled = isBooleanFlagEnabled(flag); + pw.print(enabled); + if (enabled) { + pw.print(" "); + } else { + pw.print(" "); + } + Boolean teamfood = isTeamfoodFlag(flag); + if (teamfood != null) { + pw.print(teamfood); + } + pw.println(); + } else if (isStringFlag(flag)) { pw.println(getStringFlag(flag)); } else if (isIntFlag(flag)) { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 8587329de1bd..c7f4afc2e1a2 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -40,8 +40,8 @@ object Flags { // 100 - notification // TODO(b/297792660): Tracking Bug - val ADD_TRANSIENT_HUN_IN_STACK_STATE_ANIMATOR = - unreleasedFlag("add_transient_hun_in_stack_state_animator", teamfood = false) + @JvmField val UNCLEARED_TRANSIENT_HUN_FIX = + unreleasedFlag("uncleared_transient_hun_fix", teamfood = false) // TODO(b/298308067): Tracking Bug @JvmField val SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX = @@ -144,11 +144,6 @@ object Flags { "lockscreen_custom_clocks" ) - // TODO(b/275694445): Tracking Bug - @JvmField - val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = - releasedFlag("lockscreen_without_secure_lock_when_dreaming") - // TODO(b/286092087): Tracking Bug @JvmField val ENABLE_SYSTEM_UI_DREAM_CONTROLLER = unreleasedFlag("enable_system_ui_dream_controller") @@ -176,7 +171,11 @@ object Flags { */ // TODO(b/281655028): Tracking bug @JvmField - val LIGHT_REVEAL_MIGRATION = unreleasedFlag("light_reveal_migration", teamfood = false) + val LIGHT_REVEAL_MIGRATION = unreleasedFlag("light_reveal_migration", teamfood = true) + + // TODO(b/301915812): Tracking Bug + @JvmField + val NEW_AOD_TRANSITION = unreleasedFlag("new_aod_transition", teamfood = true) /** Flag to control the migration of face auth to modern architecture. */ // TODO(b/262838215): Tracking bug @@ -230,8 +229,7 @@ object Flags { /** Whether page transition animations in the wallpaper picker are enabled */ // TODO(b/291710220): Tracking bug. @JvmField - val WALLPAPER_PICKER_PAGE_TRANSITIONS = - unreleasedFlag("wallpaper_picker_page_transitions", teamfood = true) + val WALLPAPER_PICKER_PAGE_TRANSITIONS = releasedFlag("wallpaper_picker_page_transitions") /** Add "Apply" button to wall paper picker's grid preview page. */ // TODO(b/294866904): Tracking bug. @@ -306,6 +304,11 @@ object Flags { @JvmField val WALLPAPER_PICKER_PREVIEW_ANIMATION = releasedFlag("wallpaper_picker_preview_animation") + /** Flag to enable rest to unlock feature. */ + // TODO(b/303672286): Tracking bug + @JvmField + val REST_TO_UNLOCK: UnreleasedFlag = unreleasedFlag("rest_to_unlock") + /** * TODO(b/278086361): Tracking bug * Complete rewrite of the interactions between System UI and Window Manager involving keyguard @@ -328,7 +331,7 @@ object Flags { /** Flag to use a separate view for the alternate bouncer. */ // TODO(b/300440924): Tracking bug @JvmField - val ALTERNATE_BOUNCER_REFACTOR: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view") + val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view") // 300 - power menu // TODO(b/254512600): Tracking Bug @@ -664,8 +667,6 @@ object Flags { @JvmField val NOTE_TASKS = releasedFlag("keycode_flag") // 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.) - // TODO(b/259264861): Tracking Bug - @JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag("udfps_new_touch_detection") // 2300 - stylus @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag("track_stylus_ever_used") @@ -783,8 +784,7 @@ object Flags { // TODO(b/290213663): Tracking Bug @JvmField - val ONE_WAY_HAPTICS_API_MIGRATION = - unreleasedFlag("oneway_haptics_api_migration", teamfood = true) + val ONE_WAY_HAPTICS_API_MIGRATION = releasedFlag("oneway_haptics_api_migration") /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */ @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt index eaecda52a5a2..3fe68062f19a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt @@ -28,27 +28,23 @@ import com.android.systemui.Dependency * flag-disabled builds, but with a check that should crash eng builds or tests when the * expectation is violated. * - * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it, + * The constructors require that you provide a [FeatureFlags] instance. If you're using this in a + * View class, it's acceptable to ue the [forView] constructor methods, which do not require one, * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes - * inside views where injecting flag values after initialization can be error-prone. + * inside Views where injecting flag values after initialization can be error-prone. */ -class ViewRefactorFlag +class RefactorFlag private constructor( private val injectedFlags: FeatureFlags?, - private val flag: BooleanFlag, + private val flagName: Any, private val readFlagValue: (FeatureFlags) -> Boolean ) { - @JvmOverloads constructor( - flags: FeatureFlags? = null, + flags: FeatureFlags, flag: UnreleasedFlag ) : this(flags, flag, { it.isEnabled(flag) }) - @JvmOverloads - constructor( - flags: FeatureFlags? = null, - flag: ReleasedFlag - ) : this(flags, flag, { it.isEnabled(flag) }) + constructor(flags: FeatureFlags, flag: ReleasedFlag) : this(flags, flag, { it.isEnabled(flag) }) /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */ val isEnabled by lazy { @@ -69,7 +65,8 @@ private constructor( * } * ```` */ - fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." } + fun assertDisabled() = + check(!isEnabled) { "Code path not supported when $flagName is enabled." } /** * Called to ensure code is only run when the flag is enabled. This protects users from the @@ -87,13 +84,25 @@ private constructor( */ fun expectEnabled(): Boolean { if (!isEnabled) { - val message = "Code path not supported when $flag is disabled." + val message = "Code path not supported when $flagName is disabled." Log.wtf(TAG, message, Exception(message)) } return isEnabled } - private companion object { - private const val TAG = "ViewRefactorFlag" + companion object { + private const val TAG = "RefactorFlag" + + /** Construct a [RefactorFlag] within View construction where injection is impossible. */ + @JvmStatic + @JvmOverloads + fun forView(flag: UnreleasedFlag, flags: FeatureFlags? = null) = + RefactorFlag(flags, flag) { it.isEnabled(flag) } + + /** Construct a [RefactorFlag] within View construction where injection is impossible. */ + @JvmStatic + @JvmOverloads + fun forView(flag: ReleasedFlag, flags: FeatureFlags? = null) = + RefactorFlag(flags, flag) { it.isEnabled(flag) } } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index ac402303bf69..c6c1f79c7113 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -749,7 +749,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @VisibleForTesting boolean shouldDisplayBugReport(@Nullable UserInfo user) { return user != null && user.isAdmin() - && mGlobalSettings.getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 0, + && mSecureSettings.getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 0, user.id) != 0; } @@ -1091,7 +1091,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @Override public boolean showBeforeProvisioning() { - return Build.isDebuggable() && mGlobalSettings.getIntForUser( + return Build.isDebuggable() && mSecureSettings.getIntForUser( Settings.Secure.BUGREPORT_IN_POWER_MENU, 0, getCurrentUser().id) != 0 && getCurrentUser().isAdmin(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt new file mode 100644 index 000000000000..5bc5d0b290ca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt @@ -0,0 +1,56 @@ +/* + * 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.data.repository + +import android.view.KeyEvent +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.CommandQueue +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** Defines interface for classes that encapsulate application state for key event presses. */ +interface KeyEventRepository { + /** Observable for whether the power button key is pressed/down or not. */ + val isPowerButtonDown: Flow<Boolean> +} + +@SysUISingleton +class KeyEventRepositoryImpl +@Inject +constructor( + val commandQueue: CommandQueue, +) : KeyEventRepository { + override val isPowerButtonDown: Flow<Boolean> = conflatedCallbackFlow { + val callback = + object : CommandQueue.Callbacks { + override fun handleSystemKey(event: KeyEvent) { + if (event.keyCode == KeyEvent.KEYCODE_POWER) { + trySendWithFailureLogging(event.isDown, TAG, "updated isPowerButtonDown") + } + } + } + commandQueue.addCallback(callback) + awaitClose { commandQueue.removeCallback(callback) } + } + + companion object { + private const val TAG = "KeyEventRepositoryImpl" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt new file mode 100644 index 000000000000..afba5dbd84c1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt @@ -0,0 +1,25 @@ +/* + * 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.data.repository + +import dagger.Binds +import dagger.Module + +@Module +interface KeyEventRepositoryModule { + @Binds fun keyEventRepository(impl: KeyEventRepositoryImpl): KeyEventRepository +} 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 index 3f2f67dbba37..9949fa589cd5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt @@ -13,55 +13,23 @@ * 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 com.android.systemui.keyevent.data.repository.KeyEventRepository 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. + * Business logic for all key event state. This includes all key events, regardless of whether + * they've been handled or not by a consumer. + * + * For key events that SysUI wants to properly handle, see [SysUIKeyEventHandler]. */ @SysUISingleton class KeyEventInteractor @Inject constructor( - private val backActionInteractor: BackActionInteractor, - private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor, + repository: KeyEventRepository, ) { - 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 - } - } + val isPowerButtonDown = repository.isPowerButtonDown } diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt new file mode 100644 index 000000000000..1febc79b8241 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt @@ -0,0 +1,69 @@ +/* + * 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. + * + * To observe any key event states, see [KeyEventInteractor]. + */ +@SysUISingleton +class SysUIKeyEventHandler +@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/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index a1f0e7723597..b45613e0e182 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -269,6 +269,11 @@ public class KeyguardService extends Service { } } + @Override + public void onTransitionConsumed(IBinder transition, boolean aborted) + throws RemoteException { + } + private static void initAlphaForAnimationTargets(@NonNull SurfaceControl.Transaction t, @NonNull RemoteAnimationTarget[] targets) { for (RemoteAnimationTarget target : targets) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index f500017e3a33..86bf368791bc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -82,7 +82,8 @@ constructor( val statusViewComponent = keyguardStatusViewComponentFactory.build( LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, null) - as KeyguardStatusView + as KeyguardStatusView, + context.display ) val controller = statusViewComponent.keyguardStatusViewController controller.init() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7678f4d643ca..39742a02000b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -171,6 +171,8 @@ import com.android.systemui.util.time.SystemClock; import com.android.systemui.wallpapers.data.repository.WallpaperRepository; import com.android.wm.shell.keyguard.KeyguardTransitions; +import dagger.Lazy; + import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -180,7 +182,8 @@ import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; -import dagger.Lazy; + + import kotlinx.coroutines.CoroutineDispatcher; /** @@ -1933,11 +1936,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void onDreamingStarted() { mUpdateMonitor.dispatchDreamingStarted(); synchronized (this) { - final boolean alwaysShowKeyguard = - mFeatureFlags.isEnabled(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING); - if (mDeviceInteractive - && (alwaysShowKeyguard || - mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()))) { + if (mDeviceInteractive) { doKeyguardLaterLocked(); } } 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 03c166c05a62..081edd152538 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -28,6 +28,7 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.dagger.KeyguardDisplayModule; import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; @@ -38,6 +39,7 @@ 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.CommunalRepositoryModule; +import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule; import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -95,11 +97,13 @@ import kotlinx.coroutines.CoroutineDispatcher; KeyguardUserSwitcherComponent.class}, includes = { CommunalRepositoryModule.class, + CommunalTutorialRepositoryModule.class, CommunalWidgetRepositoryModule.class, FalsingModule.class, KeyguardDataQuickAffordanceModule.class, KeyguardRepositoryModule.class, KeyguardFaceAuthModule.class, + KeyguardDisplayModule.class, StartKeyguardTransitionModule.class, ResourceTrimmerModule.class, }) 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 3ccf446fef59..d06f31fed8db 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 @@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds @SysUISingleton class FromAlternateBouncerTransitionInteractor @@ -129,11 +130,11 @@ constructor( override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { return ValueAnimator().apply { interpolator = Interpolators.LINEAR - duration = TRANSITION_DURATION_MS + duration = TRANSITION_DURATION_MS.inWholeMilliseconds } } companion object { - private const val TRANSITION_DURATION_MS = 300L + val TRANSITION_DURATION_MS = 300.milliseconds } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt index e57c919a5b3e..89aca7631934 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt @@ -61,6 +61,7 @@ interface KeyguardFaceAuthInteractor { fun onSwipeUpOnBouncer() fun onPrimaryBouncerUserInput() fun onAccessibilityAction() + fun onWalletLaunched() } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt index d69876292024..6d084563cbb3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt @@ -21,7 +21,7 @@ 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.keyevent.domain.interactor.SysUIKeyEventHandler.Companion.handleAction import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.domain.interactor.PowerInteractor @@ -29,15 +29,16 @@ import com.android.systemui.shade.ShadeController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi /** Handles key events arriving when the keyguard is showing or device is dozing. */ +@ExperimentalCoroutinesApi @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, @@ -56,7 +57,11 @@ constructor( if (event.handleAction()) { when (event.keyCode) { KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent() - KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent() + KeyEvent.KEYCODE_SPACE, + KeyEvent.KEYCODE_ENTER -> + if (isDeviceAwake()) { + return collapseShadeLockedOrShowPrimaryBouncer() + } } } return false @@ -92,16 +97,24 @@ constructor( (statusBarStateController.state != StatusBarState.SHADE) && statusBarKeyguardViewManager.shouldDismissOnMenuPressed() if (shouldUnlockOnMenuPressed) { - shadeController.animateCollapseShadeForced() - return true + return collapseShadeLockedOrShowPrimaryBouncer() } return false } - private fun dispatchSpaceEvent(): Boolean { - if (isDeviceAwake() && statusBarStateController.state != StatusBarState.SHADE) { - shadeController.animateCollapseShadeForced() - return true + private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean { + when (statusBarStateController.state) { + StatusBarState.SHADE -> return false + StatusBarState.SHADE_LOCKED -> { + shadeController.animateCollapseShadeForced() + return true + } + StatusBarState.KEYGUARD -> { + if (!statusBarKeyguardViewManager.primaryBouncerIsShowing()) { + statusBarKeyguardViewManager.showPrimaryBouncer(true) + return true + } + } } return false } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 1c43609aa551..fbe26de4e9ba 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -34,16 +34,17 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.stateIn -import javax.inject.Inject /** Encapsulates business-logic related to the keyguard transitions. */ @SysUISingleton @@ -176,18 +177,17 @@ constructor( .map { step -> step.to } .stateIn(scope, SharingStarted.Eagerly, LOCKSCREEN) - /** * Whether we're currently in a transition to a new [KeyguardState] and haven't yet completed * it. */ val isInTransitionToAnyState = - combine( - startedKeyguardTransitionStep, - finishedKeyguardState, - ) { startedStep, finishedState -> - startedStep.to != finishedState - } + combine( + startedKeyguardTransitionStep, + finishedKeyguardState, + ) { startedStep, finishedState -> + startedStep.to != finishedState + } /** * The amount of transition into or out of the given [KeyguardState]. @@ -236,14 +236,9 @@ constructor( /** Whether we're in a transition to the given [KeyguardState], but haven't yet completed it. */ fun isInTransitionToState( - state: KeyguardState, + state: KeyguardState, ): Flow<Boolean> { - return combine( - startedKeyguardTransitionStep, - finishedKeyguardState, - ) { startedStep, finishedState -> - startedStep.to == state && finishedState != state - } + return isInTransitionToStateWhere { it == state } } /** @@ -251,28 +246,18 @@ constructor( * haven't yet completed it. */ fun isInTransitionToStateWhere( - stateMatcher: (KeyguardState) -> Boolean, + stateMatcher: (KeyguardState) -> Boolean, ): Flow<Boolean> { - return combine( - startedKeyguardTransitionStep, - finishedKeyguardState, - ) { startedStep, finishedState -> - stateMatcher(startedStep.to) && finishedState != startedStep.from - } + return isInTransitionWhere(fromStatePredicate = { true }, toStatePredicate = stateMatcher) } /** * Whether we're in a transition out of the given [KeyguardState], but haven't yet completed it. */ fun isInTransitionFromState( - state: KeyguardState, + state: KeyguardState, ): Flow<Boolean> { - return combine( - startedKeyguardTransitionStep, - finishedKeyguardState, - ) { startedStep, finishedState -> - startedStep.from == state && finishedState != state - } + return isInTransitionFromStateWhere { it == state } } /** @@ -280,14 +265,9 @@ constructor( * haven't yet completed it. */ fun isInTransitionFromStateWhere( - stateMatcher: (KeyguardState) -> Boolean, + stateMatcher: (KeyguardState) -> Boolean, ): Flow<Boolean> { - return combine( - startedKeyguardTransitionStep, - finishedKeyguardState, - ) { startedStep, finishedState -> - stateMatcher(startedStep.from) && finishedState != startedStep.from - } + return isInTransitionWhere(fromStatePredicate = stateMatcher, toStatePredicate = { true }) } /** @@ -299,26 +279,23 @@ constructor( toStatePredicate: (KeyguardState) -> Boolean, ): Flow<Boolean> { return combine( - startedKeyguardTransitionStep, - finishedKeyguardState, - ) { startedStep, finishedState -> - fromStatePredicate(startedStep.from) - && toStatePredicate(startedStep.to) - && finishedState != startedStep.from - } + startedKeyguardTransitionStep, + finishedKeyguardState, + ) { startedStep, finishedState -> + fromStatePredicate(startedStep.from) && + toStatePredicate(startedStep.to) && + finishedState != startedStep.to + } + .distinctUntilChanged() } - /** - * Whether we've FINISHED a transition to a state that matches the given predicate. - */ + /** Whether we've FINISHED a transition to a state that matches the given predicate. */ fun isFinishedInStateWhere(stateMatcher: (KeyguardState) -> Boolean): Flow<Boolean> { - return finishedKeyguardState.map { stateMatcher(it) } + return finishedKeyguardState.map { stateMatcher(it) }.distinctUntilChanged() } - /** - * Whether we've FINISHED a transition to a state that matches the given predicate. - */ - fun isFinishedInState(state: KeyguardState) : Flow<Boolean> { - return finishedKeyguardState.map { it == state } + /** Whether we've FINISHED a transition to a state that matches the given predicate. */ + fun isFinishedInState(state: KeyguardState): Flow<Boolean> { + return finishedKeyguardState.map { it == state }.distinctUntilChanged() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt index 596a1c01ca42..f38bb2b519e7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt @@ -61,4 +61,5 @@ class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInt override fun onSwipeUpOnBouncer() {} override fun onPrimaryBouncerUserInput() {} override fun onAccessibilityAction() {} + override fun onWalletLaunched() = Unit } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt index ef1d5ac2a6d4..797dec2c9625 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.CoreStartable import com.android.systemui.biometrics.data.repository.FacePropertyRepository import com.android.systemui.biometrics.shared.model.LockoutMode +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton @@ -33,7 +34,6 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.TransitionState @@ -44,6 +44,7 @@ import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -56,7 +57,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.yield -import javax.inject.Inject /** * Encapsulates business logic related face authentication being triggered for device entry from @@ -79,7 +79,6 @@ constructor( private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, private val userRepository: UserRepository, private val facePropertyRepository: FacePropertyRepository, - private val keyguardRepository: KeyguardRepository, private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig, private val powerInteractor: PowerInteractor, ) : CoreStartable, KeyguardFaceAuthInteractor { @@ -207,6 +206,12 @@ constructor( runFaceAuth(FaceAuthUiEvent.FACE_AUTH_ACCESSIBILITY_ACTION, false) } + override fun onWalletLaunched() { + if (facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG) { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true) + } + } + override fun registerListener(listener: FaceAuthenticationListener) { listeners.add(listener) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt new file mode 100644 index 000000000000..41af9e810fbf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt @@ -0,0 +1,115 @@ +/* + * 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.ui.binder + +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.scrim.ScrimView +import com.android.systemui.shade.NotificationShadeWindowView +import com.android.systemui.statusbar.NotificationShadeWindowController +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import javax.inject.Inject + +@ExperimentalCoroutinesApi +@SysUISingleton +class AlternateBouncerBinder +@Inject +constructor( + private val notificationShadeWindowView: NotificationShadeWindowView, + private val featureFlags: FeatureFlagsClassic, + private val alternateBouncerViewModel: AlternateBouncerViewModel, + @Application private val scope: CoroutineScope, + private val notificationShadeWindowController: NotificationShadeWindowController, +) : CoreStartable { + override fun start() { + if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + return + } + + AlternateBouncerViewBinder.bind( + notificationShadeWindowView.requireViewById(R.id.alternate_bouncer), + alternateBouncerViewModel, + scope, + notificationShadeWindowController, + ) + } +} + +/** + * Binds the alternate bouncer view to its view-model. + * + * To use this properly, users should maintain a one-to-one relationship between the [View] and the + * view-binding, binding each view only once. It is okay and expected for the same instance of the + * view-model to be reused for multiple view/view-binder bindings. + */ +@ExperimentalCoroutinesApi +object AlternateBouncerViewBinder { + + /** Binds the view to the view-model, continuing to update the former based on the latter. */ + @JvmStatic + fun bind( + view: ViewGroup, + viewModel: AlternateBouncerViewModel, + scope: CoroutineScope, + notificationShadeWindowController: NotificationShadeWindowController, + ) { + scope.launch { + // forcePluginOpen is necessary to show over occluded apps. + // This cannot be tied to the view's lifecycle because setting this allows the view + // to be started in the first place. + viewModel.forcePluginOpen.collect { + notificationShadeWindowController.setForcePluginOpen(it, this) + } + } + + val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView + view.repeatWhenAttached { alternateBouncerViewContainer -> + repeatOnLifecycle(Lifecycle.State.STARTED) { + scrim.viewAlpha = 0f + + launch { + viewModel.onClickListener.collect { + // TODO (b/287599719): Support swiping to dismiss altBouncer + alternateBouncerViewContainer.setOnClickListener(it) + } + } + + launch { + viewModel.scrimAlpha.collect { + alternateBouncerViewContainer.visibility = + if (it < .1f) View.INVISIBLE else View.VISIBLE + scrim.viewAlpha = it + } + } + + launch { viewModel.scrimColor.collect { scrim.tint = it } } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index abc30efec716..c5a8375f5576 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -47,6 +47,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.util.doOnEnd import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -402,6 +403,9 @@ object KeyguardBottomAreaViewBinder { KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds shakeAnimator.interpolator = CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles) + shakeAnimator.doOnEnd { + view.translationX = 0f + } shakeAnimator.start() vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index aa76702dc3d4..99025acef70d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt @@ -41,6 +41,7 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewMod import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.util.doOnEnd import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -134,7 +135,14 @@ object KeyguardQuickAffordanceViewBinder { vibratorHelper: VibratorHelper?, ) { if (!viewModel.isVisible) { - view.isInvisible = true + view.alpha = 1f + view + .animate() + .alpha(0f) + .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) + .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS) + .withEndAction { view.isInvisible = true } + .start() return } @@ -142,11 +150,9 @@ object KeyguardQuickAffordanceViewBinder { view.isVisible = true if (viewModel.animateReveal) { view.alpha = 0f - view.translationY = view.height / 2f view .animate() .alpha(1f) - .translationY(0f) .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS) .start() @@ -235,6 +241,9 @@ object KeyguardQuickAffordanceViewBinder { KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds shakeAnimator.interpolator = CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles) + shakeAnimator.doOnEnd { + view.translationX = 0f + } shakeAnimator.start() vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt index a0e0da452036..475d26f1db54 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt @@ -23,7 +23,6 @@ import android.widget.FrameLayout import androidx.asynclayoutinflater.view.AsyncLayoutInflater import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.res.R import com.android.systemui.biometrics.UdfpsKeyguardView import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel @@ -32,6 +31,7 @@ import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModel import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -52,8 +52,6 @@ object UdfpsKeyguardViewBinder { fingerprintViewModel: FingerprintViewModel, backgroundViewModel: BackgroundViewModel, ) { - view.useExpandedOverlay(viewModel.useExpandedOverlay()) - val layoutInflaterFinishListener = AsyncLayoutInflater.OnInflateFinishedListener { inflatedInternalView, _, parent -> UdfpsKeyguardInternalViewBinder.bind( @@ -63,25 +61,21 @@ object UdfpsKeyguardViewBinder { fingerprintViewModel, backgroundViewModel, ) - if (viewModel.useExpandedOverlay()) { - val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams - lp.width = viewModel.sensorBounds.width() - lp.height = viewModel.sensorBounds.height() - val relativeToView = - getBoundsRelativeToView( - inflatedInternalView, - RectF(viewModel.sensorBounds), - ) - lp.setMarginsRelative( - relativeToView.left.toInt(), - relativeToView.top.toInt(), - relativeToView.right.toInt(), - relativeToView.bottom.toInt(), + val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams + lp.width = viewModel.sensorBounds.width() + lp.height = viewModel.sensorBounds.height() + val relativeToView = + getBoundsRelativeToView( + inflatedInternalView, + RectF(viewModel.sensorBounds), ) - parent!!.addView(inflatedInternalView, lp) - } else { - parent!!.addView(inflatedInternalView) - } + lp.setMarginsRelative( + relativeToView.left.toInt(), + relativeToView.top.toInt(), + relativeToView.right.toInt(), + relativeToView.bottom.toInt(), + ) + parent!!.addView(inflatedInternalView, lp) } val inflater = AsyncLayoutInflater(view.context) inflater.inflate(R.layout.udfps_keyguard_view_internal, view, layoutInflaterFinishListener) 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 231eeb59737c..c6d8ec7789f2 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 @@ -22,17 +22,19 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.content.res.Resources import android.graphics.Rect import android.hardware.display.DisplayManager import android.os.Bundle import android.os.Handler import android.os.IBinder +import android.view.Display +import android.view.Display.DEFAULT_DISPLAY import android.view.LayoutInflater import android.view.SurfaceControlViewHost import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD import android.widget.FrameLayout import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isInvisible @@ -126,6 +128,8 @@ constructor( private val shouldHideClock: Boolean = bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false) private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS) + private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY) + private val display: Display = displayManager.getDisplay(displayId) private var host: SurfaceControlViewHost @@ -165,7 +169,7 @@ constructor( host = SurfaceControlViewHost( context, - displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)), + displayManager.getDisplay(DEFAULT_DISPLAY), hostToken, "KeyguardPreviewRenderer" ) @@ -175,21 +179,27 @@ constructor( fun render() { mainHandler.post { - val rootView = FrameLayout(context) + val previewContext = context.createDisplayContext(display) - setupKeyguardRootView(rootView) + val rootView = FrameLayout(previewContext) + + setupKeyguardRootView(previewContext, rootView) if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { setUpBottomArea(rootView) } + val windowContext = context.createWindowContext(display, TYPE_KEYGUARD, null) + val windowManagerOfDisplay = windowContext.getSystemService(WindowManager::class.java) rootView.measure( View.MeasureSpec.makeMeasureSpec( - windowManager.currentWindowMetrics.bounds.width(), + windowManagerOfDisplay?.currentWindowMetrics?.bounds?.width() + ?: windowManager.currentWindowMetrics.bounds.width(), View.MeasureSpec.EXACTLY ), View.MeasureSpec.makeMeasureSpec( - windowManager.currentWindowMetrics.bounds.height(), + windowManagerOfDisplay?.currentWindowMetrics?.bounds?.height() + ?: windowManager.currentWindowMetrics.bounds.height(), View.MeasureSpec.EXACTLY ), ) @@ -252,7 +262,7 @@ constructor( * * The end padding is as follows: Below clock padding end */ - private fun setUpSmartspace(parentView: ViewGroup) { + private fun setUpSmartspace(previewContext: Context, parentView: ViewGroup) { if ( !lockscreenSmartspaceController.isEnabled() || !lockscreenSmartspaceController.isDateWeatherDecoupled() @@ -264,12 +274,12 @@ constructor( val topPadding: Int = KeyguardPreviewSmartspaceViewModel.getLargeClockSmartspaceTopPadding( - context.resources, + previewContext.resources, ) val startPadding: Int = - context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) + previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) val endPadding: Int = - context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) + previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) smartSpaceView?.let { it.setPaddingRelative(startPadding, topPadding, endPadding, 0) @@ -309,8 +319,8 @@ constructor( } @OptIn(ExperimentalCoroutinesApi::class) - private fun setupKeyguardRootView(rootView: FrameLayout) { - val keyguardRootView = KeyguardRootView(context, null).apply { removeAllViews() } + private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) { + val keyguardRootView = KeyguardRootView(previewContext, null).apply { removeAllViews() } disposables.add( KeyguardRootViewBinder.bind( keyguardRootView, @@ -334,10 +344,10 @@ constructor( if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { setupShortcuts(keyguardRootView) } - setUpUdfps(rootView) + setUpUdfps(previewContext, rootView) if (!shouldHideClock) { - setUpClock(rootView) + setUpClock(previewContext, rootView) KeyguardPreviewClockViewBinder.bind( largeClockHostView, smallClockHostView, @@ -345,7 +355,7 @@ constructor( ) } - setUpSmartspace(rootView) + setUpSmartspace(previewContext, rootView) smartSpaceView?.let { KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel) } @@ -382,7 +392,7 @@ constructor( } } - private fun setUpUdfps(parentView: ViewGroup) { + private fun setUpUdfps(previewContext: Context, parentView: ViewGroup) { val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds // If sensorBounds are default rect, then there is no UDFPS @@ -400,7 +410,7 @@ constructor( sensorBounds.bottom ) val finger = - LayoutInflater.from(context) + LayoutInflater.from(previewContext) .inflate( R.layout.udfps_keyguard_preview, parentView, @@ -409,17 +419,54 @@ constructor( parentView.addView(finger, fingerprintLayoutParams) } - private fun setUpClock(parentView: ViewGroup) { + private fun setUpClock(previewContext: Context, parentView: ViewGroup) { largeClockHostView = - if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) + if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view_large) - else createLargeClockHostView() + } else { + val hostView = FrameLayout(previewContext) + hostView.layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + parentView.addView(hostView) + hostView + } largeClockHostView.isInvisible = true smallClockHostView = - if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) + if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view) - else createSmallClockHostView(parentView.resources) + } else { + val resources = parentView.resources + val hostView = FrameLayout(previewContext) + val layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.small_clock_height + ) + ) + layoutParams.topMargin = + KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) + + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.small_clock_padding_top + ) + hostView.layoutParams = layoutParams + + hostView.setPaddingRelative( + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.clock_padding_start + ), + 0, + 0, + 0 + ) + hostView.clipChildren = false + parentView.addView(hostView) + hostView + } smallClockHostView.isInvisible = true // TODO (b/283465254): Move the listeners to KeyguardClockRepository @@ -476,44 +523,6 @@ constructor( onClockChanged() } - private fun createLargeClockHostView(): FrameLayout { - val hostView = FrameLayout(context) - hostView.layoutParams = - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT, - ) - return hostView - } - - private fun createSmallClockHostView(resources: Resources): FrameLayout { - val hostView = FrameLayout(context) - val layoutParams = - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_height - ) - ) - layoutParams.topMargin = - KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) + - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_padding_top - ) - hostView.layoutParams = layoutParams - - hostView.setPaddingRelative( - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.clock_padding_start - ), - 0, - 0, - 0 - ) - hostView.clipChildren = false - return hostView - } - private fun onClockChanged() { val clock = clockRegistry.createCurrentClock() clockController.clock = clock diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt index e8df1a6fdaab..d8e43966990e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints +import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection @@ -53,6 +54,7 @@ constructor( splitShadeGuidelines: SplitShadeGuidelines, aodNotificationIconsSection: AodNotificationIconsSection, aodBurnInSection: AodBurnInSection, + communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, ) : KeyguardBlueprint { override val id: String = DEFAULT @@ -69,6 +71,7 @@ constructor( splitShadeGuidelines, aodNotificationIconsSection, aodBurnInSection, + communalTutorialIndicatorSection, ) companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt index 1a9f021a7622..0b0f21d7a281 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt @@ -31,12 +31,12 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.keyguard.KeyguardStatusView import com.android.keyguard.dagger.KeyguardStatusViewComponent -import com.android.systemui.res.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.media.controls.ui.KeyguardMediaController +import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.statusbar.policy.SplitShadeStateController @@ -84,7 +84,8 @@ constructor( override fun bindData(constraintLayout: ConstraintLayout) { if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let { - val statusViewComponent = keyguardStatusViewComponentFactory.build(it) + val statusViewComponent = + keyguardStatusViewComponentFactory.build(it, context.display) val controller = statusViewComponent.keyguardStatusViewController controller.init() keyguardMediaController.attachSplitShadeContainer( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt new file mode 100644 index 000000000000..235a28d4ebc5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt @@ -0,0 +1,103 @@ +/* + * 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.ui.viewmodel + +import android.graphics.Color +import android.view.View +import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.wm.shell.animation.Interpolators +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +@ExperimentalCoroutinesApi +class AlternateBouncerViewModel +@Inject +constructor( + statusBarKeyguardViewManager: StatusBarKeyguardViewManager, + transitionInteractor: KeyguardTransitionInteractor, + falsingManager: FalsingManager, +) { + // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: + private val alternateBouncerScrimAlpha = .66f + private val toAlternateBouncerTransition = + KeyguardTransitionAnimationFlow( + transitionDuration = TRANSITION_DURATION_MS, + transitionFlow = transitionInteractor.anyStateToAlternateBouncerTransition, + ) + .createFlow( + duration = TRANSITION_DURATION_MS, + onStep = { it }, + onFinish = { 1f }, + // Reset on cancel + onCancel = { 0f }, + interpolator = Interpolators.FAST_OUT_SLOW_IN, + ) + private val fromAlternateBouncerTransition = + KeyguardTransitionAnimationFlow( + transitionDuration = TRANSITION_DURATION_MS, + transitionFlow = transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER), + ) + .createFlow( + duration = TRANSITION_DURATION_MS, + onStep = { 1f - it }, + // Reset on cancel + onCancel = { 0f }, + interpolator = Interpolators.FAST_OUT_SLOW_IN, + ) + + /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */ + private val transitionToAlternateBouncerProgress = + merge(fromAlternateBouncerTransition, toAlternateBouncerTransition) + + val forcePluginOpen: Flow<Boolean> = + transitionToAlternateBouncerProgress.map { it > 0f }.distinctUntilChanged() + + /** An observable for the scrim alpha. */ + val scrimAlpha = transitionToAlternateBouncerProgress.map { it * alternateBouncerScrimAlpha } + + /** An observable for the scrim color. Change color for easier debugging. */ + val scrimColor: Flow<Int> = flowOf(Color.BLACK) + + private val clickListener = + View.OnClickListener { + if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true) + } + } + + val onClickListener: Flow<View.OnClickListener?> = + transitionToAlternateBouncerProgress + .map { + if (it == 1f) { + clickListener + } else { + null + } + } + .distinctUntilChanged() +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt index 929f27f4fea3..dca151db8b58 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt @@ -17,20 +17,10 @@ package com.android.systemui.keyguard.ui.viewmodel import android.graphics.Rect -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi -class UdfpsKeyguardViewModel -@Inject -constructor( - private val featureFlags: FeatureFlags, -) { +class UdfpsKeyguardViewModel @Inject constructor() { var sensorBounds: Rect = Rect() - - fun useExpandedOverlay(): Boolean { - return featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) - } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt index 54abc5bc695f..0b1079f69d6e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt @@ -19,13 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context import androidx.annotation.ColorInt import com.android.settingslib.Utils.getColorAttrDefaultColor -import com.android.systemui.res.R import com.android.systemui.keyguard.domain.interactor.BurnInOffsets import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.res.R import com.android.wm.shell.animation.Interpolators import javax.inject.Inject import kotlin.math.roundToInt @@ -62,7 +62,7 @@ open class UdfpsLockscreenViewModel( private val toAlternateBouncer: Flow<TransitionViewModel> = keyguardInteractor.statusBarState.flatMapLatest { statusBarState -> - transitionInteractor.anyStateToAlternateBouncerTransition.map { + transitionInteractor.transitionStepsToState(KeyguardState.ALTERNATE_BOUNCER).map { TransitionViewModel( alpha = 1f, scale = diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index 3d4fca1b8945..1b3b47350197 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -51,11 +51,12 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener Uri uri; boolean looping; AudioAttributes attributes; + float volume; long requestTime; public String toString() { return "{ code=" + code + " looping=" + looping + " attributes=" + attributes - + " uri=" + uri + " }"; + + " volume=" + volume + " uri=" + uri + " }"; } } @@ -101,6 +102,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener player.setAudioAttributes(mCmd.attributes); player.setDataSource(mCmd.context, mCmd.uri); player.setLooping(mCmd.looping); + player.setVolume(mCmd.volume); player.setOnCompletionListener(NotificationPlayer.this); player.setOnErrorListener(NotificationPlayer.this); player.prepare(); @@ -401,10 +403,11 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener * (see {@link MediaPlayer#setLooping(boolean)}) * @param stream the AudioStream to use. * (see {@link MediaPlayer#setAudioStreamType(int)}) + * @param volume the volume for the audio with values in range [0.0, 1.0] * @deprecated use {@link #play(Context, Uri, boolean, AudioAttributes)} instead. */ @Deprecated - public void play(Context context, Uri uri, boolean looping, int stream) { + public void play(Context context, Uri uri, boolean looping, int stream, float volume) { if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); } PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play"); Command cmd = new Command(); @@ -414,6 +417,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener cmd.uri = uri; cmd.looping = looping; cmd.attributes = new AudioAttributes.Builder().setInternalLegacyStreamType(stream).build(); + cmd.volume = volume; synchronized (mCmdQueue) { enqueueLocked(cmd); mState = PLAY; @@ -432,8 +436,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener * (see {@link MediaPlayer#setLooping(boolean)}) * @param attributes the AudioAttributes to use. * (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)}) + * @param volume the volume for the audio with values in range [0.0, 1.0] */ - public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) { + public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes, + float volume) { if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); } Command cmd = new Command(); cmd.requestTime = SystemClock.uptimeMillis(); @@ -442,6 +448,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener cmd.uri = uri; cmd.looping = looping; cmd.attributes = attributes; + cmd.volume = volume; synchronized (mCmdQueue) { enqueueLocked(cmd); mState = PLAY; diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java index 68202d5629a0..7a488365c740 100644 --- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java @@ -38,6 +38,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.VibrationEffect; +import android.os.vibrator.Flags; import android.provider.MediaStore; import android.util.Log; @@ -111,7 +112,7 @@ public class RingtonePlayer implements CoreStartable { @Override public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping) throws RemoteException { - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { playRemoteRingtone(token, uri, aa, true, Ringtone.MEDIA_SOUND, null, volume, looping, /* hapticGenerator= */ false, null); @@ -284,7 +285,8 @@ public class RingtonePlayer implements CoreStartable { } @Override - public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) { + public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa, + float volume) { if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")"); if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Async playback only available from system UID."); @@ -292,7 +294,7 @@ public class RingtonePlayer implements CoreStartable { if (UserHandle.ALL.equals(user)) { user = UserHandle.SYSTEM; } - mAsyncPlayer.play(getContextForUser(user), uri, looping, aa); + mAsyncPlayer.play(getContextForUser(user), uri, looping, aa, volume); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt index 0f3e0acd007d..4e43ccffb6a7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt @@ -60,7 +60,6 @@ import com.android.internal.annotations.Keep import com.android.internal.logging.InstanceId import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.Dumpable -import com.android.systemui.res.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -83,6 +82,7 @@ import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.res.R import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.statusbar.notification.row.HybridGroupManager @@ -1429,8 +1429,6 @@ class MediaDataManager( } private fun onSessionDestroyed(key: String) { - if (!mediaFlags.isRetainingPlayersEnabled()) return - if (DEBUG) Log.d(TAG, "session destroyed for $key") val entry = mediaEntries.remove(key) ?: return // Clear token since the session is no longer valid @@ -1474,7 +1472,7 @@ class MediaDataManager( if (DEBUG) Log.d(TAG, "Removing still-active player $key") notifyMediaDataRemoved(key) logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) - } else { + } else if (mediaFlags.isRetainingPlayersEnabled() || isAbleToResume(removed)) { // Convert to resume if (DEBUG) { Log.d( @@ -1484,6 +1482,11 @@ class MediaDataManager( ) } convertToResumePlayer(key, removed) + } else { + // Retaining players flag is off and app doesn't support resume: remove player. + if (DEBUG) Log.d(TAG, "Removing player $key") + notifyMediaDataRemoved(key) + logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt index 72aea040ba05..2217509167ef 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt @@ -33,6 +33,8 @@ import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnail import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider +import com.android.systemui.mediaprojection.appselector.view.WindowMetricsProvider +import com.android.systemui.mediaprojection.appselector.view.WindowMetricsProviderImpl import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity @@ -106,6 +108,8 @@ interface MediaProjectionAppSelectorModule { impl: TaskPreviewSizeProvider ): DefaultLifecycleObserver + @Binds fun windowMetricsProvider(impl: WindowMetricsProviderImpl): WindowMetricsProvider + companion object { @Provides @MediaProjectionAppSelector diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt index 864d35af41b4..c829471f53f3 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt @@ -19,8 +19,6 @@ package com.android.systemui.mediaprojection.appselector.view import android.content.Context import android.content.res.Configuration import android.graphics.Rect -import android.view.WindowInsets.Type -import android.view.WindowManager import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope @@ -36,7 +34,7 @@ class TaskPreviewSizeProvider @Inject constructor( private val context: Context, - private val windowManager: WindowManager, + private val windowMetricsProvider: WindowMetricsProvider, private val configurationController: ConfigurationController, ) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver { @@ -62,17 +60,14 @@ constructor( } private fun calculateSize(): Rect { - val windowMetrics = windowManager.maximumWindowMetrics - val maximumWindowHeight = windowMetrics.bounds.height() - val width = windowMetrics.bounds.width() + val maxWindowBounds = windowMetricsProvider.maximumWindowBounds + val maximumWindowHeight = maxWindowBounds.height() + val width = maxWindowBounds.width() var height = maximumWindowHeight val isLargeScreen = isLargeScreen(context) if (isLargeScreen) { - val taskbarSize = - windowManager.currentWindowMetrics.windowInsets - .getInsets(Type.tappableElement()) - .bottom + val taskbarSize = windowMetricsProvider.currentWindowInsets.bottom height -= taskbarSize } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt new file mode 100644 index 000000000000..193292032868 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt @@ -0,0 +1,30 @@ +package com.android.systemui.mediaprojection.appselector.view + +import android.graphics.Insets +import android.graphics.Rect +import android.view.WindowInsets +import android.view.WindowManager +import javax.inject.Inject + +/** Provides values related to window metrics. */ +interface WindowMetricsProvider { + + val maximumWindowBounds: Rect + + val currentWindowInsets: Insets +} + +class WindowMetricsProviderImpl +@Inject +constructor( + private val windowManager: WindowManager, +) : WindowMetricsProvider { + override val maximumWindowBounds: Rect + get() = windowManager.maximumWindowMetrics.bounds + + override val currentWindowInsets: Insets + get() = + windowManager.currentWindowMetrics.windowInsets.getInsets( + WindowInsets.Type.tappableElement() + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index 2b56d0cf9f83..d08d0400f354 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -239,6 +239,8 @@ public class MediaProjectionPermissionActivity extends Activity protected void onDestroy() { super.onDestroy(); if (mDialog != null) { + mDialog.setOnDismissListener(null); + mDialog.setOnCancelListener(null); mDialog.dismiss(); } } diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java index 07846b56d784..3cdcb2c4f550 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java @@ -24,6 +24,8 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shared.system.QuickStepContract; +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -108,6 +110,7 @@ public class SysUiState implements Dumpable { } } + @NeverCompile @Override public void dump(PrintWriter pw, String[] args) { pw.println("SysUiState state:"); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java index 564e984fbce2..2928cceb35aa 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java @@ -65,6 +65,8 @@ import com.android.systemui.util.settings.SecureSettings; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.pip.Pip; +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.util.Optional; @@ -476,6 +478,7 @@ public class NavigationBarControllerImpl implements return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId()); } + @NeverCompile @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mIsLargeScreen=" + mIsLargeScreen); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 4d6d95a05b1b..bc4f7f2513ce 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -584,7 +584,6 @@ public class NavigationBarView extends FrameLayout { if (!visible) { mTransitionListener.onBackAltCleared(); } - mRotationButtonController.getRotationButton().setCanShowRotationButton(!visible); } void setDisabledFlags(int disabledFlags, SysUiState sysUiState) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java index 8589ae9305fd..68bf88b16ae2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java @@ -72,7 +72,7 @@ public class QSFragmentLegacy extends LifecycleFragment implements QS { @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this); + QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(getView()); mQsImpl = mQsImplProvider.get(); mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt index 2a36fdb21e18..65a2c8ca692b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt @@ -105,7 +105,7 @@ constructor( override fun removeCallback(callback: QSHost.Callback) { if (useNewHost) { - synchronized(callbacksMap) { callbacksMap.get(callback)?.cancel() } + synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() } } else { qsTileHost.removeCallback(callback) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java index a32a024dbb44..4aad6a069cbb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java @@ -72,6 +72,8 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.util.Utils; +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.util.Arrays; import java.util.function.Consumer; @@ -199,11 +201,13 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner(); } + /** + * This method will set up all the necessary fields. Methods from the implemented interfaces + * should not be called before this method returns. + */ public void onComponentCreated(QSComponent qsComponent, @Nullable Bundle savedInstanceState) { mRootView = qsComponent.getRootView(); - mCommandQueue.addCallback(this); - mQSPanelController = qsComponent.getQSPanelController(); mQuickQSPanelController = qsComponent.getQuickQSPanelController(); @@ -270,6 +274,9 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl mQSPanelController.getMediaHost().getHostView().setAlpha(1.0f); mQSAnimator.requestAnimatorUpdate(); }); + + // This will immediately call disable, so it needs to be added after setting up the fields. + mCommandQueue.addCallback(this); } private void bindFooterActionsView(View root) { @@ -323,6 +330,8 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl public void onDestroy() { mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); + mQSPanelController.destroy(); + mQuickQSPanelController.destroy(); if (mListening) { setListening(false); } @@ -927,6 +936,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl return mListeningAndVisibilityLifecycleOwner; } + @NeverCompile @Override public void dump(PrintWriter pw, String[] args) { IndentingPrintWriter indentingPw = new IndentingPrintWriter(pw, /* singleIndent= */ " "); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 60c92c000760..fc2f5f9c7226 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -158,6 +158,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr protected void onInit() { mView.initialize(mQSLogger); mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), ""); + mHost.addCallback(mQSHostCallback); } /** @@ -172,6 +173,18 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } @Override + public void destroy() { + super.destroy(); + mHost.removeCallback(mQSHostCallback); + + for (TileRecord record : mRecords) { + record.tile.removeCallback(record.callback); + mView.removeTile(record); + } + mRecords.clear(); + } + + @Override protected void onViewAttached() { mQsTileRevealController = createTileRevealController(); if (mQsTileRevealController != null) { @@ -180,7 +193,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener); mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener); - mHost.addCallback(mQSHostCallback); setTiles(); mLastOrientation = getResources().getConfiguration().orientation; mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag()); @@ -193,16 +205,11 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr protected void onViewDetached() { mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag()); mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); - mHost.removeCallback(mQSHostCallback); mView.getTileLayout().setListening(false, mUiEventLogger); mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener); - for (TileRecord record : mRecords) { - record.tile.removeCallback(record.callback); - } - mRecords.clear(); mDumpManager.unregisterDumpable(mView.getDumpableTag()); } @@ -222,15 +229,30 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr if (!collapsedView && mQsTileRevealController != null) { mQsTileRevealController.updateRevealedTiles(tiles); } - - for (QSPanelControllerBase.TileRecord record : mRecords) { - mView.removeTile(record); - record.tile.removeCallback(record.callback); + boolean shouldChange = false; + if (tiles.size() == mRecords.size()) { + int i = 0; + for (QSTile tile : tiles) { + if (tile != mRecords.get(i).tile) { + shouldChange = true; + break; + } + i++; + } + } else { + shouldChange = true; } - mRecords.clear(); - mCachedSpecs = ""; - for (QSTile tile : tiles) { - addTile(tile, collapsedView); + + if (shouldChange) { + for (QSPanelControllerBase.TileRecord record : mRecords) { + mView.removeTile(record); + record.tile.removeCallback(record.callback); + } + mRecords.clear(); + mCachedSpecs = ""; + for (QSTile tile : tiles) { + addTile(tile, collapsedView); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java index 7794fa071f45..eb11568614b7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java @@ -20,15 +20,14 @@ import android.database.ContentObserver; import android.os.Handler; import com.android.systemui.statusbar.policy.Listenable; -import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.settings.SettingsProxy; import com.android.systemui.util.settings.SystemSettings; /** - * Helper for managing secure, global, and system settings through use of {@link SettingsProxy}, - * which is the common superclass of {@link SecureSettings}, {@link GlobalSettings}, and - * {@link SystemSettings}. + * Helper for managing global settings through use of {@link SettingsProxy}. This should + * <em>not</em> be used for {@link SecureSettings} or {@link SystemSettings} since those must be + * user-aware (instead, use {@link UserSettingObserver}). */ public abstract class SettingObserver extends ContentObserver implements Listenable { private final SettingsProxy mSettingsProxy; @@ -36,23 +35,20 @@ public abstract class SettingObserver extends ContentObserver implements Listena private final int mDefaultValue; private boolean mListening; - private int mUserId; private int mObservedValue; protected abstract void handleValueChanged(int value, boolean observedChange); - public SettingObserver(SettingsProxy settingsProxy, Handler handler, String settingName, - int userId) { - this(settingsProxy, handler, settingName, userId, 0); + public SettingObserver(SettingsProxy settingsProxy, Handler handler, String settingName) { + this(settingsProxy, handler, settingName, 0); } public SettingObserver(SettingsProxy settingsProxy, Handler handler, String settingName, - int userId, int defaultValue) { + int defaultValue) { super(handler); mSettingsProxy = settingsProxy; mSettingName = settingName; mObservedValue = mDefaultValue = defaultValue; - mUserId = userId; } public int getValue() { @@ -65,11 +61,11 @@ public abstract class SettingObserver extends ContentObserver implements Listena * @param value The new value for the setting. */ public void setValue(int value) { - mSettingsProxy.putIntForUser(mSettingName, value, mUserId); + mSettingsProxy.putInt(mSettingName, value); } private int getValueFromProvider() { - return mSettingsProxy.getIntForUser(mSettingName, mDefaultValue, mUserId); + return mSettingsProxy.getInt(mSettingName, mDefaultValue); } @Override @@ -78,8 +74,8 @@ public abstract class SettingObserver extends ContentObserver implements Listena mListening = listening; if (listening) { mObservedValue = getValueFromProvider(); - mSettingsProxy.registerContentObserverForUser( - mSettingsProxy.getUriFor(mSettingName), false, this, mUserId); + mSettingsProxy.registerContentObserver( + mSettingsProxy.getUriFor(mSettingName), false, this); } else { mSettingsProxy.unregisterContentObserver(this); mObservedValue = mDefaultValue; @@ -94,21 +90,6 @@ public abstract class SettingObserver extends ContentObserver implements Listena handleValueChanged(value, changed); } - /** - * Set user handle for which to observe the setting. - */ - public void setUserId(int userId) { - mUserId = userId; - if (mListening) { - setListening(false); - setListening(true); - } - } - - public int getCurrentUser() { - return mUserId; - } - public String getKey() { return mSettingName; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt index 11759f7ce06b..777faeacadd8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt @@ -19,7 +19,7 @@ package com.android.systemui.qs import android.content.Context import android.util.AttributeSet import com.android.systemui.flags.Flags -import com.android.systemui.flags.ViewRefactorFlag +import com.android.systemui.flags.RefactorFlag import com.android.systemui.res.R open class SideLabelTileLayout( @@ -27,8 +27,8 @@ open class SideLabelTileLayout( attrs: AttributeSet? ) : TileLayout(context, attrs) { - private final val isSmallLandscapeLockscreenEnabled = - ViewRefactorFlag(flag = Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled + private val isSmallLandscapeLockscreenEnabled = + RefactorFlag.forView(Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled override fun updateResources(): Boolean { return super.updateResources().also { diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 9ba16454aea6..9d4eba5aafc2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -15,13 +15,13 @@ import androidx.annotation.Nullable; import com.android.internal.logging.UiEventLogger; import com.android.systemui.FontSizeUtils; -import com.android.systemui.flags.ViewRefactorFlag; -import com.android.systemui.res.R; import com.android.systemui.flags.Flags; +import com.android.systemui.flags.RefactorFlag; import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.QSPanelControllerBase.TileRecord; import com.android.systemui.qs.tileimpl.HeightOverrideable; import com.android.systemui.qs.tileimpl.QSTileViewImplKt; +import com.android.systemui.res.R; import java.util.ArrayList; @@ -55,7 +55,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { protected int mLastTileBottom; protected TextView mTempTextView; private final Boolean mIsSmallLandscapeLockscreenEnabled = - new ViewRefactorFlag(Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled(); + RefactorFlag.forView(Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled(); public TileLayout(Context context) { this(context, null); diff --git a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java new file mode 100644 index 000000000000..539c2d6e64da --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java @@ -0,0 +1,117 @@ +/* + * 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.qs; + +import android.database.ContentObserver; +import android.os.Handler; + +import com.android.systemui.statusbar.policy.Listenable; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.util.settings.SystemSettings; +import com.android.systemui.util.settings.UserSettingsProxy; + +/** + * Helper for managing secure and system settings through use of {@link UserSettingsProxy}, + * which is the common superclass of {@link SecureSettings} and {@link SystemSettings}. + */ +public abstract class UserSettingObserver extends ContentObserver implements Listenable { + private final UserSettingsProxy mSettingsProxy; + private final String mSettingName; + private final int mDefaultValue; + + private boolean mListening; + private int mUserId; + private int mObservedValue; + + protected abstract void handleValueChanged(int value, boolean observedChange); + + public UserSettingObserver(UserSettingsProxy settingsProxy, Handler handler, String settingName, + int userId) { + this(settingsProxy, handler, settingName, userId, 0); + } + + public UserSettingObserver(UserSettingsProxy settingsProxy, Handler handler, String settingName, + int userId, int defaultValue) { + super(handler); + mSettingsProxy = settingsProxy; + mSettingName = settingName; + mObservedValue = mDefaultValue = defaultValue; + mUserId = userId; + } + + public int getValue() { + return mListening ? mObservedValue : getValueFromProvider(); + } + + /** + * Set the value of the observed setting. + * + * @param value The new value for the setting. + */ + public void setValue(int value) { + mSettingsProxy.putIntForUser(mSettingName, value, mUserId); + } + + private int getValueFromProvider() { + return mSettingsProxy.getIntForUser(mSettingName, mDefaultValue, mUserId); + } + + @Override + public void setListening(boolean listening) { + if (listening == mListening) return; + mListening = listening; + if (listening) { + mObservedValue = getValueFromProvider(); + mSettingsProxy.registerContentObserverForUser( + mSettingsProxy.getUriFor(mSettingName), false, this, mUserId); + } else { + mSettingsProxy.unregisterContentObserver(this); + mObservedValue = mDefaultValue; + } + } + + @Override + public void onChange(boolean selfChange) { + final int value = getValueFromProvider(); + final boolean changed = value != mObservedValue; + mObservedValue = value; + handleValueChanged(value, changed); + } + + /** + * Set user handle for which to observe the setting. + */ + public void setUserId(int userId) { + mUserId = userId; + if (mListening) { + setListening(false); + setListening(true); + } + } + + public int getCurrentUser() { + return mUserId; + } + + public String getKey() { + return mSettingName; + } + + public boolean isListening() { + return mListening; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java index 327e858059f3..ce8db789842a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java @@ -16,6 +16,9 @@ package com.android.systemui.qs.dagger; +import android.view.View; + +import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.qs.QSFragmentLegacy; import dagger.BindsInstance; @@ -31,6 +34,7 @@ public interface QSFragmentComponent extends QSComponent { /** Factory for building a {@link QSFragmentComponent}. */ @Subcomponent.Factory interface Factory { - QSFragmentComponent create(@BindsInstance QSFragmentLegacy qsFragment); + /** */ + QSFragmentComponent create(@BindsInstance @RootView View view); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java index 0c9c24df5e36..0e75b2157a7d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java @@ -20,34 +20,17 @@ import static com.android.systemui.util.Utils.useCollapsedMediaInLandscape; import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.content.Context; -import android.view.View; -import com.android.systemui.dagger.qualifiers.RootView; -import com.android.systemui.plugins.qs.QS; -import com.android.systemui.qs.QSFragmentLegacy; - -import javax.inject.Named; - -import dagger.Binds; import dagger.Module; import dagger.Provides; +import javax.inject.Named; + /** * Dagger Module for {@link QSFragmentComponent}. */ @Module(includes = {QSScopeModule.class}) public interface QSFragmentModule { - - @Provides - @RootView - static View provideRootView(QSFragmentLegacy qsFragment) { - return qsFragment.getView(); - } - - /** */ - @Binds - QS bindQS(QSFragmentLegacy qsFragment); - /** */ @Provides @Named(QSScopeModule.QS_USING_MEDIA_PLAYER) 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 c5512c15ccc2..4bb8c6e4bb2d 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 @@ -272,7 +272,6 @@ constructor( // repository launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } } - Log.d("Fabian", "Finished resolving tiles") } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index fb71cefe8e0a..17251c37473f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -36,7 +36,6 @@ import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.res.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -49,6 +48,7 @@ import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.GlobalSettings; @@ -56,8 +56,6 @@ import dagger.Lazy; import javax.inject.Inject; - - /** Quick settings tile: Airplane mode **/ public class AirplaneModeTile extends QSTileImpl<BooleanState> { @@ -90,8 +88,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> { mBroadcastDispatcher = broadcastDispatcher; mLazyConnectivityManager = lazyConnectivityManager; - mSetting = new SettingObserver(globalSettings, mHandler, Global.AIRPLANE_MODE_ON, - userTracker.getUserId()) { + mSetting = new SettingObserver(globalSettings, mHandler, Global.AIRPLANE_MODE_ON) { @Override protected void handleValueChanged(int value, boolean observedChange) { // mHandler is the background handler so calling this is OK diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 18d472b9c755..426aa553f082 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -29,7 +29,6 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -38,9 +37,10 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.settings.SecureSettings; @@ -53,7 +53,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements private final BatteryController mBatteryController; @VisibleForTesting - protected final SettingObserver mSetting; + protected final UserSettingObserver mSetting; private int mLevel; private boolean mPowerSave; @@ -79,7 +79,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements mBatteryController = batteryController; mBatteryController.observe(getLifecycle(), this); int currentUser = host.getUserContext().getUserId(); - mSetting = new SettingObserver( + mSetting = new UserSettingObserver( secureSettings, mHandler, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java index ee57b2b62d49..c8adbfcf5487 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java @@ -28,8 +28,6 @@ import android.widget.Switch; import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.res.R; -import com.android.systemui.res.R.drawable; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -38,9 +36,10 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; @@ -51,8 +50,8 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> { public static final String TILE_SPEC = "color_correction"; - private final Icon mIcon = ResourceIcon.get(drawable.ic_qs_color_correction); - private final SettingObserver mSetting; + private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_color_correction); + private final UserSettingObserver mSetting; @Inject public ColorCorrectionTile( @@ -71,7 +70,7 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); - mSetting = new SettingObserver(secureSettings, mHandler, + mSetting = new UserSettingObserver(secureSettings, mHandler, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, userTracker.getUserId()) { @Override protected void handleValueChanged(int value, boolean observedChange) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 993ada6b86aa..c34a5842c1e3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -29,8 +29,6 @@ import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.res.R; -import com.android.systemui.res.R.drawable; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -39,9 +37,10 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; @@ -51,7 +50,7 @@ import javax.inject.Inject; public class ColorInversionTile extends QSTileImpl<BooleanState> { public static final String TILE_SPEC = "inversion"; - private final SettingObserver mSetting; + private final UserSettingObserver mSetting; @Inject public ColorInversionTile( @@ -70,7 +69,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); - mSetting = new SettingObserver(secureSettings, mHandler, + mSetting = new UserSettingObserver(secureSettings, mHandler, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, userTracker.getUserId()) { @Override protected void handleValueChanged(int value, boolean observedChange) { @@ -126,8 +125,8 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.label = mContext.getString(R.string.quick_settings_inversion_label); state.icon = ResourceIcon.get(state.value - ? drawable.qs_invert_colors_icon_on - : drawable.qs_invert_colors_icon_off); + ? R.drawable.qs_invert_colors_icon_on + : R.drawable.qs_invert_colors_icon_off); state.expandedAccessibilityClassName = Switch.class.getName(); state.contentDescription = state.label; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 0617b30eb8fa..f6518d1e8023 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -45,7 +45,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.notification.EnableZenModeDialog; import com.android.systemui.Prefs; -import com.android.systemui.res.R; import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; @@ -56,10 +55,11 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger; +import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.settings.SecureSettings; @@ -81,7 +81,7 @@ public class DndTile extends QSTileImpl<BooleanState> { private final ZenModeController mController; private final SharedPreferences mSharedPreferences; - private final SettingObserver mSettingZenDuration; + private final UserSettingObserver mSettingZenDuration; private final DialogLaunchAnimator mDialogLaunchAnimator; private final QSZenModeDialogMetricsLogger mQSZenDialogMetricsLogger; @@ -109,7 +109,7 @@ public class DndTile extends QSTileImpl<BooleanState> { mSharedPreferences = sharedPreferences; mController.observe(getLifecycle(), mZenCallback); mDialogLaunchAnimator = dialogLaunchAnimator; - mSettingZenDuration = new SettingObserver(secureSettings, mUiHandler, + mSettingZenDuration = new UserSettingObserver(secureSettings, mUiHandler, Settings.Secure.ZEN_DURATION, getHost().getUserId()) { @Override protected void handleValueChanged(int value, boolean observedChange) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java index a08b3fca15aa..4f0a63b667f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java @@ -38,7 +38,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.res.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -49,9 +48,10 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; @@ -69,8 +69,8 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> { private final Icon mIconUndocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked); private final IDreamManager mDreamManager; private final BroadcastDispatcher mBroadcastDispatcher; - private final SettingObserver mEnabledSettingObserver; - private final SettingObserver mDreamSettingObserver; + private final UserSettingObserver mEnabledSettingObserver; + private final UserSettingObserver mDreamSettingObserver; private final UserTracker mUserTracker; private final boolean mDreamSupported; private final boolean mDreamOnlyEnabledForDockUser; @@ -111,14 +111,14 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> { statusBarStateController, activityStarter, qsLogger); mDreamManager = dreamManager; mBroadcastDispatcher = broadcastDispatcher; - mEnabledSettingObserver = new SettingObserver(secureSettings, mHandler, + mEnabledSettingObserver = new UserSettingObserver(secureSettings, mHandler, Settings.Secure.SCREENSAVER_ENABLED, userTracker.getUserId()) { @Override protected void handleValueChanged(int value, boolean observedChange) { refreshState(); } }; - mDreamSettingObserver = new SettingObserver(secureSettings, mHandler, + mDreamSettingObserver = new UserSettingObserver(secureSettings, mHandler, Settings.Secure.SCREENSAVER_COMPONENTS, userTracker.getUserId()) { @Override protected void handleValueChanged(int value, boolean observedChange) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java index 78af97668fc0..b08e6a5580a2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java @@ -28,7 +28,6 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -37,9 +36,10 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; import com.android.wm.shell.onehanded.OneHanded; @@ -53,7 +53,7 @@ public class OneHandedModeTile extends QSTileImpl<BooleanState> { private final Icon mIcon = ResourceIcon.get( com.android.internal.R.drawable.ic_qs_one_handed_mode); - private final SettingObserver mSetting; + private final UserSettingObserver mSetting; @Inject public OneHandedModeTile( @@ -70,7 +70,7 @@ public class OneHandedModeTile extends QSTileImpl<BooleanState> { SecureSettings secureSettings) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); - mSetting = new SettingObserver(secureSettings, mHandler, + mSetting = new UserSettingObserver(secureSettings, mHandler, Settings.Secure.ONE_HANDED_MODE_ENABLED, userTracker.getUserId()) { @Override protected void handleValueChanged(int value, boolean observedChange) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 5a9500494de0..f1d8f9f25286 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -36,7 +36,6 @@ import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -45,9 +44,10 @@ import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; @@ -67,7 +67,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements private final RotationLockController mController; private final SensorPrivacyManager mPrivacyManager; private final BatteryController mBatteryController; - private final SettingObserver mSetting; + private final UserSettingObserver mSetting; private final boolean mAllowRotationResolver; @Inject @@ -93,7 +93,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements mPrivacyManager = privacyManager; mBatteryController = batteryController; int currentUser = host.getUserContext().getUserId(); - mSetting = new SettingObserver( + mSetting = new UserSettingObserver( secureSettings, mHandler, Secure.CAMERA_AUTOROTATE, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt index 8ae2dc227998..80af76de23df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt @@ -102,9 +102,10 @@ constructor( showSeeAll: Boolean, showPairNewDevice: Boolean ) { - seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE - pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE - deviceItemAdapter.refreshDeviceItemList(deviceItem) + deviceItemAdapter.refreshDeviceItemList(deviceItem) { + seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE + pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE + } } internal fun onBluetoothStateUpdated(isEnabled: Boolean, subtitleResId: Int) { @@ -173,8 +174,8 @@ constructor( internal fun getItem(position: Int) = asyncListDiffer.currentList[position] - internal fun refreshDeviceItemList(updated: List<DeviceItem>) { - asyncListDiffer.submitList(updated) + internal fun refreshDeviceItemList(updated: List<DeviceItem>, callback: () -> Unit) { + asyncListDiffer.submitList(updated, callback) } internal inner class DeviceItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt index 97e178371f2d..8e274932d4d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt @@ -113,7 +113,6 @@ constructor( .launchIn(this) deviceItemInteractor.deviceItemUpdate - .filterNotNull() .onEach { dialog!!.onDeviceItemUpdated( it.take(MAX_DEVICE_ITEM_ENTRY), diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt index 14d24f952c8d..e196c6c27c8b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt @@ -34,10 +34,10 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.withContext @@ -55,10 +55,10 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, ) { - private val mutableDeviceItemUpdate: MutableStateFlow<List<DeviceItem>?> = - MutableStateFlow(null) + private val mutableDeviceItemUpdate: MutableSharedFlow<List<DeviceItem>> = + MutableSharedFlow(extraBufferCapacity = 1) internal val deviceItemUpdate - get() = mutableDeviceItemUpdate.asStateFlow() + get() = mutableDeviceItemUpdate.asSharedFlow() internal val deviceItemUpdateRequest: SharedFlow<Unit> = conflatedCallbackFlow { @@ -119,16 +119,15 @@ constructor( internal suspend fun updateDeviceItems(context: Context) { withContext(backgroundDispatcher) { - val mostRecentlyConnectedDevices = bluetoothAdapter?.mostRecentlyConnectedDevices - - mutableDeviceItemUpdate.value = + mutableDeviceItemUpdate.tryEmit( bluetoothTileDialogRepository.cachedDevices .mapNotNull { cachedDevice -> deviceItemFactoryList .firstOrNull { it.isFilterMatched(cachedDevice, audioManager) } ?.create(context, cachedDevice) } - .sort(displayPriority, mostRecentlyConnectedDevices) + .sort(displayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices) + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 584456d79890..91b4d1778e1c 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -42,6 +42,7 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICA import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.distinctUntilChanged @@ -51,7 +52,6 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch -import javax.inject.Inject /** * Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI @@ -142,7 +142,7 @@ constructor( // When the device becomes unlocked in Lockscreen, go to Gone if // bypass is enabled. renderedScenes.contains(SceneKey.Lockscreen) -> - if (deviceEntryInteractor.isBypassEnabled()) { + if (deviceEntryInteractor.isBypassEnabled.value) { SceneKey.Gone to "device unlocked in Lockscreen scene with bypass" } else { @@ -179,36 +179,34 @@ constructor( } applicationScope.launch { - powerInteractor.isAsleep - .collect { isAsleep -> - if (isAsleep) { - switchToScene( - targetSceneKey = SceneKey.Lockscreen, - loggingReason = "device is starting to sleep", - ) - } else { - val authMethod = authenticationInteractor.getAuthenticationMethod() - val isUnlocked = deviceEntryInteractor.isUnlocked.value - when { - authMethod == AuthenticationMethodModel.None -> { - switchToScene( - targetSceneKey = SceneKey.Gone, - loggingReason = - "device is starting to wake up while auth method is" + - " none", - ) - } - authMethod.isSecure && isUnlocked -> { - switchToScene( - targetSceneKey = SceneKey.Gone, - loggingReason = - "device is starting to wake up while unlocked with a" + - " secure auth method", - ) - } + powerInteractor.isAsleep.collect { isAsleep -> + if (isAsleep) { + switchToScene( + targetSceneKey = SceneKey.Lockscreen, + loggingReason = "device is starting to sleep", + ) + } else { + val authMethod = authenticationInteractor.getAuthenticationMethod() + val isUnlocked = deviceEntryInteractor.isUnlocked.value + when { + authMethod == AuthenticationMethodModel.None -> { + switchToScene( + targetSceneKey = SceneKey.Gone, + loggingReason = + "device is starting to wake up while auth method is" + " none", + ) + } + authMethod.isSecure && isUnlocked -> { + switchToScene( + targetSceneKey = SceneKey.Gone, + loggingReason = + "device is starting to wake up while unlocked with a" + + " secure auth method", + ) } } } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt index 3927873f8ba8..f704894e56e2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt @@ -42,6 +42,13 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isUserInputDriven: Boolean, + val isInitiatedByUserInput: Boolean, + + /** + * Whether user input is currently driving the transition. For example, if a user is + * dragging a pointer, this emits true. Once they lift their finger, this emits false while + * the transition completes/settles. + */ + val isUserInputOngoing: Flow<Boolean>, ) : ObservableTransitionState() } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt index 4bc93a8f1ca5..2e45353634fe 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt @@ -61,12 +61,17 @@ sealed interface UserAction { data class Swipe( /** The direction of the swipe. */ val direction: Direction, + /** + * The edge from which the swipe originated or `null`, if the swipe didn't start close to an + * edge. + */ + val fromEdge: Edge? = null, /** The number of pointers that were used (for example, one or two fingers). */ val pointerCount: Int = 1, ) : UserAction /** The user has hit the back button or performed the back navigation gesture. */ - object Back : UserAction + data object Back : UserAction } /** Enumerates all known "cardinal" directions for user actions. */ @@ -76,3 +81,11 @@ enum class Direction { RIGHT, DOWN, } + +/** Enumerates all known edges from which a swipe can start. */ +enum class Edge { + LEFT, + TOP, + RIGHT, + BOTTOM, +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index 2f873019349c..393a698bcdb7 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -132,6 +132,7 @@ open class UserTrackerImpl internal constructor( setUserIdInternal(startingUser) val filter = IntentFilter().apply { + addAction(Intent.ACTION_LOCALE_CHANGED) addAction(Intent.ACTION_USER_INFO_CHANGED) // These get called when a managed profile goes in or out of quiet mode. addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) @@ -149,6 +150,7 @@ open class UserTrackerImpl internal constructor( override fun onReceive(context: Context, intent: Intent) { when (intent.action) { + Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_USER_INFO_CHANGED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java index 9235fcc8202f..c42fdf8e7b93 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java @@ -67,8 +67,6 @@ public class DebugDrawable extends Drawable { mDebugPaint.setStrokeWidth(2); mDebugPaint.setStyle(Paint.Style.STROKE); mDebugPaint.setTextSize(24); - String headerDebugInfo = mNotificationPanelViewController.getHeaderDebugInfo(); - if (headerDebugInfo != null) canvas.drawText(headerDebugInfo, 50, 100, mDebugPaint); drawDebugInfo(canvas, mNotificationPanelViewController.getMaxPanelHeight(), Color.RED, "getMaxPanelHeight()"); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index d3d38e5c21cd..ba0cf08150f6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -138,7 +138,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; -import com.android.systemui.power.shared.model.WakefulnessModel; import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel; @@ -162,9 +161,10 @@ import com.android.systemui.plugins.FalsingManager.FalsingTapListener; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.power.shared.model.WakefulnessModel; import com.android.systemui.res.R; import com.android.systemui.shade.data.repository.ShadeRepository; -import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; @@ -200,7 +200,6 @@ import com.android.systemui.statusbar.phone.BounceInterpolator; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; @@ -217,6 +216,7 @@ import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; @@ -230,6 +230,8 @@ import com.android.systemui.util.Utils; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; +import dalvik.annotation.optimization.NeverCompile; + import kotlin.Unit; import java.io.PrintWriter; @@ -362,7 +364,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private boolean mIsLaunchAnimationRunning; private float mOverExpansion; private CentralSurfaces mCentralSurfaces; - private HeadsUpManagerPhone mHeadsUpManager; + private HeadsUpManager mHeadsUpManager; private float mExpandedHeight = 0; /** The current squish amount for the predictive back animation */ private float mCurrentBackProgress = 0.0f; @@ -1275,7 +1277,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById( R.id.keyguard_status_view); KeyguardStatusViewComponent statusViewComponent = - mKeyguardStatusViewComponentFactory.build(keyguardStatusView); + mKeyguardStatusViewComponentFactory.build(keyguardStatusView, + mView.getContext().getDisplay()); mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); mKeyguardStatusViewController.init(); } @@ -3025,7 +3028,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return headsUpVisible || isExpanded() || mBouncerShowing; } - private void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { + private void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; mHeadsUpManager.addListener(mOnHeadsUpChangedListener); mHeadsUpTouchHelper = new HeadsUpTouchHelper( @@ -3378,6 +3381,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mBlockingExpansionForCurrentTouch = isTracking(); } + @NeverCompile @Override public void dump(PrintWriter pw, String[] args) { pw.println(TAG + ":"); @@ -3507,7 +3511,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump GestureRecorder recorder, Runnable hideExpandedRunnable, NotificationShelfController notificationShelfController, - HeadsUpManagerPhone headsUpManager) { + HeadsUpManager headsUpManager) { setHeadsUpManager(headsUpManager); // TODO(b/254859580): this can be injected. mCentralSurfaces = centralSurfaces; @@ -3556,10 +3560,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); } - String getHeaderDebugInfo() { - return "USER " + mHeadsUpManager.getUser(); - } - @Override public void onThemeChanged() { mConfigurationListener.onThemeChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 6cf4ff569662..d05dfe2c11c1 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -21,7 +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.os.PowerManager; import android.util.Log; import android.view.GestureDetector; import android.view.InputDevice; @@ -36,10 +35,11 @@ import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.LockIconViewController; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.back.domain.interactor.BackActionInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder; import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.classifier.FalsingCollector; @@ -48,7 +48,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor; +import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionState; @@ -56,6 +56,7 @@ import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.log.BouncerLogger; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.res.R; import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -105,7 +106,9 @@ public class NotificationShadeWindowViewController implements Dumpable { private final NotificationInsetsController mNotificationInsetsController; private final boolean mIsTrackpadCommonEnabled; private final FeatureFlags mFeatureFlags; - private final KeyEventInteractor mKeyEventInteractor; + private final SysUIKeyEventHandler mSysUIKeyEventHandler; + private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private GestureDetector mPulsingWakeupGestureHandler; private GestureDetector mDreamingWakeupGestureHandler; private View mBrightnessMirror; @@ -182,7 +185,9 @@ public class NotificationShadeWindowViewController implements Dumpable { SystemClock clock, BouncerMessageInteractor bouncerMessageInteractor, BouncerLogger bouncerLogger, - KeyEventInteractor keyEventInteractor) { + SysUIKeyEventHandler sysUIKeyEventHandler, + PrimaryBouncerInteractor primaryBouncerInteractor, + AlternateBouncerInteractor alternateBouncerInteractor) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; @@ -209,7 +214,9 @@ public class NotificationShadeWindowViewController implements Dumpable { mNotificationInsetsController = notificationInsetsController; mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON); mFeatureFlags = featureFlags; - mKeyEventInteractor = keyEventInteractor; + mSysUIKeyEventHandler = sysUIKeyEventHandler; + mPrimaryBouncerInteractor = primaryBouncerInteractor; + mAlternateBouncerInteractor = alternateBouncerInteractor; // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); @@ -351,16 +358,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing()) { mDozeScrimController.extendPulse(); } - mLockIconViewController.onTouchEvent( - ev, - /* onGestureDetectedRunnable */ - () -> { - mService.userActivity(); - mPowerInteractor.wakeUpIfDozing( - "LOCK_ICON_TOUCH", - PowerManager.WAKE_REASON_GESTURE); - } - ); // In case we start outside of the view bounds (below the status bar), we need to // dispatch the touch manually as the view system can't accommodate for touches @@ -415,8 +412,18 @@ public class NotificationShadeWindowViewController implements Dumpable { private boolean shouldInterceptTouchEventInternal(MotionEvent ev) { mLastInterceptWasDragDownHelper = false; - if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing() - && !mDockManager.isDocked()) { + // When the device starts dozing, there's a delay before the device's display state + // changes from ON => DOZE to allow for the light reveal animation to run at + // a higher refresh rate and to delay visual changes (ie: display blink) when + // changing the display state. We'll call this specific state the + // "aodDefermentState". In this state we: + // - don't want touches to get sent to underlying views, except the lock icon + // - handle the tap to wake gesture via the PulsingGestureListener + if (mStatusBarStateController.isDozing() + && !mDozeServiceHost.isPulsing() + && !mDockManager.isDocked() + && !mLockIconViewController.willHandleTouchWhileDozing(ev) + ) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mShadeLogger.d("NSWVC: capture all touch events in always-on"); } @@ -432,16 +439,15 @@ public class NotificationShadeWindowViewController implements Dumpable { return true; } - if (mLockIconViewController.onInterceptTouchEvent(ev)) { - // immediately return true; don't send the touch to the drag down helper - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mShadeLogger.d("NSWVC: don't send touch to drag down helper"); - } - return true; + boolean bouncerShowing; + if (mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() + || mAlternateBouncerInteractor.isVisibleState(); + } else { + bouncerShowing = mService.isBouncerShowing(); } - if (mNotificationPanelViewController.isFullyExpanded() - && !mService.isBouncerShowing() + && !bouncerShowing && !mStatusBarStateController.isDozing()) { if (mDragDownHelper.isDragDownEnabled()) { // This handles drag down over lockscreen @@ -523,17 +529,17 @@ public class NotificationShadeWindowViewController implements Dumpable { @Override public boolean interceptMediaKey(KeyEvent event) { - return mKeyEventInteractor.interceptMediaKey(event); + return mSysUIKeyEventHandler.interceptMediaKey(event); } @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { - return mKeyEventInteractor.dispatchKeyEventPreIme(event); + return mSysUIKeyEventHandler.dispatchKeyEventPreIme(event); } @Override public boolean dispatchKeyEvent(KeyEvent event) { - return mKeyEventInteractor.dispatchKeyEvent(event); + return mSysUIKeyEventHandler.dispatchKeyEvent(event); } }); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 9b74ac4afed4..3bbb2cf83a83 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -105,6 +105,8 @@ import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.util.LargeScreenUtils; import com.android.systemui.util.kotlin.JavaAdapter; +import dalvik.annotation.optimization.NeverCompile; + import dagger.Lazy; import java.io.PrintWriter; @@ -2015,6 +2017,7 @@ public class QuickSettingsController implements Dumpable { (int) ((y - getInitialTouchY()) / displayDensity), (int) (vel / displayDensity)); } + @NeverCompile @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println(TAG + ":"); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index 447a15d34c05..2c4b0b990e6d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -178,9 +178,6 @@ public interface ShadeController extends CoreStartable { /** Listens for shade visibility changes. */ interface ShadeVisibilityListener { - /** Called when the visibility of the shade changes. */ - void visibilityChanged(boolean visible); - /** Called when shade expanded and visible state changed. */ void expandedVisibleChanged(boolean expandedVisible); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index 367449b9d59d..fdc7eecd9b15 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -24,6 +24,7 @@ import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import com.android.systemui.DejankUtils; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -75,6 +76,7 @@ public final class ShadeControllerImpl implements ShadeController { private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); private boolean mExpandedVisible; + private boolean mLockscreenOrShadeVisible; private NotificationPresenter mPresenter; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; @@ -399,8 +401,19 @@ public final class ShadeControllerImpl implements ShadeController { } private void notifyVisibilityChanged(boolean visible) { - mShadeVisibilityListener.visibilityChanged(visible); mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(visible); + if (mLockscreenOrShadeVisible != visible) { + mLockscreenOrShadeVisible = visible; + if (visible) { + // It would be best if this could be done as a side effect of listening to the + // [WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible] flow inside + // NotificationShadeWindowViewController. However, there's no guarantee that the + // flow will emit in the same frame as when the visibility changed, and we want the + // DejankUtils to be notified immediately, so we do it immediately here. + DejankUtils.notifyRendererOfExpensiveFrame( + getNotificationShadeWindowView(), "onShadeVisibilityChanged"); + } + } } private void notifyExpandedVisibleChanged(boolean expandedVisible) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt index 6ee6cbf69a70..4e23e7d97a01 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt @@ -20,7 +20,7 @@ import com.android.systemui.statusbar.GestureRecorder import com.android.systemui.statusbar.NotificationShelfController import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.HeadsUpManager /** * Allows CentralSurfacesImpl to interact with the shade. Only CentralSurfacesImpl should reference @@ -34,7 +34,7 @@ interface ShadeSurface : ShadeViewController { recorder: GestureRecorder, hideExpandedRunnable: Runnable, notificationShelfController: NotificationShelfController, - headsUpManager: HeadsUpManagerPhone + headsUpManager: HeadsUpManager ) /** Cancels any pending collapses. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt index ac8333ae84ad..6117f9f80e6c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt @@ -19,7 +19,11 @@ package com.android.systemui.shade.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.ObservableTransitionState @@ -27,8 +31,9 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject @@ -56,13 +61,16 @@ class ShadeInteractor @Inject constructor( @Application scope: CoroutineScope, + deviceProvisioningRepository: DeviceProvisioningRepository, disableFlagsRepository: DisableFlagsRepository, + dozeParams: DozeParameters, sceneContainerFlags: SceneContainerFlags, // TODO(b/300258424) convert to direct reference instead of provider sceneInteractorProvider: Provider<SceneInteractor>, keyguardRepository: KeyguardRepository, + keyguardTransitionInteractor: KeyguardTransitionInteractor, + powerInteractor: PowerInteractor, userSetupRepository: UserSetupRepository, - deviceProvisionedController: DeviceProvisionedController, userInteractor: UserInteractor, sharedNotificationContainerInteractor: SharedNotificationContainerInteractor, repository: ShadeRepository, @@ -187,6 +195,26 @@ constructor( combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs } .distinctUntilChanged() + /** Are touches allowed on the notification panel? */ + val isShadeTouchable: Flow<Boolean> = + combine( + powerInteractor.isAsleep, + keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD }, + keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING }, + deviceProvisioningRepository.isFactoryResetProtectionActive, + ) { isAsleep, goingToSleep, isPulsing, isFrpActive -> + when { + // Touches are disabled when Factory Reset Protection is active + isFrpActive -> false + // If the device is going to sleep, only accept touches if we're still + // animating + goingToSleep -> dozeParams.shouldControlScreenOff() + // If the device is asleep, only accept touches if there's a pulse + isAsleep -> isPulsing + else -> true + } + } + /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */ val isExpandToQsEnabled: Flow<Boolean> = combine( @@ -194,8 +222,9 @@ constructor( isShadeEnabled, keyguardRepository.isDozing, userSetupRepository.isUserSetupFlow, - ) { disableFlags, isShadeEnabled, isDozing, isUserSetup -> - deviceProvisionedController.isDeviceProvisioned && + deviceProvisioningRepository.isDeviceProvisioned, + ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned -> + isDeviceProvisioned && // Disallow QS during setup if it's a simple user switcher. (The user intends to // use the lock screen user switcher, QS is not needed.) (isUserSetup || !userInteractor.isSimpleUserSwitcher) && @@ -232,7 +261,7 @@ constructor( when (state) { is ObservableTransitionState.Idle -> false is ObservableTransitionState.Transition -> - state.isUserInputDriven && + state.isInitiatedByUserInput && (state.toScene == sceneKey || state.fromScene == sceneKey) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java index d24f9d8f476c..77b095802b00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import android.view.View; +import androidx.annotation.Nullable; + import com.android.app.animation.Interpolators; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -113,7 +115,16 @@ public class CrossFadeHelper { fadeIn(view, ANIMATION_DURATION_LENGTH, 0); } + public static void fadeIn(final View view, Runnable endRunnable) { + fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, endRunnable); + } + public static void fadeIn(final View view, long duration, int delay) { + fadeIn(view, duration, delay, /* endRunnable= */ null); + } + + public static void fadeIn(final View view, long duration, int delay, + @Nullable Runnable endRunnable) { view.animate().cancel(); if (view.getVisibility() == View.INVISIBLE) { view.setAlpha(0.0f); @@ -124,7 +135,7 @@ public class CrossFadeHelper { .setDuration(duration) .setStartDelay(delay) .setInterpolator(Interpolators.ALPHA_IN) - .withEndAction(null); + .withEndAction(endRunnable); if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) { view.animate().withLayer(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 09ad55e005d4..f8c049e86cb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -37,11 +37,11 @@ import androidx.annotation.NonNull; import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; -import com.android.systemui.res.R; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.flags.Flags; -import com.android.systemui.flags.ViewRefactorFlag; +import com.android.systemui.flags.RefactorFlag; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.res.R; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.SourceType; @@ -96,10 +96,10 @@ public class NotificationShelf extends ActivatableNotificationView implements St private float mCornerAnimationDistance; private NotificationShelfController mController; private float mActualWidth = -1; - private final ViewRefactorFlag mSensitiveRevealAnim = - new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM); - private final ViewRefactorFlag mShelfRefactor = - new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR); + private final RefactorFlag mSensitiveRevealAnim = + RefactorFlag.forView(Flags.SENSITIVE_REVEAL_ANIM); + private final RefactorFlag mShelfRefactor = + RefactorFlag.forView(Flags.NOTIFICATION_SHELF_REFACTOR); private boolean mCanModifyColorOfNotifications; private boolean mCanInteract; private NotificationStackScrollLayout mHostLayout; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 3640ae067537..4ea70264b152 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -31,22 +31,22 @@ import androidx.annotation.VisibleForTesting import com.android.app.animation.Interpolators import com.android.systemui.Dumpable import com.android.systemui.Gefingerpoken -import com.android.systemui.res.R import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.HeadsUpManager import java.io.PrintWriter import javax.inject.Inject import kotlin.math.max @@ -63,7 +63,7 @@ constructor( context: Context, private val wakeUpCoordinator: NotificationWakeUpCoordinator, private val bypassController: KeyguardBypassController, - private val headsUpManager: HeadsUpManagerPhone, + private val headsUpManager: HeadsUpManager, private val roundnessManager: NotificationRoundnessManager, configurationController: ConfigurationController, private val statusBarStateController: StatusBarStateController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index f616b91c4712..3a4ad0e79994 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -51,6 +51,7 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.Interpolator; +import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import com.android.app.animation.Interpolators; @@ -959,12 +960,17 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } public void setDozing(boolean dozing, boolean fade, long delay) { + setDozing(dozing, fade, delay, /* onChildCompleted= */ null); + } + + public void setDozing(boolean dozing, boolean fade, long delay, + @Nullable Runnable endRunnable) { mDozer.setDozing(f -> { mDozeAmount = f; updateDecorColor(); updateIconColor(); updateAllowAnimation(); - }, dozing, fade, delay, this); + }, dozing, fade, delay, this, endRunnable); } private void updateAllowAnimation() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java index ff40f70a84fb..91ca148c93c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java @@ -87,7 +87,7 @@ public class AccessPointControllerImpl implements AccessPointController, */ public void init() { if (mWifiPickerTracker == null) { - mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this); + mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this, TAG); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 9db61c6b3ba2..fc84973c46bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -83,6 +83,8 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.CarrierConfigTracker; +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -1154,6 +1156,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } /** */ + @NeverCompile public void dump(PrintWriter pw, String[] args) { pw.println("NetworkController state:"); pw.println(" mUserSetup=" + mUserSetup); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt index ddbfcef513c3..dc2ebe55fb73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt @@ -23,8 +23,8 @@ import android.os.Handler import android.os.SimpleClock import androidx.lifecycle.Lifecycle import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.util.concurrency.ThreadFactory import com.android.systemui.util.time.SystemClock import com.android.wifitrackerlib.WifiPickerTracker import com.android.wifitrackerlib.WifiPickerTracker.WifiPickerTrackerCallback @@ -46,7 +46,7 @@ constructor( private val connectivityManager: ConnectivityManager, private val systemClock: SystemClock, @Main private val mainHandler: Handler, - @Background private val workerHandler: Handler, + private val threadFactory: ThreadFactory, ) { private val clock: Clock = object : SimpleClock(ZoneOffset.UTC) { @@ -60,11 +60,13 @@ constructor( /** * Creates a [WifiPickerTracker] instance. * + * @param name a name to identify the worker thread used for [WifiPickerTracker] operations. * @return a new [WifiPickerTracker] or null if [WifiManager] is null. */ fun create( lifecycle: Lifecycle, listener: WifiPickerTrackerCallback, + name: String, ): WifiPickerTracker? { return if (wifiManager == null) { null @@ -75,7 +77,10 @@ constructor( wifiManager, connectivityManager, mainHandler, - workerHandler, + // WifiPickerTracker can take tens of seconds to finish operations, so it can't use + // the default background handler (it would block all other background operations). + // Use a custom handler instead. + threadFactory.buildHandlerOnNewThread("WifiPickerTracker-$name"), clock, MAX_SCAN_AGE_MILLIS, SCAN_INTERVAL_MILLIS, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java index 167efc784ff5..dc0eb7b4aab3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java @@ -24,6 +24,8 @@ import android.graphics.ColorMatrixColorFilter; import android.view.View; import android.widget.ImageView; +import androidx.annotation.Nullable; + import com.android.app.animation.Interpolators; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -81,6 +83,11 @@ public class NotificationDozeHelper { public void setDozing(Consumer<Float> listener, boolean dozing, boolean animate, long delay, View view) { + setDozing(listener, dozing, animate, delay, view, /* endRunnable= */ null); + } + + public void setDozing(Consumer<Float> listener, boolean dozing, + boolean animate, long delay, View view, @Nullable Runnable endRunnable) { if (animate) { startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dozing, delay, @@ -89,6 +96,9 @@ public class NotificationDozeHelper { @Override public void onAnimationEnd(Animator animation) { view.setTag(DOZE_ANIMATOR_TAG, null); + if (endRunnable != null) { + endRunnable.run(); + } } @Override @@ -102,6 +112,9 @@ public class NotificationDozeHelper { animator.cancel(); } listener.accept(dozing ? 1f : 0f); + if (endRunnable != null) { + endRunnable.run(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt index c62546f67f8d..756151bd57e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt @@ -24,7 +24,7 @@ import com.android.systemui.animation.LaunchAnimator import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpUtil import kotlin.math.ceil import kotlin.math.max @@ -35,7 +35,7 @@ private const val TAG = "NotificationLaunchAnimatorController" class NotificationLaunchAnimatorControllerProvider( private val notificationExpansionRepository: NotificationExpansionRepository, private val notificationListContainer: NotificationListContainer, - private val headsUpManager: HeadsUpManagerPhone, + private val headsUpManager: HeadsUpManager, private val jankMonitor: InteractionJankMonitor ) { @JvmOverloads @@ -62,7 +62,7 @@ class NotificationLaunchAnimatorControllerProvider( class NotificationLaunchAnimatorController( private val notificationExpansionRepository: NotificationExpansionRepository, private val notificationListContainer: NotificationListContainer, - private val headsUpManager: HeadsUpManagerPhone, + private val headsUpManager: HeadsUpManager, private val notification: ExpandableNotificationRow, private val jankMonitor: InteractionJankMonitor, private val onFinishAnimationCallback: Runnable? @@ -152,16 +152,17 @@ class NotificationLaunchAnimatorController( } } - private val headsUpNotificationRow: ExpandableNotificationRow? get() { - val summaryEntry = notificationEntry.parent?.summary + private val headsUpNotificationRow: ExpandableNotificationRow? + get() { + val summaryEntry = notificationEntry.parent?.summary - return when { - headsUpManager.isAlerting(notificationKey) -> notification - summaryEntry == null -> null - headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row - else -> null + return when { + headsUpManager.isAlerting(notificationKey) -> notification + summaryEntry == null -> null + headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row + else -> null + } } - } private fun removeHun(animate: Boolean) { val row = headsUpNotificationRow ?: return diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 328a74100e48..3e9c6fbb2ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -3,10 +3,10 @@ package com.android.systemui.statusbar.notification import android.util.FloatProperty import android.view.View import androidx.annotation.FloatRange -import com.android.systemui.res.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.flags.ViewRefactorFlag +import com.android.systemui.flags.RefactorFlag +import com.android.systemui.res.R import com.android.systemui.statusbar.notification.stack.AnimationProperties import com.android.systemui.statusbar.notification.stack.StackStateAnimator import kotlin.math.abs @@ -323,7 +323,7 @@ constructor( internal var maxRadius = maxRadius private set - internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS) + internal val newHeadsUpAnim = RefactorFlag.forView(Flags.IMPROVED_HUN_ANIMATIONS, featureFlags) /** Animatable for top roundness */ private val topAnimatable = topAnimatable(roundable) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 07eb8a00a178..2d839704e0b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -37,8 +37,8 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.asIndenting @@ -85,7 +85,7 @@ constructor( @Application private val scope: CoroutineScope, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val secureSettings: SecureSettings, - private val seenNotifsProvider: SeenNotificationsProviderImpl, + private val notificationListInteractor: NotificationListInteractor, private val statusBarStateController: StatusBarStateController, ) : Coordinator, Dumpable { @@ -351,7 +351,7 @@ constructor( override fun onCleanup() { logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs) - seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs + notificationListInteractor.setHasFilteredOutSeenNotifications(hasFilteredAnyNotifs) hasFilteredAnyNotifs = false } } @@ -388,8 +388,8 @@ constructor( override fun dump(pw: PrintWriter, args: Array<out String>) = with(pw.asIndenting()) { println( - "seenNotifsProvider.hasFilteredOutSeenNotifications=" + - seenNotifsProvider.hasFilteredOutSeenNotifications + "notificationListInteractor.hasFilteredOutSeenNotifications.value=" + + notificationListInteractor.hasFilteredOutSeenNotifications.value ) println("unseen notifications:") indentIfPossible { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt index 657c394d4e30..c0f674846991 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt @@ -28,6 +28,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Compile +import com.android.systemui.util.traceSection import javax.inject.Inject /** @@ -122,8 +123,10 @@ class ViewConfigCoordinator @Inject internal constructor( private fun updateNotificationsOnUiModeChanged() { log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" } - mPipeline?.allNotifs?.forEach { entry -> - entry.row?.onUiModeChanged() + traceSection("updateNotifOnUiModeChanged") { + mPipeline?.allNotifs?.forEach { entry -> + entry.row?.onUiModeChanged() + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt deleted file mode 100644 index cff47e220299..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt +++ /dev/null @@ -1,41 +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.systemui.statusbar.notification.collection.provider - -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module -import javax.inject.Inject - -/** Keeps track of whether "seen" notification content has been filtered out of the shade. */ -interface SeenNotificationsProvider { - /** Are any already-seen notifications currently filtered out of the shade? */ - val hasFilteredOutSeenNotifications: Boolean -} - -@Module -interface SeenNotificationsProviderModule { - @Binds - fun bindSeenNotificationsProvider( - impl: SeenNotificationsProviderImpl - ): SeenNotificationsProvider -} - -@SysUISingleton -class SeenNotificationsProviderImpl @Inject constructor() : SeenNotificationsProvider { - override var hasFilteredOutSeenNotifications: Boolean = false -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt index 59fc387c4608..1a88815acfaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt @@ -231,18 +231,24 @@ private class ShadeNode(val controller: NodeController) { fun getChildCount(): Int = controller.getChildCount() fun addChildAt(child: ShadeNode, index: Int) { - controller.addChildAt(child.controller, index) - child.controller.onViewAdded() + traceSection("ShadeNode#addChildAt") { + controller.addChildAt(child.controller, index) + child.controller.onViewAdded() + } } fun moveChildTo(child: ShadeNode, index: Int) { - controller.moveChildTo(child.controller, index) - child.controller.onViewMoved() + traceSection("ShadeNode#moveChildTo") { + controller.moveChildTo(child.controller, index) + child.controller.onViewMoved() + } } fun removeChild(child: ShadeNode, isTransfer: Boolean) { - controller.removeChild(child.controller, isTransfer) - child.controller.onViewRemoved() + traceSection("ShadeNode#removeChild") { + controller.removeChild(child.controller, isTransfer) + child.controller.onViewRemoved() + } } fun offerToKeepInParentForAnimation(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 8ee0de691557..8561869af352 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -20,10 +20,10 @@ import android.content.Context; import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.CoreStartable; -import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; import com.android.systemui.shade.ShadeEventsModule; import com.android.systemui.statusbar.NotificationListener; @@ -45,7 +45,6 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProviderImpl; import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl; -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl; @@ -54,6 +53,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; +import com.android.systemui.statusbar.notification.data.NotificationDataLayerModule; import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository; import com.android.systemui.statusbar.notification.icon.ConversationIconManager; import com.android.systemui.statusbar.notification.icon.IconManager; @@ -74,9 +74,9 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.kotlin.JavaAdapter; import dagger.Binds; @@ -95,8 +95,8 @@ import javax.inject.Provider; @Module(includes = { CoordinatorsModule.class, KeyguardNotificationVisibilityProviderModule.class, - SeenNotificationsProviderModule.class, ShadeEventsModule.class, + NotificationDataLayerModule.class, NotifPipelineChoreographerModule.class, NotificationSectionHeadersModule.class, NotificationListViewModelModule.class, @@ -206,7 +206,7 @@ public interface NotificationsModule { static NotificationLaunchAnimatorControllerProvider provideNotifLaunchAnimControllerProvider( NotificationExpansionRepository notificationExpansionRepository, NotificationListContainer notificationListContainer, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, InteractionJankMonitor jankMonitor) { return new NotificationLaunchAnimatorControllerProvider( notificationExpansionRepository, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt new file mode 100644 index 000000000000..5435fb5449cd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.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.data + +import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardStateRepositoryModule +import dagger.Module + +@Module(includes = [NotificationsKeyguardStateRepositoryModule::class]) +interface NotificationDataLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt new file mode 100644 index 000000000000..cf03d1c5addc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt @@ -0,0 +1,73 @@ +/* + * 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.data.repository + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** View-states pertaining to notifications on the keyguard. */ +interface NotificationsKeyguardViewStateRepository { + /** Are notifications fully hidden from view? */ + val areNotificationsFullyHidden: Flow<Boolean> + + /** Is a pulse expansion occurring? */ + val isPulseExpanding: Flow<Boolean> +} + +@Module +interface NotificationsKeyguardStateRepositoryModule { + @Binds + fun bindImpl( + impl: NotificationsKeyguardViewStateRepositoryImpl + ): NotificationsKeyguardViewStateRepository +} + +@SysUISingleton +class NotificationsKeyguardViewStateRepositoryImpl +@Inject +constructor( + wakeUpCoordinator: NotificationWakeUpCoordinator, +) : NotificationsKeyguardViewStateRepository { + override val areNotificationsFullyHidden: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : NotificationWakeUpCoordinator.WakeUpListener { + override fun onFullyHiddenChanged(isFullyHidden: Boolean) { + trySend(isFullyHidden) + } + } + trySend(wakeUpCoordinator.notificationsFullyHidden) + wakeUpCoordinator.addListener(listener) + awaitClose { wakeUpCoordinator.removeListener(listener) } + } + + override val isPulseExpanding: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : NotificationWakeUpCoordinator.WakeUpListener { + override fun onPulseExpansionChanged(expandingChanged: Boolean) { + trySend(expandingChanged) + } + } + trySend(wakeUpCoordinator.isPulseExpanding()) + wakeUpCoordinator.addListener(listener) + awaitClose { wakeUpCoordinator.removeListener(listener) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt new file mode 100644 index 000000000000..87b8e55dbd1a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.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.domain.interactor + +import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Domain logic pertaining to notifications on the keyguard. */ +class NotificationsKeyguardInteractor +@Inject +constructor( + repository: NotificationsKeyguardViewStateRepository, +) { + /** Is a pulse expansion occurring? */ + val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding + + /** Are notifications fully hidden from view? */ + val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java index 26db5f2bc095..e74b3fcdf050 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java @@ -11,10 +11,10 @@ * 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. */ -package com.android.systemui.statusbar.notification.row; +package com.android.systemui.statusbar.notification.footer.ui.view; import static android.graphics.PorterDuff.Mode.SRC_ATOP; @@ -35,6 +35,8 @@ import androidx.annotation.NonNull; import com.android.settingslib.Utils; import com.android.systemui.res.R; +import com.android.systemui.statusbar.notification.row.FooterViewButton; +import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.util.DumpUtilsKt; @@ -93,6 +95,7 @@ public class FooterView extends StackScrollerDecorView { updateColors(); } + /** Show a message instead of the footer buttons. */ public void setFooterLabelVisible(boolean isVisible) { if (isVisible) { mManageButton.setVisibility(View.GONE); @@ -105,14 +108,22 @@ public class FooterView extends StackScrollerDecorView { } } + /** Set onClickListener for the manage/history button. */ public void setManageButtonClickListener(OnClickListener listener) { mManageButton.setOnClickListener(listener); } + /** Set onClickListener for the clear all (end) button. */ public void setClearAllButtonClickListener(OnClickListener listener) { mClearAllButton.setOnClickListener(listener); } + /** + * Whether the touch is outside the Clear all button. + * + * TODO(b/293167744): This is an artifact from the time when we could press underneath the + * shade to dismiss it. Check if it's safe to remove. + */ public boolean isOnEmptySpace(float touchX, float touchY) { return touchX < mContent.getX() || touchX > mContent.getX() + mContent.getWidth() @@ -120,6 +131,7 @@ public class FooterView extends StackScrollerDecorView { || touchY > mContent.getY() + mContent.getHeight(); } + /** Show "History" instead of "Manage" on the start button. */ public void showHistory(boolean showHistory) { if (mShowHistory == showHistory) { return; @@ -141,6 +153,7 @@ public class FooterView extends StackScrollerDecorView { .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null); } + /** Whether the start button shows "History" (true) or "Manage" (false). */ public boolean isHistoryShown() { return mShowHistory; } 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 index 20241c323ab6..50efbb5458cf 100644 --- 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 @@ -26,25 +26,21 @@ 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.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.FeatureFlagsClassic import com.android.systemui.flags.Flags -import com.android.systemui.flags.ViewRefactorFlag +import com.android.systemui.flags.RefactorFlag import com.android.systemui.plugins.DarkIconDispatcher -import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R -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 @@ -59,6 +55,7 @@ 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.policy.ConfigurationController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.wm.shell.bubbles.Bubbles import java.util.Optional @@ -78,9 +75,9 @@ class NotificationIconAreaControllerViewBinderWrapperImpl @Inject constructor( private val context: Context, - private val statusBarStateController: StatusBarStateController, private val wakeUpCoordinator: NotificationWakeUpCoordinator, private val bypassController: KeyguardBypassController, + private val configurationController: ConfigurationController, private val mediaManager: NotificationMediaManager, notificationListener: NotificationListener, private val dozeParameters: DozeParameters, @@ -88,7 +85,7 @@ constructor( private val bubblesOptional: Optional<Bubbles>, demoModeController: DemoModeController, darkIconDispatcher: DarkIconDispatcher, - featureFlags: FeatureFlags, + private val featureFlags: FeatureFlagsClassic, private val statusBarWindowController: StatusBarWindowController, private val screenOffAnimationController: ScreenOffAnimationController, private val shelfIconsViewModel: NotificationIconContainerShelfViewModel, @@ -97,14 +94,12 @@ constructor( ) : 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 statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) + private val shelfRefactor = RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR) private val tintAreas = ArrayList<Rect>() private var iconSize = 0 @@ -117,9 +112,7 @@ constructor( 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 @@ -132,7 +125,6 @@ constructor( } init { - statusBarStateController.addCallback(this) wakeUpCoordinator.addListener(this) demoModeController.addCallback(this) notificationListener.addNotificationSettingsListener(settingsListener) @@ -156,9 +148,15 @@ constructor( } this.aodIcons = aodIcons this.aodIcons!!.setOnLockScreen(true) - aodBindJob = NotificationIconContainerViewBinder.bind(aodIcons, aodIconsViewModel) - updateAodIconsVisibility(animate = false, forceUpdate = changed) - updateAnimations() + aodBindJob = + NotificationIconContainerViewBinder.bind( + aodIcons, + aodIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, + ) if (changed) { updateAodNotificationIcons() } @@ -170,7 +168,14 @@ constructor( override fun setShelfIcons(icons: NotificationIconContainer) { if (shelfRefactor.expectEnabled()) { - NotificationIconContainerViewBinder.bind(icons, shelfIconsViewModel) + NotificationIconContainerViewBinder.bind( + icons, + shelfIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, + ) shelfIcons = icons } } @@ -243,23 +248,7 @@ constructor( 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 setAnimationsEnabled(enabled: Boolean) = unsupported override fun onThemeChanged() { reloadAodColor() @@ -270,50 +259,11 @@ constructor( return if (aodIcons == null) 0 else aodIcons!!.height } - @VisibleForTesting - fun appearAodIcons() { - if (aodIcons == null) { - return - } - if (screenOffAnimationController.shouldAnimateAodIcons()) { - if (!statusViewMigrated) { - 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 - if (!statusViewMigrated) { - 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) @@ -344,7 +294,14 @@ constructor( val layoutInflater = LayoutInflater.from(context) notificationIconArea = inflateIconArea(layoutInflater) notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons) - NotificationIconContainerViewBinder.bind(notificationIcons!!, statusBarIconsViewModel) + NotificationIconContainerViewBinder.bind( + notificationIcons!!, + statusBarIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, + ) } private fun updateIconLayoutParams(context: Context) { @@ -594,25 +551,6 @@ constructor( 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() { - if (!statusViewMigrated) { - aodIcons!! - .animate() - .setInterpolator(Interpolators.DECELERATE_QUINT) - .translationY(0f) - .setDuration(AOD_ICONS_APPEAR_DURATION) - .start() - } - } - private fun reloadAodColor() { aodIconTint = Utils.getColorAttrDefaultColor( @@ -635,59 +573,13 @@ constructor( } } - 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 - if (!statusViewMigrated) { - 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 + + val unsupported: Nothing + get() = + error( + "Code path not supported when NOTIFICATION_ICON_CONTAINER_REFACTOR is disabled" + ) } } 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 index 8293bb329a01..0d2f00aa3627 100644 --- 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 @@ -15,19 +15,172 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewbinder +import android.content.res.Resources +import android.view.View +import androidx.annotation.DimenRes import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.statusbar.CrossFadeHelper import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged +import com.android.systemui.util.kotlin.stateFlow +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch /** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */ object NotificationIconContainerViewBinder { fun bind( view: NotificationIconContainer, viewModel: NotificationIconContainerViewModel, + configurationController: ConfigurationController, + dozeParameters: DozeParameters, + featureFlags: FeatureFlagsClassic, + screenOffAnimationController: ScreenOffAnimationController, ): DisposableHandle { - return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) {} } + return view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.CREATED) { + launch { viewModel.animationsEnabled.collect(view::setAnimationsEnabled) } + launch { + viewModel.isDozing.collect { (isDozing, animate) -> + val animateIfNotBlanking = animate && !dozeParameters.displayNeedsBlanking + view.setDozing(isDozing, animateIfNotBlanking, /* delay= */ 0) { + viewModel.completeDozeAnimation() + } + } + } + // TODO(278765923): this should live where AOD is bound, not inside of the NIC + // view-binder + launch { + val iconAppearTranslation = + view.resources.getConfigAwareDimensionPixelSize( + this, + configurationController, + R.dimen.shelf_appear_translation, + ) + bindVisibility( + viewModel, + view, + featureFlags, + screenOffAnimationController, + iconAppearTranslation, + ) { + viewModel.completeVisibilityAnimation() + } + } + } + } } + private suspend fun bindVisibility( + viewModel: NotificationIconContainerViewModel, + view: NotificationIconContainer, + featureFlags: FeatureFlagsClassic, + screenOffAnimationController: ScreenOffAnimationController, + iconAppearTranslation: StateFlow<Int>, + onAnimationEnd: () -> Unit, + ) { + val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) + viewModel.isVisible.collect { (isVisible, animate) -> + view.animate().cancel() + when { + !animate -> { + view.alpha = 1f + if (!statusViewMigrated) { + view.translationY = 0f + } + view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + } + featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> { + animateInIconTranslation(view, statusViewMigrated) + if (isVisible) { + CrossFadeHelper.fadeIn(view, onAnimationEnd) + } else { + CrossFadeHelper.fadeOut(view, onAnimationEnd) + } + } + !isVisible -> { + // Let's make sure the icon are translated to 0, since we cancelled it above + animateInIconTranslation(view, statusViewMigrated) + CrossFadeHelper.fadeOut(view, onAnimationEnd) + } + view.visibility != View.VISIBLE -> { + // No fading here, let's just appear the icons instead! + view.visibility = View.VISIBLE + view.alpha = 1f + appearIcons( + view, + animate = screenOffAnimationController.shouldAnimateAodIcons(), + iconAppearTranslation.value, + statusViewMigrated, + ) + onAnimationEnd() + } + else -> { + // Let's make sure the icons are translated to 0, since we cancelled it above + animateInIconTranslation(view, statusViewMigrated) + // We were fading out, let's fade in instead + CrossFadeHelper.fadeIn(view, onAnimationEnd) + } + } + } + } + + private fun appearIcons( + view: View, + animate: Boolean, + iconAppearTranslation: Int, + statusViewMigrated: Boolean, + ) { + if (animate) { + if (!statusViewMigrated) { + view.translationY = -iconAppearTranslation.toFloat() + } + view.alpha = 0f + animateInIconTranslation(view, statusViewMigrated) + view + .animate() + .alpha(1f) + .setInterpolator(Interpolators.LINEAR) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } else { + view.alpha = 1.0f + if (!statusViewMigrated) { + view.translationY = 0f + } + } + } + + private fun animateInIconTranslation(view: View, statusViewMigrated: Boolean) { + if (!statusViewMigrated) { + view + .animate() + .setInterpolator(Interpolators.DECELERATE_QUINT) + .translationY(0f) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } + } + + private const val AOD_ICONS_APPEAR_DURATION: Long = 200 } + +fun Resources.getConfigAwareDimensionPixelSize( + scope: CoroutineScope, + configurationController: ConfigurationController, + @DimenRes id: Int, +): StateFlow<Int> = + scope.stateFlow( + changedSignals = configurationController.onDensityOrFontScaleChanged, + getValue = { getDimensionPixelSize(id) } + ) 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 index f68b0ef79638..3289a3ce5574 100644 --- 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 @@ -15,8 +15,146 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.util.kotlin.pairwise +import com.android.systemui.util.kotlin.sample +import com.android.systemui.util.ui.AnimatableEvent +import com.android.systemui.util.ui.AnimatedValue +import com.android.systemui.util.ui.toAnimatedValueFlow import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map /** View-model for the row of notification icons displayed on the always-on display. */ -class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor() : - NotificationIconContainerViewModel +@SysUISingleton +class NotificationIconContainerAlwaysOnDisplayViewModel +@Inject +constructor( + private val deviceEntryInteractor: DeviceEntryInteractor, + private val dozeParameters: DozeParameters, + private val featureFlags: FeatureFlagsClassic, + keyguardInteractor: KeyguardInteractor, + keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor, + screenOffAnimationController: ScreenOffAnimationController, + shadeInteractor: ShadeInteractor, +) : NotificationIconContainerViewModel { + + private val onDozeAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) + private val onVisAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) + + override val animationsEnabled: Flow<Boolean> = + combine( + shadeInteractor.isShadeTouchable, + keyguardInteractor.isKeyguardVisible, + ) { panelTouchesEnabled, isKeyguardVisible -> + panelTouchesEnabled && isKeyguardVisible + } + + override val isDozing: Flow<AnimatedValue<Boolean>> = + keyguardTransitionInteractor.startedKeyguardTransitionStep + // Determine if we're dozing based on the most recent transition + .map { step: TransitionStep -> + val isDozing = step.to == KeyguardState.AOD || step.to == KeyguardState.DOZING + isDozing to step + } + // Only emit changes based on whether we've started or stopped dozing + .distinctUntilChanged { (wasDozing, _), (isDozing, _) -> wasDozing != isDozing } + // Determine whether we need to animate + .map { (isDozing, step) -> + val animate = step.to == KeyguardState.AOD || step.from == KeyguardState.AOD + AnimatableEvent(isDozing, animate) + } + .distinctUntilChanged() + .toAnimatedValueFlow(completionEvents = onDozeAnimationComplete) + + override val isVisible: Flow<AnimatedValue<Boolean>> = + combine( + keyguardTransitionInteractor.finishedKeyguardState.map { it != KeyguardState.GONE }, + deviceEntryInteractor.isBypassEnabled, + areNotifsFullyHiddenAnimated(), + isPulseExpandingAnimated(), + ) { + onKeyguard: Boolean, + bypassEnabled: Boolean, + (notifsFullyHidden: Boolean, isAnimatingHide: Boolean), + (pulseExpanding: Boolean, isAnimatingPulse: Boolean), + -> + val isAnimating = isAnimatingHide || isAnimatingPulse + when { + // 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 if we're + // animating in the AOD UI and will be switching to KEYGUARD shortly. + !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() -> + AnimatedValue(false, isAnimating = false) + // If we're bypassing, then we're visible + bypassEnabled -> AnimatedValue(true, isAnimating) + // If we are pulsing (and not bypassing), then we are hidden + pulseExpanding -> AnimatedValue(false, isAnimating) + // If notifs are fully gone, then we're visible + notifsFullyHidden -> AnimatedValue(true, isAnimating) + // Otherwise, we're hidden + else -> AnimatedValue(false, isAnimating) + } + } + .distinctUntilChanged() + + override fun completeDozeAnimation() { + onDozeAnimationComplete.tryEmit(Unit) + } + + override fun completeVisibilityAnimation() { + onVisAnimationComplete.tryEmit(Unit) + } + + /** Is there an expanded pulse, are we animating in response? */ + private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> { + return notificationsKeyguardInteractor.isPulseExpanding + .pairwise(initialValue = null) + // If pulsing changes, start animating, unless it's the first emission + .map { (prev, expanding) -> + AnimatableEvent(expanding!!, startAnimating = prev != null) + } + .toAnimatedValueFlow(completionEvents = onVisAnimationComplete) + } + + /** Are notifications completely hidden from view, are we animating in response? */ + private fun areNotifsFullyHiddenAnimated(): Flow<AnimatedValue<Boolean>> { + return notificationsKeyguardInteractor.areNotificationsFullyHidden + .pairwise(initialValue = null) + .sample(deviceEntryInteractor.isBypassEnabled) { (prev, fullyHidden), bypassEnabled -> + val animate = + when { + // Don't animate for the first value + prev == null -> false + // Always animate if bypass is enabled. + bypassEnabled -> true + // If we're not bypassing and we're not going to AOD, then we're not + // animating. + !dozeParameters.alwaysOn -> false + // Don't animate when going to AOD if the display needs blanking. + dozeParameters.displayNeedsBlanking -> false + // We only want the appear animations to happen when the notifications + // get fully hidden, since otherwise the un-hide animation overlaps. + featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true + else -> fullyHidden!! + } + AnimatableEvent(fullyHidden!!, animate) + } + .toAnimatedValueFlow(completionEvents = onVisAnimationComplete) + } +} 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 index 933c76f19aee..c44a2b60142c 100644 --- 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 @@ -15,8 +15,18 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.util.ui.AnimatedValue import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flowOf /** View-model for the overflow row of notification icons displayed in the notification shade. */ class NotificationIconContainerShelfViewModel @Inject constructor() : - NotificationIconContainerViewModel + NotificationIconContainerViewModel { + override val animationsEnabled: Flow<Boolean> = flowOf(true) + override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow() + override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() + override fun completeDozeAnimation() {} + override fun completeVisibilityAnimation() {} +} 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 index 2217646e6022..035687a4a91b 100644 --- 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 @@ -15,8 +15,31 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.util.ui.AnimatedValue import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow /** View-model for the row of notification icons displayed in the status bar, */ -class NotificationIconContainerStatusBarViewModel @Inject constructor() : - NotificationIconContainerViewModel +class NotificationIconContainerStatusBarViewModel +@Inject +constructor( + keyguardInteractor: KeyguardInteractor, + shadeInteractor: ShadeInteractor, +) : NotificationIconContainerViewModel { + override val animationsEnabled: Flow<Boolean> = + combine( + shadeInteractor.isShadeTouchable, + keyguardInteractor.isKeyguardShowing, + ) { panelTouchesEnabled, isKeyguardShowing -> + panelTouchesEnabled && !isKeyguardShowing + } + + override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow() + override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() + override fun completeDozeAnimation() {} + override fun completeVisibilityAnimation() {} +} 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 index 892b2be9ed6e..65eb22075ec7 100644 --- 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 @@ -15,8 +15,32 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.util.ui.AnimatedValue +import kotlinx.coroutines.flow.Flow + /** * View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and * AOD. */ -interface NotificationIconContainerViewModel +interface NotificationIconContainerViewModel { + /** Are changes to the icon container animated? */ + val animationsEnabled: Flow<Boolean> + + /** Should icons be rendered in "dozing" mode? */ + val isDozing: Flow<AnimatedValue<Boolean>> + + /** Is the icon container visible? */ + val isVisible: Flow<AnimatedValue<Boolean>> + + /** + * Signal completion of the [isDozing] animation; if [isDozing]'s [AnimatedValue.isAnimating] + * property was `true`, calling this method will update it to `false. + */ + fun completeDozeAnimation() + + /** + * Signal completion of the [isVisible] animation; if [isVisible]'s [AnimatedValue.isAnimating] + * property was `true`, calling this method will update it to `false. + */ + fun completeVisibilityAnimation() +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt index 197ae1a77198..dc9028d94312 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt @@ -25,6 +25,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpsysTableLogger import com.android.systemui.dump.Row import com.android.systemui.statusbar.notification.collection.NotifPipeline +import dalvik.annotation.optimization.NeverCompile import java.io.PrintWriter import javax.inject.Inject @@ -39,6 +40,7 @@ constructor(val dumpManager: DumpManager, val notificationPipeline: NotifPipelin Log.i("NotificationMemory", "Registered dumpable.") } + @NeverCompile override fun dump(pw: PrintWriter, args: Array<out String>) { val memoryUse = NotificationMemoryMeter.notificationMemoryUse(notificationPipeline.allNotifs) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index c61258b5e0ec..847d94861401 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -355,12 +355,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView AnimatorListenerAdapter animationListener) { enableAppearDrawing(true); mIsHeadsUpAnimation = isHeadsUpAnimation; - if (mDrawingAppearAnimation) { - startAppearAnimation(false /* isAppearing */, translationDirection, - delay, duration, onFinishedRunnable, animationListener); - } else if (onFinishedRunnable != null) { - onFinishedRunnable.run(); - } + startAppearAnimation(false /* isAppearing */, translationDirection, + delay, duration, onFinishedRunnable, animationListener); return 0; } @@ -369,10 +365,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView Runnable onFinishRunnable) { enableAppearDrawing(true); mIsHeadsUpAnimation = isHeadsUpAppear; - if (mDrawingAppearAnimation) { - startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay, - duration, null, null); - } + startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay, + duration, null, null); } private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index d18f9919604d..bc570f2cae35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -27,7 +27,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.Notification; -import android.app.NotificationChannel; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -40,8 +39,6 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.Trace; -import android.service.notification.StatusBarNotification; -import android.util.ArraySet; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.IndentingPrintWriter; @@ -73,15 +70,15 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; -import com.android.systemui.res.R; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.flags.ViewRefactorFlag; +import com.android.systemui.flags.RefactorFlag; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.StatusBarIconView; @@ -274,8 +271,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private OnExpandClickListener mOnExpandClickListener; private View.OnClickListener mOnFeedbackClickListener; private Path mExpandingClipPath; - private final ViewRefactorFlag mInlineReplyAnimation = - new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION); + private final RefactorFlag mInlineReplyAnimation = + RefactorFlag.forView(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION); // Listener will be called when receiving a long click event. // Use #setLongPressPosition to optionally assign positional data with the long press. @@ -772,6 +769,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** * @return if the view is in heads up state, i.e either still heads upped or it's disappearing. */ + @Override public boolean isHeadsUpState() { return mIsHeadsUp || mHeadsupDisappearRunning; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 259923170477..2a3e69b7f4d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -28,9 +28,9 @@ import android.util.IndentingPrintWriter; import android.view.View; import android.view.ViewOutlineProvider; -import com.android.systemui.res.R; import com.android.systemui.flags.Flags; -import com.android.systemui.flags.ViewRefactorFlag; +import com.android.systemui.flags.RefactorFlag; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.util.DumpUtilsKt; @@ -49,8 +49,8 @@ public abstract class ExpandableOutlineView extends ExpandableView { private float mOutlineAlpha = -1f; private boolean mAlwaysRoundBothCorners; private Path mTmpPath = new Path(); - protected final ViewRefactorFlag mImprovedHunAnimation = - new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS); + protected final RefactorFlag mImprovedHunAnimation = + RefactorFlag.forView(Flags.IMPROVED_HUN_ANIMATIONS); /** * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index 5aae48899bc9..f2f55a87ba3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -621,6 +621,10 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro return false; } + public boolean isHeadsUpState() { + return false; + } + public boolean isChildInGroup() { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java index 42c80ed22717..40897dae4c44 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java @@ -42,9 +42,8 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.Dependency; -import com.android.systemui.res.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.util.Compile; @@ -76,7 +75,9 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC final StatusBarNotification sbn, final NotificationEntry entry, final ExpandableNotificationRow row, - final AssistantFeedbackController controller) { + final AssistantFeedbackController controller, + final IStatusBarService statusBarService, + final NotificationGutsManager notificationGutsManager) { mPkg = sbn.getPackageName(); mPm = pm; mEntry = entry; @@ -84,8 +85,8 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC mRanking = entry.getRanking(); mFeedbackController = controller; mAppName = mPkg; - mStatusBarService = Dependency.get(IStatusBarService.class); - mNotificationGutsManager = Dependency.get(NotificationGutsManager.class); + mStatusBarService = statusBarService; + mNotificationGutsManager = notificationGutsManager; bindHeader(); bindPrompt(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 6d6566058aed..9e9116bd70e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -44,9 +44,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.statusbar.IStatusBarService; import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.CoreStartable; -import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -54,6 +54,7 @@ import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.shade.ShadeController; @@ -69,8 +70,8 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CentralSurfaces; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.wmshell.BubblesManager; @@ -99,6 +100,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager; private final StatusBarStateController mStatusBarStateController; + private final IStatusBarService mStatusBarService; private final DeviceProvisionedController mDeviceProvisionedController; private final AssistantFeedbackController mAssistantFeedbackController; @@ -127,7 +129,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta private final ShadeController mShadeController; private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor; private NotifGutsViewListener mGutsListener; - private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private final HeadsUpManager mHeadsUpManager; private final ActivityStarter mActivityStarter; @Inject @@ -152,9 +154,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor, NotificationLockscreenUserManager notificationLockscreenUserManager, StatusBarStateController statusBarStateController, + IStatusBarService statusBarService, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, - HeadsUpManagerPhone headsUpManagerPhone, + HeadsUpManager headsUpManager, ActivityStarter activityStarter) { mContext = context; mMainHandler = mainHandler; @@ -177,9 +180,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor; mLockscreenUserManager = notificationLockscreenUserManager; mStatusBarStateController = statusBarStateController; + mStatusBarService = statusBarService; mDeviceProvisionedController = deviceProvisionedController; mMetricsLogger = metricsLogger; - mHeadsUpManagerPhone = headsUpManagerPhone; + mHeadsUpManager = headsUpManager; mActivityStarter = activityStarter; } @@ -296,7 +300,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta if (mGutsListener != null) { mGutsListener.onGutsClose(entry); } - mHeadsUpManagerPhone.setGutsShown(row.getEntry(), false); + mHeadsUpManager.setGutsShown(row.getEntry(), false); }); View gutsView = item.getGutsView(); @@ -358,7 +362,8 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext, userHandle.getIdentifier()); - feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController); + feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController, + mStatusBarService, this); } /** @@ -676,7 +681,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta row.closeRemoteInput(); mListContainer.onHeightChanged(row, true /* needsAnimation */); mGutsMenuItem = menuItem; - mHeadsUpManagerPhone.setGutsShown(row.getEntry(), true); + mHeadsUpManager.setGutsShown(row.getEntry(), true); } }; guts.post(mOpenRunnable); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java index 0c686be0406d..e200b901e815 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java @@ -167,7 +167,7 @@ public abstract class StackScrollerDecorView extends ExpandableView { } @VisibleForTesting - boolean isSecondaryVisible() { + public boolean isSecondaryVisible() { return mIsSecondaryVisible; } @@ -179,7 +179,8 @@ public abstract class StackScrollerDecorView extends ExpandableView { return mIsVisible; } - void setDuration(int duration) { + @VisibleForTesting + public void setDuration(int duration) { mDuration = duration; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 28f0a0c5fd78..6f3cd5d52e1e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -86,12 +86,12 @@ import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.ExpandHelper; -import com.android.systemui.res.R; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.flags.ViewRefactorFlag; +import com.android.systemui.flags.RefactorFlag; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.TouchLogger; import com.android.systemui.statusbar.CommandQueue; @@ -106,12 +106,12 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; -import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; @@ -200,8 +200,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private Set<Integer> mDebugTextUsedYPositions; private final boolean mDebugRemoveAnimation; private final boolean mSensitiveRevealAnimEndabled; - private final ViewRefactorFlag mAnimatedInsets; - private final ViewRefactorFlag mShelfRefactor; + private final RefactorFlag mAnimatedInsets; + private final RefactorFlag mShelfRefactor; + + private final boolean mNewAodTransition; private int mContentHeight; private float mIntrinsicContentHeight; @@ -485,8 +487,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private ShadeController mShadeController; private Consumer<Boolean> mOnStackYChanged; - protected boolean mClearAllEnabled; - private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN; private final NotificationSectionsManager mSectionsManager; @@ -632,11 +632,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mIsSmallLandscapeLockscreenEnabled = featureFlags.isEnabled( Flags.LOCKSCREEN_ENABLE_LANDSCAPE); mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES); + mNewAodTransition = featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION); mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION); mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM); mAnimatedInsets = - new ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS); - mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); + new RefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS); + mShelfRefactor = new RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); mSectionsManager = Dependency.get(NotificationSectionsManager.class); mScreenOffAnimationController = Dependency.get(ScreenOffAnimationController.class); @@ -670,7 +671,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mDebugPaint.setStyle(Paint.Style.STROKE); mDebugPaint.setTextSize(25f); } - mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll); mGroupMembershipManager = Dependency.get(GroupMembershipManager.class); mGroupExpansionManager = Dependency.get(GroupExpansionManager.class); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); @@ -758,8 +758,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private boolean shouldShowDismissView() { - return mClearAllEnabled - && mController.hasActiveClearableNotifications(ROWS_ALL); + return mController.hasActiveClearableNotifications(ROWS_ALL); } private boolean shouldShowFooterView(boolean showDismissView) { @@ -1432,12 +1431,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @VisibleForTesting public void updateStackHeight(float endHeight, float fraction) { - // During the (AOD<=>LS) transition where dozeAmount is changing, - // apply dozeAmount to stack height instead of expansionFraction - // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep) - final float dozeAmount = mAmbientState.getDozeAmount(); - if (0f < dozeAmount && dozeAmount < 1f) { - fraction = 1f - dozeAmount; + if (!mNewAodTransition) { + // During the (AOD<=>LS) transition where dozeAmount is changing, + // apply dozeAmount to stack height instead of expansionFraction + // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep) + final float dozeAmount = mAmbientState.getDozeAmount(); + if (0f < dozeAmount && dozeAmount < 1f) { + fraction = 1f - dozeAmount; + } } mAmbientState.setStackHeight( MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION, @@ -5173,7 +5174,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable DumpUtilsKt.withIncreasedIndent( pw, () -> { - pw.println("mClearAllEnabled: " + mClearAllEnabled); pw.println( "hasActiveClearableNotifications: " + mController.hasActiveClearableNotifications( 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 66b2555a4446..50207806ecaa 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 @@ -64,7 +64,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.flags.ViewRefactorFlag; +import com.android.systemui.flags.RefactorFlag; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -99,7 +99,6 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumper; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.NotifStackController; @@ -115,10 +114,10 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor; import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; @@ -127,6 +126,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -156,7 +156,7 @@ public class NotificationStackScrollLayoutController { private final NotificationGutsManager mNotificationGutsManager; private final NotificationsController mNotificationsController; private final NotificationVisibilityProvider mVisibilityProvider; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotificationRoundnessManager mNotificationRoundnessManager; private final TunerService mTunerService; private final DeviceProvisionedController mDeviceProvisionedController; @@ -194,7 +194,7 @@ public class NotificationStackScrollLayoutController { private final GroupExpansionManager mGroupExpansionManager; private final NotifPipelineFlags mNotifPipelineFlags; - private final SeenNotificationsProvider mSeenNotificationsProvider; + private final NotificationListInteractor mNotificationListInteractor; private final KeyguardTransitionRepository mKeyguardTransitionRepo; private NotificationStackScrollLayout mView; @@ -207,7 +207,7 @@ public class NotificationStackScrollLayoutController { private boolean mIsInTransitionToAod = false; private final FeatureFlags mFeatureFlags; - private final ViewRefactorFlag mShelfRefactor; + private final RefactorFlag mShelfRefactor; private final NotificationTargetsHelper mNotificationTargetsHelper; private final SecureSettings mSecureSettings; private final NotificationDismissibilityProvider mDismissibilityProvider; @@ -631,7 +631,7 @@ public class NotificationStackScrollLayoutController { NotificationGutsManager notificationGutsManager, NotificationsController notificationsController, NotificationVisibilityProvider visibilityProvider, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, DeviceProvisionedController deviceProvisionedController, @@ -662,7 +662,7 @@ public class NotificationStackScrollLayoutController { UiEventLogger uiEventLogger, NotificationRemoteInputManager remoteInputManager, VisibilityLocationProviderDelegator visibilityLocationProviderDelegator, - SeenNotificationsProvider seenNotificationsProvider, + NotificationListInteractor notificationListInteractor, ShadeController shadeController, InteractionJankMonitor jankMonitor, StackStateLogger stackLogger, @@ -715,11 +715,11 @@ public class NotificationStackScrollLayoutController { mUiEventLogger = uiEventLogger; mRemoteInputManager = remoteInputManager; mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator; - mSeenNotificationsProvider = seenNotificationsProvider; + mNotificationListInteractor = notificationListInteractor; mShadeController = shadeController; mNotifIconAreaController = notifIconAreaController; mFeatureFlags = featureFlags; - mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); + mShelfRefactor = new RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); mNotificationTargetsHelper = notificationTargetsHelper; mSecureSettings = secureSettings; mDismissibilityProvider = dismissibilityProvider; @@ -2006,7 +2006,7 @@ public class NotificationStackScrollLayoutController { public void setNotifStats(@NonNull NotifStats notifStats) { mNotifStats = notifStats; mView.setHasFilteredOutSeenNotifications( - mSeenNotificationsProvider.getHasFilteredOutSeenNotifications()); + mNotificationListInteractor.getHasFilteredOutSeenNotifications().getValue()); updateFooter(); updateShowEmptyShadeView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index f2d5394e0aee..80f98a68b4f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -27,16 +27,16 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.keyguard.BouncerPanelExpansionCalculator; -import com.android.systemui.res.R; import com.android.systemui.animation.ShadeInterpolation; +import com.android.systemui.res.R; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.SourceType; +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; -import com.android.systemui.statusbar.notification.row.FooterView; import java.util.ArrayList; import java.util.List; @@ -142,7 +142,15 @@ public class StackScrollAlgorithm { viewState.setAlpha(1f); } else if (ambientState.isOnKeyguard()) { // Adjust alpha for wakeup to lockscreen. - viewState.setAlpha(1f - ambientState.getHideAmount()); + if (view.isHeadsUpState()) { + // Pulsing HUN should be visible on AOD and stay visible during + // AOD=>lockscreen transition + viewState.setAlpha(1f - ambientState.getHideAmount()); + } else { + // Normal notifications are hidden on AOD and should fade in during + // AOD=>lockscreen transition + viewState.setAlpha(1f - ambientState.getDozeAmount()); + } } else if (ambientState.isExpansionChanging()) { // Adjust alpha for shade open & close. float expansion = ambientState.getExpansionFraction(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt new file mode 100644 index 000000000000..f6ed8c884816 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt @@ -0,0 +1,35 @@ +/* + * 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.stack.data.repository + +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Repository for information about the current notification list. */ +@SysUISingleton +class NotificationListRepository @Inject constructor() { + private val _hasFilteredOutSeenNotifications = MutableStateFlow(false) + val hasFilteredOutSeenNotifications: StateFlow<Boolean> = + _hasFilteredOutSeenNotifications.asStateFlow() + + fun setHasFilteredOutSeenNotifications(value: Boolean) { + _hasFilteredOutSeenNotifications.value = value + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt new file mode 100644 index 000000000000..3fd68a8bdd7c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.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.notification.stack.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +/** Interactor for business logic associated with the notification stack. */ +@SysUISingleton +class NotificationListInteractor +@Inject +constructor( + private val notificationListRepository: NotificationListRepository, +) { + /** Are any already-seen notifications currently filtered out of the shade? */ + val hasFilteredOutSeenNotifications: StateFlow<Boolean> + get() = notificationListRepository.hasFilteredOutSeenNotifications + + fun setHasFilteredOutSeenNotifications(value: Boolean) { + notificationListRepository.setHasFilteredOutSeenNotifications(value) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 697d2978db0c..3877bb6660ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -28,15 +28,15 @@ import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.res.R; import com.android.systemui.dagger.NightDisplayListenerModule; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.ReduceBrightColorsController; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.external.CustomTile; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.DataSaverController; @@ -461,8 +461,8 @@ public class AutoTileManager implements UserAwareController { }; @VisibleForTesting - protected SettingObserver getSecureSettingForKey(String key) { - for (SettingObserver s : mAutoAddSettingList) { + protected UserSettingObserver getSecureSettingForKey(String key) { + for (UserSettingObserver s : mAutoAddSettingList) { if (Objects.equals(key, s.getKey())) { return s; } @@ -476,7 +476,7 @@ public class AutoTileManager implements UserAwareController { * When the setting changes to a value different from 0, if the tile has not been auto added * before, it will be added and the listener will be stopped. */ - private class AutoAddSetting extends SettingObserver { + private class AutoAddSetting extends UserSettingObserver { private final String mSpec; AutoAddSetting( 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 3e2f10dd1a0c..ed1c4ece1dbf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -220,6 +220,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.ExtensionController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -236,6 +237,10 @@ import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.SplashscreenContentDrawer; import com.android.wm.shell.startingsurface.StartingSurface; +import dagger.Lazy; + +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; @@ -247,8 +252,6 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; -import dagger.Lazy; - /** * A class handling initialization and coordination between some of the key central surfaces in * System UI: The notification shade, the keyguard (lockscreen), and the status bar. @@ -417,7 +420,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final NotificationWakeUpCoordinator mWakeUpCoordinator; private final KeyguardBypassController mKeyguardBypassController; private final KeyguardStateController mKeyguardStateController; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; @@ -611,7 +614,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { NotificationWakeUpCoordinator notificationWakeUpCoordinator, KeyguardBypassController keyguardBypassController, KeyguardStateController keyguardStateController, - HeadsUpManagerPhone headsUpManagerPhone, + HeadsUpManager headsUpManager, DynamicPrivacyController dynamicPrivacyController, FalsingManager falsingManager, FalsingCollector falsingCollector, @@ -718,7 +721,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mWakeUpCoordinator = notificationWakeUpCoordinator; mKeyguardBypassController = keyguardBypassController; mKeyguardStateController = keyguardStateController; - mHeadsUpManager = headsUpManagerPhone; + mHeadsUpManager = headsUpManager; mBackActionInteractor = backActionInteractor; mKeyguardIndicationController = keyguardIndicationController; mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; @@ -1088,11 +1091,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { void initShadeVisibilityListener() { mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() { @Override - public void visibilityChanged(boolean visible) { - onShadeVisibilityChanged(visible); - } - - @Override public void expandedVisibleChanged(boolean expandedVisible) { if (expandedVisible) { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); @@ -1195,7 +1193,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }); mStatusBarInitializer.initializeStatusBar(); - mStatusBarTouchableRegionManager.setup(this, getNotificationShadeWindowView()); + mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView()); createNavigationBar(result); @@ -1767,6 +1765,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } } + @NeverCompile @Override public void dump(PrintWriter pwOriginal, String[] args) { IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal); @@ -2603,7 +2602,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mShouldDelayWakeUpAnimation); updateIsKeyguard(); + // TODO(b/301913237): can't delay transition if config_displayBlanksAfterDoze=true, + // otherwise, the clock will flicker during LOCKSCREEN_TRANSITION_FROM_AOD mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn() + && !mDozeParameters.getDisplayNeedsBlanking() && mFeatureFlags.isEnabled( Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD); if (!mShouldDelayLockscreenTransitionFromAod) { @@ -2688,7 +2690,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { !mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive()); mShadeSurface.setTouchAndAnimationDisabled(disabled); - mNotificationIconAreaController.setAnimationsEnabled(!disabled); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mNotificationIconAreaController.setAnimationsEnabled(!disabled); + } } final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @@ -2844,13 +2848,16 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScrimController.setExpansionAffectsAlpha(!unlocking); if (mAlternateBouncerInteractor.isVisibleState()) { - if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) - && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED - || mTransitionToFullShadeProgress > 0f)) { - mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE); - } else { - mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); + if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) + && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED + || mTransitionToFullShadeProgress > 0f)) { + mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE); + } else { + mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); + } } + // This will cancel the keyguardFadingAway animation if it is running. We need to do // this as otherwise it can remain pending and leave keyguard in a weird state. mUnlockScrimCallback.onCancelled(); @@ -2913,8 +2920,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected boolean mDeviceInteractive; - protected boolean mVisible; - protected DevicePolicyManager mDevicePolicyManager; private final PowerManager mPowerManager; protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -3032,16 +3037,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { afterKeyguardGone); } - private void onShadeVisibilityChanged(boolean visible) { - if (mVisible != visible) { - mVisible = visible; - if (visible) { - DejankUtils.notifyRendererOfExpensiveFrame( - getNotificationShadeWindowView(), "onShadeVisibilityChanged"); - } - } - } - private void clearNotificationEffects() { try { mBarService.clearNotificationEffects(); @@ -3163,7 +3158,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // TODO: Bring these out of CentralSurfaces. mUserInfoControllerImpl.onDensityOrFontScaleChanged(); mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); - mHeadsUpManager.onDensityOrFontScaleChanged(); } @Override @@ -3200,7 +3194,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // down on the lockscreen), clear notification LED, vibration, // ringing. // Other transitions are covered in WindowRootViewVisibilityInteractor. - if (mVisible && (newState == StatusBarState.SHADE_LOCKED + if (mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible().getValue() + && (newState == StatusBarState.SHADE_LOCKED || mStatusBarStateController.goingToFullShade())) { clearNotificationEffects(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index 4849f64659d0..d3d11ea4a9f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.Assert; @@ -80,7 +81,7 @@ public final class DozeServiceHost implements DozeHost { private final WakefulnessLifecycle mWakefulnessLifecycle; private final SysuiStatusBarStateController mStatusBarStateController; private final DeviceProvisionedController mDeviceProvisionedController; - private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private final HeadsUpManager mHeadsUpManager; private final BatteryController mBatteryController; private final ScrimController mScrimController; private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; @@ -106,7 +107,7 @@ public final class DozeServiceHost implements DozeHost { WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, DeviceProvisionedController deviceProvisionedController, - HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController, + HeadsUpManager headsUpManager, BatteryController batteryController, ScrimController scrimController, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, Lazy<AssistManager> assistManagerLazy, @@ -123,7 +124,7 @@ public final class DozeServiceHost implements DozeHost { mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mDeviceProvisionedController = deviceProvisionedController; - mHeadsUpManagerPhone = headsUpManagerPhone; + mHeadsUpManager = headsUpManager; mBatteryController = batteryController; mScrimController = scrimController; mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; @@ -135,7 +136,7 @@ public final class DozeServiceHost implements DozeHost { mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; mAuthController = authController; mNotificationIconAreaController = notificationIconAreaController; - mHeadsUpManagerPhone.addListener(mOnHeadsUpChangedListener); + mHeadsUpManager.addListener(mOnHeadsUpChangedListener); mDozeInteractor = dozeInteractor; } @@ -337,8 +338,8 @@ public final class DozeServiceHost implements DozeHost { if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH) { mScrimController.setWakeLockScreenSensorActive(true); } - if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) { - mHeadsUpManagerPhone.extendHeadsUp(); + if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) { + mHeadsUpManager.extendHeadsUp(); } else { mDozeScrimController.extendPulse(); } @@ -493,7 +494,7 @@ public final class DozeServiceHost implements DozeHost { mDozeScrimController.cancelPendingPulseTimeout(); } } - if (!isHeadsUp && !mHeadsUpManagerPhone.hasNotifications()) { + if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) { // There are no longer any notifications to show. We should end the // pulse now. stopPulsing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 270c40e801cd..c493eeda7077 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -26,9 +26,9 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ViewClippingUtil; -import com.android.systemui.res.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeHeadsUpTracker; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.ViewController; @@ -70,7 +71,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar private static final SourceType HEADS_UP = SourceType.from("HeadsUp"); private static final SourceType PULSING = SourceType.from("Pulsing"); private final NotificationIconAreaController mNotificationIconAreaController; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotificationStackScrollLayoutController mStackScrollerController; private final DarkIconDispatcher mDarkIconDispatcher; @@ -108,7 +109,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar @Inject public HeadsUpAppearanceController( NotificationIconAreaController notificationIconAreaController, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, StatusBarStateController stateController, PhoneStatusBarTransitions phoneStatusBarTransitions, KeyguardBypassController bypassController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index a5ea1426164b..6b4382f731ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -29,11 +29,11 @@ import androidx.collection.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.SystemBarUtils; -import com.android.systemui.Dumpable; -import com.android.systemui.res.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -42,10 +42,12 @@ import com.android.systemui.statusbar.notification.collection.provider.VisualSta import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.AnimationStateHandler; +import com.android.systemui.statusbar.policy.BaseHeadsUpManager; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange; import java.io.PrintWriter; import java.util.ArrayList; @@ -53,11 +55,11 @@ import java.util.HashSet; import java.util.List; import java.util.Stack; -/** - * A implementation of HeadsUpManager for phone and car. - */ -public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, - OnHeadsUpChangedListener { +import javax.inject.Inject; + +/** A implementation of HeadsUpManager for phone. */ +@SysUISingleton +public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUpChangedListener { private static final String TAG = "HeadsUpManagerPhone"; @VisibleForTesting @@ -102,7 +104,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, /////////////////////////////////////////////////////////////////////////////////////////////// // Constructor: - + @Inject public HeadsUpManagerPhone(@NonNull final Context context, HeadsUpManagerLogger logger, StatusBarStateController statusBarStateController, @@ -154,7 +156,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, /** * Add a listener to receive callbacks onHeadsUpGoingAway */ - void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) { + @Override + public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) { mHeadsUpPhoneListeners.add(listener); } @@ -162,7 +165,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * Gets the touchable region needed for heads up notifications. Returns null if no touchable * region is required (ie: no heads up notification currently exists). */ - @Nullable Region getTouchableRegion() { + @Override + public @Nullable Region getTouchableRegion() { NotificationEntry topEntry = getTopEntry(); // This call could be made in an inconsistent state while the pinnedMode hasn't been @@ -197,8 +201,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * @param key the key of the touched notification * @return whether the touch is invalid and should be discarded */ - boolean shouldSwallowClick(@NonNull String key) { - HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); + @Override + public boolean shouldSwallowClick(@NonNull String key) { + BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); return entry != null && mClock.currentTimeMillis() < entry.mPostTime; } @@ -238,7 +243,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * Set that we are exiting the headsUp pinned mode, but some notifications might still be * animating out. This is used to keep the touchable regions in a reasonable state. */ - void setHeadsUpGoingAway(boolean headsUpGoingAway) { + @Override + public void setHeadsUpGoingAway(boolean headsUpGoingAway) { if (headsUpGoingAway != mHeadsUpGoingAway) { mHeadsUpGoingAway = headsUpGoingAway; for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) { @@ -247,7 +253,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } } - boolean isHeadsUpGoingAway() { + @Override + public boolean isHeadsUpGoingAway() { return mHeadsUpGoingAway; } @@ -260,8 +267,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, public void setRemoteInputActive( @NonNull NotificationEntry entry, boolean remoteInputActive) { HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey()); - if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) { - headsUpEntry.remoteInputActive = remoteInputActive; + if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) { + headsUpEntry.mRemoteInputActive = remoteInputActive; if (remoteInputActive) { headsUpEntry.removeAutoRemovalCallbacks("setRemoteInputActive(true)"); } else { @@ -313,6 +320,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mSwipedOutKeys.add(key); } + @Override public boolean removeNotification(@NonNull String key, boolean releaseImmediately, boolean animate) { if (animate) { @@ -411,7 +419,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, /////////////////////////////////////////////////////////////////////////////////////////////// // HeadsUpEntryPhone: - protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry { + protected class HeadsUpEntryPhone extends BaseHeadsUpManager.HeadsUpEntry { private boolean mGutsShownPinned; @@ -459,11 +467,11 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, @Override public void setExpanded(boolean expanded) { - if (this.expanded == expanded) { + if (this.mExpanded == expanded) { return; } - this.expanded = expanded; + this.mExpanded = expanded; if (expanded) { removeAutoRemovalCallbacks("setExpanded(true)"); } else { @@ -504,21 +512,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } } - public interface AnimationStateHandler { - void setHeadsUpGoingAwayAnimationsAllowed(boolean allowed); - } - - /** - * Listener to register for HeadsUpNotification Phone changes. - */ - public interface OnHeadsUpPhoneListenerChange { - /** - * Called when a heads up notification is 'going away' or no longer 'going away'. - * See {@link HeadsUpManagerPhone#setHeadsUpGoingAway}. - */ - void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway); - } - private final StateListener mStatusBarStateListener = new StateListener() { @Override public void onStateChanged(int newState) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt new file mode 100644 index 000000000000..0d0f2cd50a29 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt @@ -0,0 +1,11 @@ +package com.android.systemui.statusbar.phone + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.HeadsUpManager +import dagger.Binds +import dagger.Module + +@Module +interface HeadsUpModule { + @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerPhone): HeadsUpManager +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index dcbaac20eacc..198272e9b162 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -26,13 +26,14 @@ import com.android.systemui.Gefingerpoken; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.policy.HeadsUpManager; /** * A helper class to handle touches on the heads-up views. */ public class HeadsUpTouchHelper implements Gefingerpoken { - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final IStatusBarService mStatusBarService; private final Callback mCallback; private int mTrackingPointer; @@ -45,7 +46,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private final HeadsUpNotificationViewController mPanel; private ExpandableNotificationRow mPickedChild; - public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager, + public HeadsUpTouchHelper(HeadsUpManager headsUpManager, IStatusBarService statusBarService, Callback callback, HeadsUpNotificationViewController notificationPanelView) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java index 660aa3f641c4..5553270ed0ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java @@ -15,6 +15,8 @@ */ package com.android.systemui.statusbar.phone; +import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION; + import android.content.Context; import android.content.res.Resources; import android.graphics.Color; @@ -34,16 +36,16 @@ 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.res.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.flags.RefactorFlag; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationMediaManager; @@ -107,7 +109,9 @@ public class LegacyNotificationIconAreaControllerImpl implements private final ArrayList<Rect> mTintAreas = new ArrayList<>(); private final Context mContext; - private final ViewRefactorFlag mShelfRefactor; + private final RefactorFlag mShelfRefactor; + + private final boolean mNewAodTransition; private int mAodIconAppearTranslation; @@ -146,7 +150,8 @@ public class LegacyNotificationIconAreaControllerImpl implements mContrastColorUtil = ContrastColorUtil.getInstance(context); mContext = context; mStatusBarStateController = statusBarStateController; - mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); + mShelfRefactor = new RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); + mNewAodTransition = featureFlags.isEnabled(NEW_AOD_TRANSITION); mStatusBarStateController.addCallback(this); mMediaManager = notificationMediaManager; mDozeParameters = dozeParameters; @@ -609,9 +614,11 @@ public class LegacyNotificationIconAreaControllerImpl implements boolean animate = true; if (!mBypassController.getBypassEnabled()) { animate = mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking(); - // We only want the appear animations to happen when the notifications get fully hidden, - // since otherwise the unhide animation overlaps - animate &= fullyHidden; + if (!mNewAodTransition) { + // We only want the appear animations to happen when the notifications get fully + // hidden, since otherwise the unhide animation overlaps + animate &= fullyHidden; + } } updateAodIconsVisibility(animate, false /* force */); updateAodNotificationIcons(); @@ -647,23 +654,34 @@ public class LegacyNotificationIconAreaControllerImpl implements mAodIconsVisible = visible; mAodIcons.animate().cancel(); if (animate) { - boolean wasFullyInvisible = mAodIcons.getVisibility() != View.VISIBLE; - if (mAodIconsVisible) { - if (wasFullyInvisible) { - // No fading here, let's just appear the icons instead! - mAodIcons.setVisibility(View.VISIBLE); - mAodIcons.setAlpha(1.0f); - appearAodIcons(); + if (mNewAodTransition) { + // Let's make sure the icon are translated to 0, since we cancelled it above + animateInAodIconTranslation(); + if (mAodIconsVisible) { + CrossFadeHelper.fadeIn(mAodIcons); + } else { + CrossFadeHelper.fadeOut(mAodIcons); + } + } else { + boolean wasFullyInvisible = mAodIcons.getVisibility() != View.VISIBLE; + if (mAodIconsVisible) { + if (wasFullyInvisible) { + // No fading here, let's just appear the icons instead! + mAodIcons.setVisibility(View.VISIBLE); + mAodIcons.setAlpha(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(mAodIcons); + } } 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(mAodIcons); + CrossFadeHelper.fadeOut(mAodIcons); } - } else { - // Let's make sure the icon are translated to 0, since we cancelled it above - animateInAodIconTranslation(); - CrossFadeHelper.fadeOut(mAodIcons); } } else { mAodIcons.setAlpha(1.0f); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 7cbaf63bc6db..b15c0fdd5a4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -33,6 +33,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.collection.ArrayMap; @@ -624,12 +625,32 @@ public class NotificationIconContainer extends ViewGroup { } public void setDozing(boolean dozing, boolean fade, long delay) { + setDozing(dozing, fade, delay, /* endRunnable= */ null); + } + + public void setDozing(boolean dozing, boolean fade, long delay, + @Nullable Runnable endRunnable) { mDozing = dozing; mDisallowNextAnimation |= !fade; - for (int i = 0; i < getChildCount(); i++) { + final int childCount = getChildCount(); + // Track all the child invocations of setDozing, invoking the top-level endRunnable once + // they have all completed. + final Runnable onChildCompleted = endRunnable == null ? null : new Runnable() { + private int mPendingCallbacks = childCount; + + @Override + public void run() { + if (--mPendingCallbacks == 0) { + endRunnable.run(); + } + } + }; + for (int i = 0; i < childCount; i++) { View view = getChildAt(i); if (view instanceof StatusBarIconView) { - ((StatusBarIconView) view).setDozing(dozing, fade, delay); + ((StatusBarIconView) view).setDozing(dozing, fade, delay, onChildCompleted); + } else if (onChildCompleted != null) { + onChildCompleted.run(); } } } 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 4b39854e43ff..235ed25e2ea1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java @@ -24,6 +24,7 @@ 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.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -39,7 +40,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, private final ShadeViewController mShadeViewController; private final NotificationStackScrollLayoutController mNsslController; private final KeyguardBypassController mKeyguardBypassController; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final StatusBarStateController mStatusBarStateController; private final NotificationRemoteInputManager mNotificationRemoteInputManager; @@ -50,7 +51,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, ShadeViewController shadeViewController, NotificationStackScrollLayoutController nsslController, KeyguardBypassController keyguardBypassController, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, StatusBarStateController statusBarStateController, NotificationRemoteInputManager notificationRemoteInputManager) { mNotificationShadeWindowController = notificationShadeWindowController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index eedf35f1e9d4..3adf3385e3cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -296,7 +296,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>(); private boolean mIsBackAnimationEnabled; - private final boolean mUdfpsNewTouchDetectionEnabled; private final UdfpsOverlayInteractor mUdfpsOverlayInteractor; private final ActivityStarter mActivityStarter; @@ -398,7 +397,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mAlternateBouncerInteractor = alternateBouncerInteractor; mIsBackAnimationEnabled = featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM); - mUdfpsNewTouchDetectionEnabled = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION); mUdfpsOverlayInteractor = udfpsOverlayInteractor; mActivityStarter = activityStarter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; @@ -1573,6 +1571,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * notification shade's child views. */ public boolean shouldInterceptTouchEvent(MotionEvent event) { + if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + return false; + } return mAlternateBouncerInteractor.isVisibleState(); } @@ -1581,13 +1582,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * showing. */ public boolean onTouch(MotionEvent event) { + if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + return false; + } + boolean handleTouch = shouldInterceptTouchEvent(event); if (handleTouch) { final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN; final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch() && event.getActionMasked() == MotionEvent.ACTION_UP; final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade = - mUdfpsNewTouchDetectionEnabled && mKeyguardUpdateManager.isUdfpsEnrolled(); + mKeyguardUpdateManager.isUdfpsEnrolled(); final boolean actionOutsideShouldDismissAlternateBouncer = event.getActionMasked() == MotionEvent.ACTION_OUTSIDE && !udfpsOverlayWillForwardEventsOutsideNotificationShade; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 053c27ccae3f..dbee080f238d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -76,6 +76,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.wmshell.BubblesManager; @@ -102,7 +103,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final Executor mUiBgExecutor; private final NotificationVisibilityProvider mVisibilityProvider; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final ActivityStarter mActivityStarter; private final NotificationClickNotifier mClickNotifier; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -141,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit Handler mainThreadHandler, Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, ActivityStarter activityStarter, NotificationClickNotifier clickNotifier, StatusBarKeyguardViewManager statusBarKeyguardViewManager, @@ -213,12 +214,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit */ @Override public void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row) { - mLogger.logStartingActivityFromClick(entry); + mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(), + mKeyguardStateController.isVisible(), + mNotificationShadeWindowController.getPanelExpanded()); if (mRemoteInputManager.isRemoteInputActive(entry)) { // We have an active remote input typed and the user clicked on the notification. // this was probably unintentional, so we're closing the edit text instead. mRemoteInputManager.closeRemoteInputs(); + mLogger.logCloseRemoteInput(entry); return; } Notification notification = entry.getSbn().getNotification(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt index d07378e86ced..4211cabcbc56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt @@ -30,11 +30,16 @@ import javax.inject.Inject class StatusBarNotificationActivityStarterLogger @Inject constructor( @NotifInteractionLog private val buffer: LogBuffer ) { - fun logStartingActivityFromClick(entry: NotificationEntry) { + fun logStartingActivityFromClick(entry: NotificationEntry, isHeadsUpState: Boolean, + isKeyguardVisible: Boolean, isPanelExpanded: Boolean) { buffer.log(TAG, DEBUG, { str1 = entry.logKey + bool1 = isHeadsUpState + bool2 = isKeyguardVisible + bool3 = isPanelExpanded }, { - "(1/5) onNotificationClicked: $str1" + "(1/5) onNotificationClicked: $str1 isHeadsUpState: $bool1 " + + "isKeyguardVisible: $bool2 isPanelExpanded: $bool3" }) } @@ -72,6 +77,14 @@ class StatusBarNotificationActivityStarterLogger @Inject constructor( }) } + fun logCloseRemoteInput(entry: NotificationEntry) { + buffer.log(TAG, DEBUG, { + str1 = entry.logKey + }, { + "Closing remote input for $str1" + }) + } + fun logExpandingBubble(entry: NotificationEntry) { buffer.log(TAG, DEBUG, { str1 = entry.logKey diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index fb7f9d1ec19b..2d14f6b3c508 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -31,11 +31,11 @@ import android.view.View; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.InitController; -import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.res.R; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.ShadeViewController; @@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; @@ -76,7 +77,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu private final NotificationMediaManager mMediaManager; private final NotificationGutsManager mGutsManager; private final ShadeViewController mNotificationPanel; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final AboveShelfObserver mAboveShelfObserver; private final DozeScrimController mDozeScrimController; private final NotificationsInteractor mNotificationsInteractor; @@ -98,7 +99,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu Context context, ShadeViewController panel, QuickSettingsController quickSettingsController, - HeadsUpManagerPhone headsUp, + HeadsUpManager headsUp, NotificationShadeWindowView statusBarWindow, ActivityStarter activityStarter, NotificationStackScrollLayoutController stackScrollerController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index 40bd8d3446b5..ba73c1098637 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -31,15 +31,18 @@ import android.view.WindowInsets; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.ScreenDecorations; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.kotlin.JavaAdapter; @@ -58,13 +61,12 @@ public final class StatusBarTouchableRegionManager implements Dumpable { private static final String TAG = "TouchableRegionManager"; private final Context mContext; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotificationShadeWindowController mNotificationShadeWindowController; private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private boolean mIsStatusBarExpanded = false; private boolean mShouldAdjustInsets = false; - private CentralSurfaces mCentralSurfaces; private View mNotificationShadeWindowView; private View mNotificationPanelView; private boolean mForceCollapsedUntilLayout = false; @@ -72,6 +74,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable { private Region mTouchableRegion = new Region(); private int mDisplayCutoutTouchableRegionSize; private int mStatusBarHeight; + private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener; @@ -80,12 +84,14 @@ public final class StatusBarTouchableRegionManager implements Dumpable { Context context, NotificationShadeWindowController notificationShadeWindowController, ConfigurationController configurationController, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, ShadeExpansionStateManager shadeExpansionStateManager, Provider<SceneInteractor> sceneInteractor, Provider<JavaAdapter> javaAdapter, SceneContainerFlags sceneContainerFlags, - UnlockedScreenOffAnimationController unlockedScreenOffAnimationController + UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + PrimaryBouncerInteractor primaryBouncerInteractor, + AlternateBouncerInteractor alternateBouncerInteractor ) { mContext = context; initResources(); @@ -128,13 +134,12 @@ public final class StatusBarTouchableRegionManager implements Dumpable { this::onShadeExpansionFullyChanged); } + mPrimaryBouncerInteractor = primaryBouncerInteractor; + mAlternateBouncerInteractor = alternateBouncerInteractor; mOnComputeInternalInsetsListener = this::onComputeInternalInsets; } - protected void setup( - @NonNull CentralSurfaces centralSurfaces, - @NonNull View notificationShadeWindowView) { - mCentralSurfaces = centralSurfaces; + protected void setup(@NonNull View notificationShadeWindowView) { mNotificationShadeWindowView = notificationShadeWindowView; mNotificationPanelView = mNotificationShadeWindowView.findViewById(R.id.notification_panel); } @@ -260,7 +265,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable { // since we don't want stray touches to go through the light reveal scrim to whatever is // underneath. return mIsStatusBarExpanded - || mCentralSurfaces.isBouncerShowing() + || mPrimaryBouncerInteractor.isShowing().getValue() + || mAlternateBouncerInteractor.isVisibleState() || mUnlockedScreenOffAnimationController.isAnimationPlaying(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index babd435c62ba..63c022c5bb6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -38,11 +38,11 @@ import com.android.app.animation.Interpolators; import com.android.app.animation.InterpolatorsAndroidX; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; @@ -74,8 +74,6 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; -import kotlin.Unit; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -85,6 +83,7 @@ import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; +import kotlin.Unit; /** * Contains the collapsed status bar and handles hiding/showing based on disable flags @@ -279,7 +278,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mDumpManager.registerDumpable(getClass().getSimpleName(), this); - mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this); + mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create( + (PhoneStatusBarView) getView()); mStatusBarFragmentComponent.init(); mStartableStates.clear(); for (Startable startable : mStatusBarFragmentComponent.getStartables()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java index d9a58442d856..0618abbf00d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java @@ -27,11 +27,11 @@ import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.StatusBarDemoMode; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; -import java.util.Set; - import dagger.BindsInstance; import dagger.Subcomponent; +import java.util.Set; + /** * A subcomponent that gets re-created each time we create a new {@link CollapsedStatusBarFragment}. * @@ -54,7 +54,7 @@ public interface StatusBarFragmentComponent { @Subcomponent.Factory interface Factory { StatusBarFragmentComponent create( - @BindsInstance CollapsedStatusBarFragment collapsedStatusBarFragment); + @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index 6ef877bd57bf..3741f143dd8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -19,15 +19,14 @@ package com.android.systemui.statusbar.phone.fragment.dagger; import android.view.View; import android.view.ViewStub; -import com.android.systemui.res.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.dagger.qualifiers.RootView; +import com.android.systemui.res.R; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarLocation; -import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -51,15 +50,6 @@ public interface StatusBarFragmentModule { /** */ @Provides - @RootView - @StatusBarFragmentScope - static PhoneStatusBarView providePhoneStatusBarView( - CollapsedStatusBarFragment collapsedStatusBarFragment) { - return (PhoneStatusBarView) collapsedStatusBarFragment.getView(); - } - - /** */ - @Provides @StatusBarFragmentScope static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.battery); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt index 8ff9198da119..8862c77c9c4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.pipeline.airplane.data.repository import android.os.Handler -import android.os.UserHandle import android.provider.Settings.Global import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -66,13 +65,7 @@ constructor( override val isAirplaneMode: StateFlow<Boolean> = conflatedCallbackFlow { val observer = - object : - SettingObserver( - globalSettings, - bgHandler, - Global.AIRPLANE_MODE_ON, - UserHandle.USER_ALL - ) { + object : SettingObserver(globalSettings, bgHandler, Global.AIRPLANE_MODE_ON) { override fun handleValueChanged(value: Int, observedChange: Boolean) { trySend(value == 1) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt index 9b404f187c88..e9e52a2397e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt @@ -165,7 +165,7 @@ constructor( } wifiPickerTracker = - wifiPickerTrackerFactory.create(lifecycle, callback).apply { + wifiPickerTrackerFactory.create(lifecycle, callback, "WifiRepository").apply { // By default, [WifiPickerTracker] will scan to see all available wifi // networks in the area. Because SysUI only needs to display the // **connected** network, we don't need scans to be running (and in fact, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java index e59ec044bece..cec76f3140cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -34,8 +34,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.EventLogTags; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.res.R; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -47,7 +47,8 @@ import java.io.PrintWriter; * A manager which handles heads up notifications which is a special mode where * they simply peek from the top of the screen. */ -public abstract class HeadsUpManager extends AlertingNotificationManager { +public abstract class BaseHeadsUpManager extends AlertingNotificationManager implements + HeadsUpManager { private static final String TAG = "HeadsUpManager"; private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; @@ -81,7 +82,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } } - public HeadsUpManager(@NonNull final Context context, + public BaseHeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger, @Main Handler handler, AccessibilityManagerWrapper accessibilityManagerWrapper, @@ -131,6 +132,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { mListeners.remove(listener); } + /** Updates the notification with the given key. */ public void updateNotification(@NonNull String key, boolean alert) { super.updateNotification(key, alert); HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); @@ -146,7 +148,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { // the NotificationEntry into AlertingNotificationManager's mAlertEntries map. return hasFullScreenIntent(entry); } - return hasFullScreenIntent(entry) && !headsUpEntry.wasUnpinned; + return hasFullScreenIntent(entry) && !headsUpEntry.mWasUnpinned; } protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) { @@ -154,11 +156,11 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } protected void setEntryPinned( - @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) { + @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) { mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned); NotificationEntry entry = headsUpEntry.mEntry; if (!isPinned) { - headsUpEntry.wasUnpinned = true; + headsUpEntry.mWasUnpinned = true; } if (entry.isRowPinned() != isPinned) { entry.setRowPinned(isPinned); @@ -292,10 +294,12 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { mUser = user; } + /** Returns the ID of the current user. */ public int getUser() { return mUser; } + @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("HeadsUpManager state:"); dumpInternal(pw, args); @@ -309,9 +313,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { for (AlertEntry entry: mAlertEntries.values()) { pw.print(" HeadsUpEntry="); pw.println(entry.mEntry); } - int N = mSnoozedPackages.size(); - pw.println(" snoozed packages: " + N); - for (int i = 0; i < N; i++) { + int n = mSnoozedPackages.size(); + pw.println(" snoozed packages: " + n); + for (int i = 0; i < n; i++) { pw.print(" "); pw.print(mSnoozedPackages.valueAt(i)); pw.print(", "); pw.println(mSnoozedPackages.keyAt(i)); } @@ -399,20 +403,20 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * * @param entry the entry that might be indirectly removed by the user's action * - * @see com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator#mActionPressListener + * @see HeadsUpCoordinator#mActionPressListener * @see #canRemoveImmediately(String) */ public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); if (headsUpEntry != null) { - headsUpEntry.userActionMayIndirectlyRemove = true; + headsUpEntry.mUserActionMayIndirectlyRemove = true; } } @Override public boolean canRemoveImmediately(@NonNull String key) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); - if (headsUpEntry != null && headsUpEntry.userActionMayIndirectlyRemove) { + if (headsUpEntry != null && headsUpEntry.mUserActionMayIndirectlyRemove) { return true; } return super.canRemoveImmediately(key); @@ -424,9 +428,6 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { return new HeadsUpEntry(); } - public void onDensityOrFontScaleChanged() { - } - /** * Determines if the notification is for a critical call that must display on top of an active * input notification. @@ -445,16 +446,16 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * lifecycle automatically when created. */ protected class HeadsUpEntry extends AlertEntry { - public boolean remoteInputActive; - public boolean userActionMayIndirectlyRemove; + public boolean mRemoteInputActive; + public boolean mUserActionMayIndirectlyRemove; - protected boolean expanded; - protected boolean wasUnpinned; + protected boolean mExpanded; + protected boolean mWasUnpinned; @Override public boolean isSticky() { - return (mEntry.isRowPinned() && expanded) - || remoteInputActive + return (mEntry.isRowPinned() && mExpanded) + || mRemoteInputActive || hasFullScreenIntent(mEntry); } @@ -490,9 +491,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { return 1; } - if (remoteInputActive && !headsUpEntry.remoteInputActive) { + if (mRemoteInputActive && !headsUpEntry.mRemoteInputActive) { return -1; - } else if (!remoteInputActive && headsUpEntry.remoteInputActive) { + } else if (!mRemoteInputActive && headsUpEntry.mRemoteInputActive) { return 1; } @@ -500,14 +501,14 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } public void setExpanded(boolean expanded) { - this.expanded = expanded; + this.mExpanded = expanded; } @Override public void reset() { super.reset(); - expanded = false; - remoteInputActive = false; + mExpanded = false; + mRemoteInputActive = false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt new file mode 100644 index 000000000000..21acfb41f10c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt @@ -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.systemui.statusbar.policy + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** + * A [Flow] that emits whenever screen density or font scale has changed. + * + * @see ConfigurationController.ConfigurationListener.onDensityOrFontScaleChanged + */ +val ConfigurationController.onDensityOrFontScaleChanged: Flow<Unit> + get() = + ConflatedCallbackFlow.conflatedCallbackFlow { + val listener = + object : ConfigurationController.ConfigurationListener { + override fun onDensityOrFontScaleChanged() { + trySend(Unit) + } + } + addCallback(listener) + awaitClose { removeCallback(listener) } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt index 8c61ada3f8ef..8b20283749c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt @@ -62,7 +62,7 @@ open class DeviceProvisionedControllerImpl @Inject constructor( } private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED) - private val frpActiveUri = secureSettings.getUriFor(Settings.Secure.SECURE_FRP_MODE) + private val frpActiveUri = globalSettings.getUriFor(Settings.Global.SECURE_FRP_MODE) private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE) private val deviceProvisioned = AtomicBoolean(false) @@ -148,7 +148,7 @@ open class DeviceProvisionedControllerImpl @Inject constructor( .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) } if (updateFrp) { - frpActive.set(globalSettings.getInt(Settings.Secure.SECURE_FRP_MODE, 0) != 0) + frpActive.set(globalSettings.getInt(Settings.Global.SECURE_FRP_MODE, 0) != 0) } synchronized(lock) { if (updateUser == ALL_USERS) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt new file mode 100644 index 000000000000..d9527fe98b1a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt @@ -0,0 +1,245 @@ +package com.android.systemui.statusbar.policy + +import android.graphics.Region +import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import dagger.Binds +import dagger.Module +import java.io.PrintWriter +import java.util.stream.Stream +import javax.inject.Inject + +/** + * A manager which handles heads up notifications which is a special mode where they simply peek + * from the top of the screen. + */ +interface HeadsUpManager : Dumpable { + /** The stream of all current notifications managed by this manager. */ + val allEntries: Stream<NotificationEntry> + + /** Add a listener to receive callbacks onHeadsUpGoingAway. */ + fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) + + /** Adds an OnHeadUpChangedListener to observe events. */ + fun addListener(listener: OnHeadsUpChangedListener) + + fun addSwipedOutNotification(key: String) + + /** + * Whether or not the alert can be removed currently. If it hasn't been on screen long enough it + * should not be removed unless forced + * + * @param key the key to check if removable + * @return true if the alert entry can be removed + */ + fun canRemoveImmediately(key: String): Boolean + + /** + * Compare two entries and decide how they should be ranked. + * + * @return -1 if the first argument should be ranked higher than the second, 1 if the second one + * should be ranked higher and 0 if they are equal. + */ + fun compare(a: NotificationEntry?, b: NotificationEntry?): Int + /** + * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts + * longer. + */ + fun extendHeadsUp() + + /** Returns when a HUN entry should be removed in milliseconds from now. */ + fun getEarliestRemovalTime(key: String?): Long + + /** Returns the top Heads Up Notification, which appears to show at first. */ + fun getTopEntry(): NotificationEntry? + + /** + * Gets the touchable region needed for heads up notifications. Returns null if no touchable + * region is required (ie: no heads up notification currently exists). + */ + fun getTouchableRegion(): Region? + + /** + * Whether or not there are any active alerting notifications. + * + * @return true if there is an alert, false otherwise + */ + fun hasNotifications(): Boolean = false + + /** Returns whether there are any pinned Heads Up Notifications or not. */ + fun hasPinnedHeadsUp(): Boolean + + /** Returns whether or not the given notification is alerting and managed by this manager. */ + fun isAlerting(key: String): Boolean + + fun isHeadsUpGoingAway(): Boolean + + /** Returns if the given notification is snoozed or not. */ + fun isSnoozed(packageName: String): Boolean + + /** Returns whether the entry is (pinned and expanded) or (has an active remote input). */ + fun isSticky(key: String?): Boolean + + fun isTrackingHeadsUp(): Boolean + + fun onExpandingFinished() + + /** Removes the OnHeadUpChangedListener from the observer list. */ + fun removeListener(listener: OnHeadsUpChangedListener) + + /** + * Try to remove the notification. May not succeed if the notification has not been shown long + * enough and needs to be kept around. + * + * @param key the key of the notification to remove + * @param releaseImmediately force a remove regardless of earliest removal time + * @return true if notification is removed, false otherwise + */ + fun removeNotification(key: String, releaseImmediately: Boolean): Boolean + + /** + * Try to remove the notification. May not succeed if the notification has not been shown long + * enough and needs to be kept around. + * + * @param key the key of the notification to remove + * @param releaseImmediately force a remove regardless of earliest removal time + * @param animate if true, animate the removal + * @return true if notification is removed, false otherwise + */ + fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean): Boolean + + /** Clears all managed notifications. */ + fun releaseAllImmediately() + + fun setAnimationStateHandler(handler: AnimationStateHandler) + + /** + * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until + * it's collapsed again. + */ + fun setExpanded(entry: NotificationEntry, expanded: Boolean) + + /** + * Sets whether an entry's guts are exposed and therefore it should stick in the heads up area + * if it's pinned until it's hidden again. + */ + fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) + + /** + * Set that we are exiting the headsUp pinned mode, but some notifications might still be + * animating out. This is used to keep the touchable regions in a reasonable state. + */ + fun setHeadsUpGoingAway(headsUpGoingAway: Boolean) + + /** + * Notifies that a remote input textbox in notification gets active or inactive. + * + * @param entry The entry of the target notification. + * @param remoteInputActive True to notify active, False to notify inactive. + */ + fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) + + fun setTrackingHeadsUp(tracking: Boolean) + + /** Sets the current user. */ + fun setUser(user: Int) + + /** + * Notes that the user took an action on an entry that might indirectly cause the system or the + * app to remove the notification. + * + * @param entry the entry that might be indirectly removed by the user's action + * @see + * com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator.mActionPressListener + * @see .canRemoveImmediately + */ + fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) + + /** + * Decides whether a click is invalid for a notification, i.e. it has not been shown long enough + * that a user might have consciously clicked on it. + * + * @param key the key of the touched notification + * @return whether the touch is invalid and should be discarded + */ + fun shouldSwallowClick(key: String): Boolean + + /** + * Called when posting a new notification that should alert the user and appear on screen. Adds + * the notification to be managed. + * + * @param entry entry to show + */ + fun showNotification(entry: NotificationEntry) + + fun snooze() + + /** + * Unpins all pinned Heads Up Notifications. + * + * @param userUnPinned The unpinned action is trigger by user real operation. + */ + fun unpinAll(userUnPinned: Boolean) + + fun updateNotification(key: String, alert: Boolean) +} + +/** Sets the animation state of the HeadsUpManager. */ +interface AnimationStateHandler { + fun setHeadsUpGoingAwayAnimationsAllowed(allowed: Boolean) +} + +/** Listener to register for HeadsUpNotification Phone changes. */ +interface OnHeadsUpPhoneListenerChange { + /** + * Called when a heads up notification is 'going away' or no longer 'going away'. See + * [HeadsUpManager.setHeadsUpGoingAway]. + */ + fun onHeadsUpGoingAwayStateChanged(headsUpGoingAway: Boolean) +} + +/* No op impl of HeadsUpManager. */ +class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager { + override val allEntries = Stream.empty<NotificationEntry>() + override fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) {} + override fun addListener(listener: OnHeadsUpChangedListener) {} + override fun addSwipedOutNotification(key: String) {} + override fun canRemoveImmediately(key: String) = false + override fun compare(a: NotificationEntry?, b: NotificationEntry?) = 0 + override fun dump(pw: PrintWriter, args: Array<out String>) {} + override fun extendHeadsUp() {} + override fun getEarliestRemovalTime(key: String?) = 0L + override fun getTouchableRegion(): Region? = null + override fun getTopEntry() = null + override fun hasPinnedHeadsUp() = false + override fun isAlerting(key: String) = false + override fun isHeadsUpGoingAway() = false + override fun isSnoozed(packageName: String) = false + override fun isSticky(key: String?) = false + override fun isTrackingHeadsUp() = false + override fun onExpandingFinished() {} + override fun releaseAllImmediately() {} + override fun removeListener(listener: OnHeadsUpChangedListener) {} + override fun removeNotification(key: String, releaseImmediately: Boolean) = false + override fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean) = + false + override fun setAnimationStateHandler(handler: AnimationStateHandler) {} + override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {} + override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {} + override fun setHeadsUpGoingAway(headsUpGoingAway: Boolean) {} + override fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) {} + override fun setTrackingHeadsUp(tracking: Boolean) {} + override fun setUser(user: Int) {} + override fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) {} + override fun shouldSwallowClick(key: String): Boolean = false + override fun showNotification(entry: NotificationEntry) {} + override fun snooze() {} + override fun unpinAll(userUnPinned: Boolean) {} + override fun updateNotification(key: String, alert: Boolean) {} +} + +@Module +interface HeadsUpEmptyImplModule { + @Binds @SysUISingleton fun bindsHeadsUpManager(noOpHum: HeadsUpManagerEmptyImpl): HeadsUpManager +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt index c302d6aeadbc..859e636a7714 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.content.res.Resources import com.android.systemui.res.R +import javax.inject.Inject /** * Fake SplitShadeStateController @@ -24,7 +25,7 @@ import com.android.systemui.res.R * Identical behaviour to legacy implementation (that used LargeScreenUtils.kt) I.E., behaviour * based solely on resources, no extra flag logic. */ -class ResourcesSplitShadeStateController : SplitShadeStateController { +class ResourcesSplitShadeStateController @Inject constructor() : SplitShadeStateController { override fun shouldUseSplitNotificationShade(resources: Resources): Boolean { return resources.getBoolean(R.bool.config_use_split_notification_shade) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index a77c69236946..818ba9512e15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepository; import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepositoryImpl; +import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepositoryModule; import dagger.Binds; import dagger.Module; @@ -80,7 +81,7 @@ import java.util.concurrent.Executor; import javax.inject.Named; /** Dagger Module for code in the statusbar.policy package. */ -@Module +@Module(includes = { DeviceProvisioningRepositoryModule.class }) public interface StatusBarPolicyModule { String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt new file mode 100644 index 000000000000..1160d657ba88 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt @@ -0,0 +1,73 @@ +/* + * 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.policy.data.repository + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** Tracks state related to device provisioning. */ +interface DeviceProvisioningRepository { + /** + * Whether this device has been provisioned. + * + * @see android.provider.Settings.Global.DEVICE_PROVISIONED + */ + val isDeviceProvisioned: Flow<Boolean> + + /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */ + val isFactoryResetProtectionActive: Flow<Boolean> +} + +@Module +interface DeviceProvisioningRepositoryModule { + @Binds fun bindsImpl(impl: DeviceProvisioningRepositoryImpl): DeviceProvisioningRepository +} + +class DeviceProvisioningRepositoryImpl +@Inject +constructor( + private val deviceProvisionedController: DeviceProvisionedController, +) : DeviceProvisioningRepository { + + override val isDeviceProvisioned: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : DeviceProvisionedController.DeviceProvisionedListener { + override fun onDeviceProvisionedChanged() { + trySend(deviceProvisionedController.isDeviceProvisioned) + } + } + deviceProvisionedController.addCallback(listener) + trySend(deviceProvisionedController.isDeviceProvisioned) + awaitClose { deviceProvisionedController.removeCallback(listener) } + } + + override val isFactoryResetProtectionActive: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : DeviceProvisionedController.DeviceProvisionedListener { + override fun onFrpActiveChanged() { + trySend(deviceProvisionedController.isFrpActive) + } + } + deviceProvisionedController.addCallback(listener) + trySend(deviceProvisionedController.isFrpActive) + awaitClose { deviceProvisionedController.removeCallback(listener) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index f40454915ee3..5fc435aae67f 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -23,7 +23,6 @@ import android.os.UserHandle import android.os.UserManager import android.provider.Settings import androidx.annotation.VisibleForTesting -import com.android.systemui.res.R import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -31,6 +30,7 @@ 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.flags.FeatureFlags +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus @@ -132,7 +132,6 @@ constructor( Settings.Global.ADD_USERS_WHEN_LOCKED, Settings.Global.USER_SWITCHER_ENABLED, ), - userId = UserHandle.USER_SYSTEM, ) .onStart { emit(Unit) } // Forces an initial update. .map { getSettings() } @@ -247,7 +246,7 @@ constructor( private suspend fun getSettings(): UserSwitcherSettingsModel { return withContext(backgroundDispatcher) { val isSimpleUserSwitcher = - globalSettings.getIntForUser( + globalSettings.getInt( SETTING_SIMPLE_USER_SWITCHER, if ( appContext.resources.getBoolean( @@ -258,18 +257,16 @@ constructor( } else { 0 }, - UserHandle.USER_SYSTEM, ) != 0 val isAddUsersFromLockscreen = - globalSettings.getIntForUser( + globalSettings.getInt( Settings.Global.ADD_USERS_WHEN_LOCKED, 0, - UserHandle.USER_SYSTEM, ) != 0 val isUserSwitcherEnabled = - globalSettings.getIntForUser( + globalSettings.getInt( Settings.Global.USER_SWITCHER_ENABLED, if ( appContext.resources.getBoolean( @@ -280,7 +277,6 @@ constructor( } else { 0 }, - UserHandle.USER_SYSTEM, ) != 0 UserSwitcherSettingsModel( diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt index ce9d6fd752c5..dbc3bf3a75a2 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt @@ -325,6 +325,7 @@ constructor( addAction(Intent.ACTION_USER_SWITCHED) addAction(Intent.ACTION_USER_STOPPED) addAction(Intent.ACTION_USER_UNLOCKED) + addAction(Intent.ACTION_LOCALE_CHANGED) }, user = UserHandle.SYSTEM, map = { intent, _ -> intent }, @@ -615,6 +616,7 @@ constructor( ) { val shouldRefreshAllUsers = when (intent.action) { + Intent.ACTION_LOCALE_CHANGED -> true Intent.ACTION_USER_SWITCHED -> { dismissDialog() val selectedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1) @@ -644,6 +646,11 @@ constructor( } private fun restartSecondaryService(@UserIdInt userId: Int) { + // Do not start service for user that is marked for deletion. + if (!manager.aliveUsers.map { it.id }.contains(userId)) { + return + } + val intent = Intent(applicationContext, SystemUISecondaryUserService::class.java) // Disconnect from the old secondary user's service val secondaryUserId = repository.secondaryUserId @@ -657,6 +664,7 @@ constructor( // Connect to the new secondary user's service (purely to ensure that a persistent // SystemUI application is created for that user) + if (userId != Process.myUserHandle().identifier) { applicationContext.startServiceAsUser( intent, diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt index d3f83b1e18bd..20f0fa8cf46b 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt @@ -68,6 +68,7 @@ constructor( private val hasCancelButtonBeenClicked = MutableStateFlow(false) private val isFinishRequiredDueToExecutedAction = MutableStateFlow(false) + private val userSwitched = MutableStateFlow(false) /** * Whether the observer should finish the experience. Once consumed, [onFinished] must be called @@ -89,6 +90,7 @@ constructor( fun onFinished() { hasCancelButtonBeenClicked.value = false isFinishRequiredDueToExecutedAction.value = false + userSwitched.value = false } /** Notifies that the user has clicked the "open menu" button. */ @@ -121,8 +123,9 @@ constructor( hasCancelButtonBeenClicked, // If an executed action told us to finish, we should finish, isFinishRequiredDueToExecutedAction, - ) { cancelButtonClicked, executedActionFinish -> - cancelButtonClicked || executedActionFinish + userSwitched, + ) { cancelButtonClicked, executedActionFinish, userSwitched -> + cancelButtonClicked || executedActionFinish || userSwitched } private fun toViewModel( @@ -191,7 +194,10 @@ constructor( return if (!model.isSelectable) { null } else { - { userInteractor.selectUser(model.id) } + { + userInteractor.selectUser(model.id) + userSwitched.value = true + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt new file mode 100644 index 000000000000..ac04d31041b6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt @@ -0,0 +1,50 @@ +package com.android.systemui.util + +import java.lang.ref.SoftReference +import java.lang.ref.WeakReference +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/** + * Creates a Kotlin idiomatic weak reference. + * + * Usage: + * ``` + * var weakReferenceObj: Object? by weakReference(null) + * weakReferenceObj = Object() + * ``` + */ +fun <T> weakReference(obj: T? = null): ReadWriteProperty<Any?, T?> { + return object : ReadWriteProperty<Any?, T?> { + var weakRef = WeakReference<T?>(obj) + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return weakRef.get() + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + weakRef = WeakReference(value) + } + } +} + +/** + * Creates a Kotlin idiomatic soft reference. + * + * Usage: + * ``` + * var softReferenceObj: Object? by softReference(null) + * softReferenceObj = Object() + * ``` + */ +fun <T> softReference(obj: T? = null): ReadWriteProperty<Any?, T?> { + return object : ReadWriteProperty<Any?, T?> { + var softRef = SoftReference<T?>(obj) + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return softRef.get() + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + softRef = SoftReference(value) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt index 47d505ea36d5..83ff78980880 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt @@ -18,19 +18,24 @@ package com.android.systemui.util.kotlin import com.android.systemui.util.time.SystemClock import com.android.systemui.util.time.SystemClockImpl -import kotlinx.coroutines.CoroutineStart import java.util.concurrent.atomic.AtomicReference +import kotlin.math.max +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlin.math.max /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. @@ -44,8 +49,7 @@ fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> var previousValue: Any? = noVal collect { newVal -> if (previousValue != noVal) { - @Suppress("UNCHECKED_CAST") - emit(transform(previousValue as T, newVal)) + @Suppress("UNCHECKED_CAST") emit(transform(previousValue as T, newVal)) } previousValue = newVal } @@ -60,13 +64,11 @@ fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> fun <T, R> Flow<T>.pairwiseBy( initialValue: T, transform: suspend (previousValue: T, newValue: T) -> R, -): Flow<R> = - onStart { emit(initialValue) }.pairwiseBy(transform) +): Flow<R> = onStart { emit(initialValue) }.pairwiseBy(transform) /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. * - * * The output of [getInitialValue] will be used as the "old" value for the first emission. As * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before * returning the initial value. @@ -76,8 +78,7 @@ fun <T, R> Flow<T>.pairwiseBy( fun <T, R> Flow<T>.pairwiseBy( getInitialValue: suspend () -> T, transform: suspend (previousValue: T, newValue: T) -> R, -): Flow<R> = - onStart { emit(getInitialValue()) }.pairwiseBy(transform) +): Flow<R> = onStart { emit(getInitialValue()) }.pairwiseBy(transform) /** * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new @@ -88,8 +89,8 @@ fun <T, R> Flow<T>.pairwiseBy( fun <T> Flow<T>.pairwise(): Flow<WithPrev<T>> = pairwiseBy(::WithPrev) /** - * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue] - * will be used as the "old" value for the first emission. + * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue] will + * be used as the "old" value for the first emission. * * Useful for code that needs to compare the current value to the previous value. */ @@ -102,9 +103,9 @@ data class WithPrev<T>(val previousValue: T, val newValue: T) * Returns a new [Flow] that combines the [Set] changes between each emission from [this] using * [transform]. * - * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause - * a change event to be emitted that contains no removals, and all elements from that first [Set] - * as additions. + * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a + * change event to be emitted that contains no removals, and all elements from that first [Set] as + * additions. * * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted * until a second [Set] has been emitted from the upstream [Flow]. @@ -112,22 +113,23 @@ data class WithPrev<T>(val previousValue: T, val newValue: T) fun <T, R> Flow<Set<T>>.setChangesBy( transform: suspend (removed: Set<T>, added: Set<T>) -> R, emitFirstEvent: Boolean = true, -): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this) - .distinctUntilChanged() - .pairwiseBy { old: Set<T>, new: Set<T> -> - // If an element was present in the old set, but not the new one, then it was removed - val removed = old - new - // If an element is present in the new set, but on the old one, then it was added - val added = new - old - transform(removed, added) - } +): Flow<R> = + (if (emitFirstEvent) onStart { emit(emptySet()) } else this) + .distinctUntilChanged() + .pairwiseBy { old: Set<T>, new: Set<T> -> + // If an element was present in the old set, but not the new one, then it was removed + val removed = old - new + // If an element is present in the new set, but on the old one, then it was added + val added = new - old + transform(removed, added) + } /** * Returns a new [Flow] that produces the [Set] changes between each emission from [this]. * - * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause - * a change event to be emitted that contains no removals, and all elements from that first [Set] - * as additions. + * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a + * change event to be emitted that contains no removals, and all elements from that first [Set] as + * additions. * * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted * until a second [Set] has been emitted from the upstream [Flow]. @@ -153,14 +155,11 @@ fun <A, B, C> Flow<A>.sample(other: Flow<B>, transform: suspend (A, B) -> C): Fl coroutineScope { val noVal = Any() val sampledRef = AtomicReference(noVal) - val job = launch(Dispatchers.Unconfined) { - other.collect { sampledRef.set(it) } - } + val job = launch(Dispatchers.Unconfined) { other.collect { sampledRef.set(it) } } collect { val sampled = sampledRef.get() if (sampled != noVal) { - @Suppress("UNCHECKED_CAST") - emit(transform(it, sampled as B)) + @Suppress("UNCHECKED_CAST") emit(transform(it, sampled as B)) } } job.cancel() @@ -181,7 +180,6 @@ fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a } * latest value is emitted. * * Example: - * * ```kotlin * flow { * emit(1) // t=0ms @@ -210,7 +208,6 @@ fun <T> Flow<T>.throttle(periodMs: Long): Flow<T> = this.throttle(periodMs, Syst * during this period, only The latest value is emitted. * * Example: - * * ```kotlin * flow { * emit(1) // t=0ms @@ -248,10 +245,11 @@ fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelF // We create delayJob to allow cancellation during the delay period delayJob = launch { delay(timeUntilNextEmit) - sendJob = outerScope.launch(start = CoroutineStart.UNDISPATCHED) { - send(it) - previousEmitTimeMs = clock.elapsedRealtime() - } + sendJob = + outerScope.launch(start = CoroutineStart.UNDISPATCHED) { + send(it) + previousEmitTimeMs = clock.elapsedRealtime() + } } } else { send(it) @@ -259,4 +257,15 @@ fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelF } } } -}
\ No newline at end of file +} + +/** + * Returns a [StateFlow] launched in the surrounding [CoroutineScope]. This [StateFlow] gets its + * value by invoking [getValue] whenever an event is emitted from [changedSignals]. It will also + * immediately invoke [getValue] to establish its initial value. + */ +inline fun <T> CoroutineScope.stateFlow( + changedSignals: Flow<Unit>, + crossinline getValue: () -> T, +): StateFlow<T> = + changedSignals.map { getValue() }.stateIn(this, SharingStarted.Eagerly, getValue()) diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java index 85fada20a7ad..42389f0ae627 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java @@ -16,22 +16,23 @@ package com.android.systemui.util.settings; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.content.ContentResolver; import android.net.Uri; import android.provider.Settings; -import com.android.systemui.settings.UserTracker; - import javax.inject.Inject; +// use UserHandle.USER_SYSTEM everywhere +@SuppressLint("StaticSettingsProvider") class GlobalSettingsImpl implements GlobalSettings { private final ContentResolver mContentResolver; - private final UserTracker mUserTracker; @Inject - GlobalSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) { + GlobalSettingsImpl(ContentResolver contentResolver) { mContentResolver = contentResolver; - mUserTracker = userTracker; } @Override @@ -40,43 +41,23 @@ class GlobalSettingsImpl implements GlobalSettings { } @Override - public UserTracker getUserTracker() { - return mUserTracker; - } - - @Override public Uri getUriFor(String name) { return Settings.Global.getUriFor(name); } @Override - public String getStringForUser(String name, int userHandle) { - return Settings.Global.getStringForUser(mContentResolver, name, - getRealUserHandle(userHandle)); - } - - @Override - public boolean putString(String name, String value, boolean overrideableByRestore) { - throw new UnsupportedOperationException( - "This method only exists publicly for Settings.System and Settings.Secure"); - } - - @Override - public boolean putStringForUser(String name, String value, int userHandle) { - return Settings.Global.putStringForUser(mContentResolver, name, value, - getRealUserHandle(userHandle)); + public String getString(String name) { + return Settings.Global.getString(mContentResolver, name); } @Override - public boolean putStringForUser(String name, String value, String tag, boolean makeDefault, - int userHandle, boolean overrideableByRestore) { - return Settings.Global.putStringForUser( - mContentResolver, name, value, tag, makeDefault, getRealUserHandle(userHandle), - overrideableByRestore); + public boolean putString(String name, String value) { + return Settings.Global.putString(mContentResolver, name, value); } @Override - public boolean putString(String name, String value, String tag, boolean makeDefault) { + public boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag, + boolean makeDefault) { return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault); } } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java index 798033e841d5..6031a4e05629 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java @@ -22,5 +22,5 @@ package com.android.systemui.util.settings; * See {@link SettingsProxy} for details. */ -public interface SecureSettings extends SettingsProxy { +public interface SecureSettings extends UserSettingsProxy { } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java index f995436594b1..6532ce8ddf7d 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java @@ -20,6 +20,8 @@ import android.content.ContentResolver; import android.net.Uri; import android.provider.Settings; +import androidx.annotation.NonNull; + import com.android.systemui.settings.UserTracker; import javax.inject.Inject; @@ -75,7 +77,7 @@ class SecureSettingsImpl implements SecureSettings { } @Override - public boolean putString(String name, String value, String tag, boolean makeDefault) { + public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) { return Settings.Secure.putString(mContentResolver, name, value, tag, makeDefault); } } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java index b6846a34a0bd..6a9edc11add0 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java @@ -18,25 +18,22 @@ package com.android.systemui.util.settings; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; -import android.os.UserHandle; import android.provider.Settings; -import com.android.systemui.settings.UserTracker; - /** - * Used to interact with Settings.Secure, Settings.Global, and Settings.System. - * + * Used to interact with mainly with Settings.Global, but can also be used for Settings.System + * and Settings.Secure. To use the per-user System and Secure settings, {@link UserSettingsProxy} + * must be used instead. + * <p> * This interface can be implemented to give instance method (instead of static method) versions - * of Settings.Secure, Settings.Global, and Settings.System. It can be injected into class - * constructors and then faked or mocked as needed in tests. - * - * You can ask for {@link SecureSettings}, {@link GlobalSettings}, or {@link SystemSettings} to be - * injected as needed. - * + * of Settings.Global. It can be injected into class constructors and then faked or mocked as needed + * in tests. + * <p> + * You can ask for {@link GlobalSettings} to be injected as needed. + * <p> * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods, * normally found on {@link ContentResolver} instances, unifying setting related actions in one * place. @@ -49,29 +46,6 @@ public interface SettingsProxy { ContentResolver getContentResolver(); /** - * Returns that {@link UserTracker} this instance was constructed with. - */ - UserTracker getUserTracker(); - - /** - * Returns the user id for the associated {@link ContentResolver}. - */ - default int getUserId() { - return getContentResolver().getUserId(); - } - - /** - * Returns the actual current user handle when querying with the current user. Otherwise, - * returns the passed in user id. - */ - default int getRealUserHandle(int userHandle) { - if (userHandle != UserHandle.USER_CURRENT) { - return userHandle; - } - return getUserTracker().getUserId(); - } - - /** * Construct the content URI for a particular name/value pair, * useful for monitoring changes with a ContentObserver. * @param name to look up in the table @@ -82,7 +56,7 @@ public interface SettingsProxy { /** * Convenience wrapper around * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' - * + * <p> * Implicitly calls {@link #getUriFor(String)} on the passed in name. */ default void registerContentObserver(String name, ContentObserver settingsObserver) { @@ -94,13 +68,13 @@ public interface SettingsProxy { * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' */ default void registerContentObserver(Uri uri, ContentObserver settingsObserver) { - registerContentObserverForUser(uri, settingsObserver, getUserId()); + registerContentObserver(uri, false, settingsObserver); } /** * Convenience wrapper around * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' - * + * <p> * Implicitly calls {@link #getUriFor(String)} on the passed in name. */ default void registerContentObserver(String name, boolean notifyForDescendants, @@ -114,53 +88,8 @@ public interface SettingsProxy { */ default void registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver) { - registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId()); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - * - * Implicitly calls {@link #getUriFor(String)} on the passed in name. - */ - default void registerContentObserverForUser( - String name, ContentObserver settingsObserver, int userHandle) { - registerContentObserverForUser( - getUriFor(name), settingsObserver, userHandle); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - */ - default void registerContentObserverForUser( - Uri uri, ContentObserver settingsObserver, int userHandle) { - registerContentObserverForUser( - uri, false, settingsObserver, userHandle); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - * - * Implicitly calls {@link #getUriFor(String)} on the passed in name. - */ - default void registerContentObserverForUser( - String name, boolean notifyForDescendants, ContentObserver settingsObserver, - int userHandle) { - registerContentObserverForUser( - getUriFor(name), notifyForDescendants, settingsObserver, userHandle); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - */ - default void registerContentObserverForUser( - Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver, - int userHandle) { getContentResolver().registerContentObserver( - uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle)); + uri, notifyForDescendants, settingsObserver); } /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */ @@ -173,22 +102,7 @@ public interface SettingsProxy { * @param name to look up in the table * @return the corresponding value, or null if not present */ - default String getString(String name) { - return getStringForUser(name, getUserId()); - } - - /**See {@link #getString(String)}. */ - String getStringForUser(String name, int userHandle); - - /** - * Store a name/value pair into the database. Values written by this method will be - * overridden if a restore happens in the future. - * - * @param name to store - * @param value to associate with the name - * @return true if the value was set, false on database errors - */ - boolean putString(String name, String value, boolean overrideableByRestore); + String getString(String name); /** * Store a name/value pair into the database. @@ -196,16 +110,7 @@ public interface SettingsProxy { * @param value to associate with the name * @return true if the value was set, false on database errors */ - default boolean putString(String name, String value) { - return putStringForUser(name, value, getUserId()); - } - - /** See {@link #putString(String, String)}. */ - boolean putStringForUser(String name, String value, int userHandle); - - /** See {@link #putString(String, String)}. */ - boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag, - boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore); + boolean putString(String name, String value); /** * Store a name/value pair into the database. @@ -262,12 +167,7 @@ public interface SettingsProxy { * or not a valid integer. */ default int getInt(String name, int def) { - return getIntForUser(name, def, getUserId()); - } - - /** See {@link #getInt(String, int)}. */ - default int getIntForUser(String name, int def, int userHandle) { - String v = getStringForUser(name, userHandle); + String v = getString(name); try { return v != null ? Integer.parseInt(v) : def; } catch (NumberFormatException e) { @@ -292,14 +192,9 @@ public interface SettingsProxy { * * @return The setting's current value. */ - default int getInt(String name) throws Settings.SettingNotFoundException { - return getIntForUser(name, getUserId()); - } - - /** See {@link #getInt(String)}. */ - default int getIntForUser(String name, int userHandle) + default int getInt(String name) throws Settings.SettingNotFoundException { - String v = getStringForUser(name, userHandle); + String v = getString(name); try { return Integer.parseInt(v); } catch (NumberFormatException e) { @@ -320,12 +215,7 @@ public interface SettingsProxy { * @return true if the value was set, false on database errors */ default boolean putInt(String name, int value) { - return putIntForUser(name, value, getUserId()); - } - - /** See {@link #putInt(String, int)}. */ - default boolean putIntForUser(String name, int value, int userHandle) { - return putStringForUser(name, Integer.toString(value), userHandle); + return putString(name, Integer.toString(value)); } /** @@ -342,12 +232,7 @@ public interface SettingsProxy { * or not a valid boolean. */ default boolean getBool(String name, boolean def) { - return getBoolForUser(name, def, getUserId()); - } - - /** See {@link #getBool(String, boolean)}. */ - default boolean getBoolForUser(String name, boolean def, int userHandle) { - return getIntForUser(name, def ? 1 : 0, userHandle) != 0; + return getInt(name, def ? 1 : 0) != 0; } /** @@ -367,14 +252,9 @@ public interface SettingsProxy { * * @return The setting's current value. */ - default boolean getBool(String name) throws Settings.SettingNotFoundException { - return getBoolForUser(name, getUserId()); - } - - /** See {@link #getBool(String)}. */ - default boolean getBoolForUser(String name, int userHandle) + default boolean getBool(String name) throws Settings.SettingNotFoundException { - return getIntForUser(name, userHandle) != 0; + return getInt(name) != 0; } /** @@ -390,12 +270,7 @@ public interface SettingsProxy { * @return true if the value was set, false on database errors */ default boolean putBool(String name, boolean value) { - return putBoolForUser(name, value, getUserId()); - } - - /** See {@link #putBool(String, boolean)}. */ - default boolean putBoolForUser(String name, boolean value, int userHandle) { - return putIntForUser(name, value ? 1 : 0, userHandle); + return putInt(name, value ? 1 : 0); } /** @@ -412,12 +287,12 @@ public interface SettingsProxy { * or not a valid {@code long}. */ default long getLong(String name, long def) { - return getLongForUser(name, def, getUserId()); + String valString = getString(name); + return parseLongOrUseDefault(valString, def); } - /** See {@link #getLong(String, long)}. */ - default long getLongForUser(String name, long def, int userHandle) { - String valString = getStringForUser(name, userHandle); + /** Convert a string to a long, or uses a default if the string is malformed or null */ + static long parseLongOrUseDefault(String valString, long def) { long value; try { value = valString != null ? Long.parseLong(valString) : def; @@ -443,14 +318,15 @@ public interface SettingsProxy { * @throws Settings.SettingNotFoundException Thrown if a setting by the given * name can't be found or the setting value is not an integer. */ - default long getLong(String name) throws Settings.SettingNotFoundException { - return getLongForUser(name, getUserId()); + default long getLong(String name) + throws Settings.SettingNotFoundException { + String valString = getString(name); + return parseLongOrThrow(name, valString); } - /** See {@link #getLong(String)}. */ - default long getLongForUser(String name, int userHandle) + /** Convert a string to a long, or throws an exception if the string is malformed or null */ + static long parseLongOrThrow(String name, String valString) throws Settings.SettingNotFoundException { - String valString = getStringForUser(name, userHandle); try { return Long.parseLong(valString); } catch (NumberFormatException e) { @@ -471,12 +347,7 @@ public interface SettingsProxy { * @return true if the value was set, false on database errors */ default boolean putLong(String name, long value) { - return putLongForUser(name, value, getUserId()); - } - - /** See {@link #putLong(String, long)}. */ - default boolean putLongForUser(String name, long value, int userHandle) { - return putStringForUser(name, Long.toString(value), userHandle); + return putString(name, Long.toString(value)); } /** @@ -493,12 +364,12 @@ public interface SettingsProxy { * or not a valid float. */ default float getFloat(String name, float def) { - return getFloatForUser(name, def, getUserId()); + String v = getString(name); + return parseFloat(v, def); } - /** See {@link #getFloat(String)}. */ - default float getFloatForUser(String name, float def, int userHandle) { - String v = getStringForUser(name, userHandle); + /** Convert a string to a float, or uses a default if the string is malformed or null */ + static float parseFloat(String v, float def) { try { return v != null ? Float.parseFloat(v) : def; } catch (NumberFormatException e) { @@ -523,14 +394,15 @@ public interface SettingsProxy { * * @return The setting's current value. */ - default float getFloat(String name) throws Settings.SettingNotFoundException { - return getFloatForUser(name, getUserId()); + default float getFloat(String name) + throws Settings.SettingNotFoundException { + String v = getString(name); + return parseFloatOrThrow(name, v); } - /** See {@link #getFloat(String, float)}. */ - default float getFloatForUser(String name, int userHandle) + /** Convert a string to a float, or throws an exception if the string is malformed or null */ + static float parseFloatOrThrow(String name, String v) throws Settings.SettingNotFoundException { - String v = getStringForUser(name, userHandle); if (v == null) { throw new Settings.SettingNotFoundException(name); } @@ -554,11 +426,6 @@ public interface SettingsProxy { * @return true if the value was set, false on database errors */ default boolean putFloat(String name, float value) { - return putFloatForUser(name, value, getUserId()); - } - - /** See {@link #putFloat(String, float)} */ - default boolean putFloatForUser(String name, float value, int userHandle) { - return putStringForUser(name, Float.toString(value), userHandle); + return putString(name, Float.toString(value)); } } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt index 561495e9d092..74843685893c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.Flow object SettingsProxyExt { /** Returns a flow of [Unit] that is invoked each time that content is updated. */ - fun SettingsProxy.observerFlow( + fun UserSettingsProxy.observerFlow( @UserIdInt userId: Int, vararg names: String, ): Flow<Unit> { @@ -44,4 +44,22 @@ object SettingsProxyExt { awaitClose { unregisterContentObserver(observer) } } } + + /** Returns a flow of [Unit] that is invoked each time that content is updated. */ + fun SettingsProxy.observerFlow( + vararg names: String, + ): Flow<Unit> { + return conflatedCallbackFlow { + val observer = + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + trySend(Unit) + } + } + + names.forEach { name -> registerContentObserver(name, observer) } + + awaitClose { unregisterContentObserver(observer) } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java index d57d7496381c..c67c60375b2c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java @@ -21,5 +21,5 @@ package com.android.systemui.util.settings; * * See {@link SettingsProxy} for details. */ -public interface SystemSettings extends SettingsProxy { +public interface SystemSettings extends UserSettingsProxy { } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java index fba7ddf5fe34..658b2992bfad 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java @@ -20,6 +20,8 @@ import android.content.ContentResolver; import android.net.Uri; import android.provider.Settings; +import androidx.annotation.NonNull; + import com.android.systemui.settings.UserTracker; import javax.inject.Inject; @@ -74,7 +76,7 @@ class SystemSettingsImpl implements SystemSettings { } @Override - public boolean putString(String name, String value, String tag, boolean makeDefault) { + public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) { throw new UnsupportedOperationException( "This method only exists publicly for Settings.Secure and Settings.Global"); } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java new file mode 100644 index 000000000000..0d6c0f59b2d0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java @@ -0,0 +1,272 @@ +/* + * 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.util.settings; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.systemui.settings.UserTracker; + +/** + * Used to interact with per-user Settings.Secure and Settings.System settings (but not + * Settings.Global, since those do not vary per-user) + * <p> + * This interface can be implemented to give instance method (instead of static method) versions + * of Settings.Secure and Settings.System. It can be injected into class constructors and then + * faked or mocked as needed in tests. + * <p> + * You can ask for {@link SecureSettings} or {@link SystemSettings} to be injected as needed. + * <p> + * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods, + * normally found on {@link ContentResolver} instances, unifying setting related actions in one + * place. + */ +public interface UserSettingsProxy extends SettingsProxy { + + /** + * Returns that {@link UserTracker} this instance was constructed with. + */ + UserTracker getUserTracker(); + + /** + * Returns the user id for the associated {@link ContentResolver}. + */ + default int getUserId() { + return getContentResolver().getUserId(); + } + + /** + * Returns the actual current user handle when querying with the current user. Otherwise, + * returns the passed in user id. + */ + default int getRealUserHandle(int userHandle) { + if (userHandle != UserHandle.USER_CURRENT) { + return userHandle; + } + return getUserTracker().getUserId(); + } + + @Override + default void registerContentObserver(Uri uri, ContentObserver settingsObserver) { + registerContentObserverForUser(uri, settingsObserver, getUserId()); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' + */ + @Override + default void registerContentObserver(Uri uri, boolean notifyForDescendants, + ContentObserver settingsObserver) { + registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId()); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} + * + * Implicitly calls {@link #getUriFor(String)} on the passed in name. + */ + default void registerContentObserverForUser( + String name, ContentObserver settingsObserver, int userHandle) { + registerContentObserverForUser( + getUriFor(name), settingsObserver, userHandle); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} + */ + default void registerContentObserverForUser( + Uri uri, ContentObserver settingsObserver, int userHandle) { + registerContentObserverForUser( + uri, false, settingsObserver, userHandle); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} + * + * Implicitly calls {@link #getUriFor(String)} on the passed in name. + */ + default void registerContentObserverForUser( + String name, boolean notifyForDescendants, ContentObserver settingsObserver, + int userHandle) { + registerContentObserverForUser( + getUriFor(name), notifyForDescendants, settingsObserver, userHandle); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} + */ + default void registerContentObserverForUser( + Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver, + int userHandle) { + getContentResolver().registerContentObserver( + uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle)); + } + + /** + * Look up a name in the database. + * @param name to look up in the table + * @return the corresponding value, or null if not present + */ + @Override + default String getString(String name) { + return getStringForUser(name, getUserId()); + } + + /**See {@link #getString(String)}. */ + String getStringForUser(String name, int userHandle); + + /** + * Store a name/value pair into the database. Values written by this method will be + * overridden if a restore happens in the future. + * + * @param name to store + * @param value to associate with the name + * @return true if the value was set, false on database errors + */ + boolean putString(String name, String value, boolean overrideableByRestore); + + @Override + default boolean putString(String name, String value) { + return putStringForUser(name, value, getUserId()); + } + + /** See {@link #putString(String, String)}. */ + boolean putStringForUser(String name, String value, int userHandle); + + /** See {@link #putString(String, String)}. */ + boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag, + boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore); + + @Override + default int getInt(String name, int def) { + return getIntForUser(name, def, getUserId()); + } + + /** See {@link #getInt(String, int)}. */ + default int getIntForUser(String name, int def, int userHandle) { + String v = getStringForUser(name, userHandle); + try { + return v != null ? Integer.parseInt(v) : def; + } catch (NumberFormatException e) { + return def; + } + } + + @Override + default int getInt(String name) throws Settings.SettingNotFoundException { + return getIntForUser(name, getUserId()); + } + + /** See {@link #getInt(String)}. */ + default int getIntForUser(String name, int userHandle) + throws Settings.SettingNotFoundException { + String v = getStringForUser(name, userHandle); + try { + return Integer.parseInt(v); + } catch (NumberFormatException e) { + throw new Settings.SettingNotFoundException(name); + } + } + + @Override + default boolean putInt(String name, int value) { + return putIntForUser(name, value, getUserId()); + } + + /** See {@link #putInt(String, int)}. */ + default boolean putIntForUser(String name, int value, int userHandle) { + return putStringForUser(name, Integer.toString(value), userHandle); + } + + @Override + default boolean getBool(String name, boolean def) { + return getBoolForUser(name, def, getUserId()); + } + + /** See {@link #getBool(String, boolean)}. */ + default boolean getBoolForUser(String name, boolean def, int userHandle) { + return getIntForUser(name, def ? 1 : 0, userHandle) != 0; + } + + @Override + default boolean getBool(String name) throws Settings.SettingNotFoundException { + return getBoolForUser(name, getUserId()); + } + + /** See {@link #getBool(String)}. */ + default boolean getBoolForUser(String name, int userHandle) + throws Settings.SettingNotFoundException { + return getIntForUser(name, userHandle) != 0; + } + + @Override + default boolean putBool(String name, boolean value) { + return putBoolForUser(name, value, getUserId()); + } + + /** See {@link #putBool(String, boolean)}. */ + default boolean putBoolForUser(String name, boolean value, int userHandle) { + return putIntForUser(name, value ? 1 : 0, userHandle); + } + + /** See {@link #getLong(String, long)}. */ + default long getLongForUser(String name, long def, int userHandle) { + String valString = getStringForUser(name, userHandle); + return SettingsProxy.parseLongOrUseDefault(valString, def); + } + + /** See {@link #getLong(String)}. */ + default long getLongForUser(String name, int userHandle) + throws Settings.SettingNotFoundException { + String valString = getStringForUser(name, userHandle); + return SettingsProxy.parseLongOrThrow(name, valString); + } + + /** See {@link #putLong(String, long)}. */ + default boolean putLongForUser(String name, long value, int userHandle) { + return putStringForUser(name, Long.toString(value), userHandle); + } + + /** See {@link #getFloat(String)}. */ + default float getFloatForUser(String name, float def, int userHandle) { + String v = getStringForUser(name, userHandle); + return SettingsProxy.parseFloat(v, def); + } + + /** See {@link #getFloat(String, float)}. */ + default float getFloatForUser(String name, int userHandle) + throws Settings.SettingNotFoundException { + String v = getStringForUser(name, userHandle); + return SettingsProxy.parseFloatOrThrow(name, v); + } + + /** See {@link #putFloat(String, float)} */ + default boolean putFloatForUser(String name, float value, int userHandle) { + return putStringForUser(name, Float.toString(value), userHandle); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt new file mode 100644 index 000000000000..51d2afabd7f9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt @@ -0,0 +1,62 @@ +/* + * 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. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.util.ui + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.transformLatest + +/** + * A state comprised of a [value] of type [T] paired with a boolean indicating whether or not the + * [value] [isAnimating] in the UI. + */ +data class AnimatedValue<T>( + val value: T, + val isAnimating: Boolean, +) + +/** + * An event comprised of a [value] of type [T] paired with a [boolean][startAnimating] indicating + * whether or not this event should start an animation. + */ +data class AnimatableEvent<T>( + val value: T, + val startAnimating: Boolean, +) + +/** + * Returns a [Flow] that tracks an [AnimatedValue] state. The input [Flow] is used to update the + * [AnimatedValue.value], as well as [AnimatedValue.isAnimating] if the event's + * [AnimatableEvent.startAnimating] value is `true`. When [completionEvents] emits a value, the + * [AnimatedValue.isAnimating] will flip to `false`. + */ +fun <T> Flow<AnimatableEvent<T>>.toAnimatedValueFlow( + completionEvents: Flow<Any?>, +): Flow<AnimatedValue<T>> = transformLatest { (value, startAnimating) -> + emit(AnimatedValue(value, isAnimating = startAnimating)) + if (startAnimating) { + // Wait for a completion now that we've started animating + completionEvents + .map { Unit } // replace the event so that it's never `null` + .firstOrNull() // `null` indicates an empty flow + // emit the new state if the flow was not empty. + ?.run { emit(AnimatedValue(value, isAnimating = false)) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index ea4d31bca035..d65a69c62072 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -77,6 +77,8 @@ import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.concurrency.ThreadFactory; +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.util.HashMap; import java.util.List; @@ -286,6 +288,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa return new MediaSessions(context, looper, callbacks); } + @NeverCompile public void dump(PrintWriter pw, String[] args) { pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:"); pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 727d649c8118..929b91cf6993 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -135,6 +135,9 @@ import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.AlphaTintDrawableWrapper; import com.android.systemui.util.RoundedCornerProgressDrawable; +import com.android.systemui.util.settings.SecureSettings; + +import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; @@ -304,6 +307,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private @DevicePostureController.DevicePostureInt int mDevicePosture; private int mOrientation; private final FeatureFlags mFeatureFlags; + private final Lazy<SecureSettings> mSecureSettings; + private int mDialogTimeoutMillis; public VolumeDialogImpl( Context context, @@ -320,7 +325,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, DevicePostureController devicePostureController, Looper looper, DumpManager dumpManager, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + Lazy<SecureSettings> secureSettings) { mFeatureFlags = featureFlags; mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); @@ -351,6 +357,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mUseBackgroundBlur = mContext.getResources().getBoolean(R.bool.config_volumeDialogUseBackgroundBlur); mInteractionJankMonitor = interactionJankMonitor; + mSecureSettings = secureSettings; + mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS; dumpManager.registerDumpable("VolumeDialogImpl", this); @@ -515,6 +523,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mDialog.setContentView(R.layout.volume_dialog); mDialogView = mDialog.findViewById(R.id.volume_dialog); mDialogView.setAlpha(0); + mDialogTimeoutMillis = mSecureSettings.get().getInt( + Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, DIALOG_TIMEOUT_MILLIS); mDialog.setCanceledOnTouchOutside(true); mDialog.setOnShowListener(dialog -> { mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); @@ -527,7 +537,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, .alpha(1) .translationX(0) .setDuration(mDialogShowAnimationDurationMs) - .setListener(getJankListener(getDialogView(), TYPE_SHOW, DIALOG_TIMEOUT_MILLIS)) + .setListener(getJankListener(getDialogView(), TYPE_SHOW, mDialogTimeoutMillis)) .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator()) .withEndAction(() -> { if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) { @@ -1514,7 +1524,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, AccessibilityManager.FLAG_CONTENT_TEXT | AccessibilityManager.FLAG_CONTENT_CONTROLS); } - return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS, + return mAccessibilityMgr.getRecommendedTimeoutMillis(mDialogTimeoutMillis, AccessibilityManager.FLAG_CONTENT_CONTROLS); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index cc9f3e14216e..e3b3c21d5d0d 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -31,6 +31,7 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.volume.CsdWarningDialog; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.volume.VolumeDialogComponent; @@ -38,6 +39,7 @@ import com.android.systemui.volume.VolumeDialogImpl; import com.android.systemui.volume.VolumePanelFactory; import dagger.Binds; +import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -63,7 +65,8 @@ public interface VolumeModule { CsdWarningDialog.Factory csdFactory, DevicePostureController devicePostureController, DumpManager dumpManager, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + Lazy<SecureSettings> secureSettings) { VolumeDialogImpl impl = new VolumeDialogImpl( context, volumeDialogController, @@ -79,7 +82,8 @@ public interface VolumeModule { devicePostureController, Looper.getMainLooper(), dumpManager, - featureFlags); + featureFlags, + secureSettings); impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); impl.setAutomute(true); impl.setSilentMode(false); diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index 750b6f95d52e..2132904caa84 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -40,12 +40,13 @@ import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; -import com.android.systemui.res.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -68,6 +69,7 @@ public class WalletActivity extends ComponentActivity implements private final Executor mExecutor; private final Handler mHandler; private final FalsingManager mFalsingManager; + private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; private FalsingCollector mFalsingCollector; private final UserTracker mUserTracker; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -91,7 +93,8 @@ public class WalletActivity extends ComponentActivity implements UserTracker userTracker, KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarKeyguardViewManager keyguardViewManager, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) { mKeyguardStateController = keyguardStateController; mKeyguardDismissUtil = keyguardDismissUtil; mActivityStarter = activityStarter; @@ -103,6 +106,7 @@ public class WalletActivity extends ComponentActivity implements mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardViewManager = keyguardViewManager; mUiEventLogger = uiEventLogger; + mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; } @Override @@ -209,6 +213,7 @@ public class WalletActivity extends ComponentActivity implements true, Utils.getColorAttrDefaultColor( this, com.android.internal.R.attr.colorAccentPrimary)); + mKeyguardFaceAuthInteractor.onWalletLaunched(); mKeyguardViewManager.requestFace(true); } diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt index 8990583cf9de..167e3417c162 100644 --- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt +++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt @@ -18,61 +18,94 @@ package com.android import android.app.ActivityManager import android.app.admin.DevicePolicyManager import android.os.UserManager +import android.util.DisplayMetrics +import com.android.internal.logging.MetricsLogger import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.demomode.DemoModeController +import com.android.systemui.dump.DumpManager +import com.android.systemui.keyguard.ScreenLifecycle +import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.BroadcastDispatcherLog import com.android.systemui.log.dagger.SceneFrameworkLog +import com.android.systemui.media.controls.ui.MediaHierarchyManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationListener +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.NotificationShadeDepthController +import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.stack.AmbientState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.SplitShadeStateController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.util.mockito.mock import com.android.wm.shell.bubbles.Bubbles +import dagger.Binds import dagger.Module import dagger.Provides import java.util.Optional -@Module +@Module(includes = [TestMocksModule.Bindings::class]) data class TestMocksModule( @get:Provides val activityStarter: ActivityStarter = mock(), + @get:Provides val ambientState: AmbientState = mock(), @get:Provides val bubbles: Optional<Bubbles> = Optional.of(mock()), - @get:Provides val configurationController: ConfigurationController = mock(), @get:Provides val darkIconDispatcher: DarkIconDispatcher = mock(), @get:Provides val demoModeController: DemoModeController = mock(), @get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(), @get:Provides val dozeParameters: DozeParameters = mock(), + @get:Provides val dumpManager: DumpManager = mock(), @get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(), @get:Provides val keyguardBypassController: KeyguardBypassController = mock(), @get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(), @get:Provides val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(), - @get:Provides val notifListener: NotificationListener = mock(), - @get:Provides val notifMediaManager: NotificationMediaManager = mock(), - @get:Provides val screenOffAnimController: ScreenOffAnimationController = mock(), - @get:Provides val splitShadeStateController: SplitShadeStateController = mock(), - @get:Provides val statusBarStateController: StatusBarStateController = mock(), + @get:Provides val mediaHierarchyManager: MediaHierarchyManager = mock(), + @get:Provides val notificationListener: NotificationListener = mock(), + @get:Provides val notificationLockscreenUserManager: NotificationLockscreenUserManager = mock(), + @get:Provides val notificationMediaManager: NotificationMediaManager = mock(), + @get:Provides val notificationShadeDepthController: NotificationShadeDepthController = mock(), + @get:Provides + val notificationStackScrollLayoutController: NotificationStackScrollLayoutController = mock(), + @get:Provides val notificationStackSizeCalculator: NotificationStackSizeCalculator = mock(), + @get:Provides val notificationWakeUpCoordinator: NotificationWakeUpCoordinator = mock(), + @get:Provides val screenLifecycle: ScreenLifecycle = mock(), + @get:Provides val screenOffAnimationController: ScreenOffAnimationController = mock(), + @get:Provides val scrimController: ScrimController = mock(), + @get:Provides val statusBarStateController: SysuiStatusBarStateController = mock(), @get:Provides val statusBarWindowController: StatusBarWindowController = mock(), - @get:Provides val wakeUpCoordinator: NotificationWakeUpCoordinator = mock(), + @get:Provides val wakefulnessLifecycle: WakefulnessLifecycle = mock(), // log buffers @get:[Provides BroadcastDispatcherLog] val broadcastDispatcherLogger: LogBuffer = mock(), @get:[Provides SceneFrameworkLog] val sceneLogger: LogBuffer = mock(), + @get:Provides val lsShadeTransitionLogger: LSShadeTransitionLogger = mock(), // framework mocks @get:Provides val activityManager: ActivityManager = mock(), @get:Provides val devicePolicyManager: DevicePolicyManager = mock(), + @get:Provides val displayMetrics: DisplayMetrics = mock(), + @get:Provides val metricsLogger: MetricsLogger = mock(), @get:Provides val userManager: UserManager = mock(), -) +) { + @Module + interface Bindings { + @Binds + fun bindStatusBarStateController( + sysuiStatusBarStateController: SysuiStatusBarStateController, + ): StatusBarStateController + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt index f943acd9180e..d8a2c5f76fce 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags @@ -47,7 +46,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardPasswordViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt index e09009389f8b..dc1618d128b3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake @@ -53,7 +52,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardPatternViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java index 8322b37151de..4a24e4a1f40f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java @@ -31,14 +31,13 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.SingleTapClassifier; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.res.R; import org.junit.Before; import org.junit.Test; @@ -47,7 +46,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 2f08804d8c05..9df4dd4df4d2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -26,7 +26,6 @@ import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityModel.SecurityMode import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake @@ -53,7 +52,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardPinViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index f3a1b68a87ae..5102957c71ed 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -69,7 +69,8 @@ public class KeyguardPresentationTest extends SysuiTestCase { when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView); - when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class))) + when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class), + any(Display.class))) .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) .thenReturn(mKeyguardClockSwitchController); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index d54843d39d21..62f9a9dcce70 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -36,7 +36,6 @@ import com.android.internal.logging.UiEventLogger import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback import com.android.keyguard.KeyguardSecurityModel.SecurityMode -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate import com.android.systemui.biometrics.SideFpsController @@ -100,7 +99,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @RunWithLooper class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @@ -812,6 +810,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -827,7 +826,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -844,7 +844,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -862,7 +863,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -878,6 +880,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Lockscreen, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") @@ -895,6 +898,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index aad11d90f5a4..156e06843d15 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -56,11 +56,10 @@ import androidx.constraintlayout.widget.ConstraintSet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.data.source.UserRecord; import com.android.systemui.util.settings.GlobalSettings; @@ -76,7 +75,6 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper() public class KeyguardSecurityContainerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java index b02b1f9df9fb..7bb6ef1c8895 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java @@ -35,10 +35,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.res.R; import org.junit.Before; import org.junit.Rule; @@ -50,7 +49,6 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper() public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index d0bb5a999327..4290b8b28687 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags @@ -44,7 +43,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardSimPinViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt index 59cd26cc6b9b..31ee6411f93b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags @@ -40,7 +39,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardSimPukViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java index 989164ebf174..3b8e02f7455a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java @@ -20,7 +20,6 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.animation.LayoutTransition; import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; @@ -69,7 +68,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardClockSwitch mKeyguardClockSwitch; @Mock protected FrameLayout mMediaHostContainer; - @Mock protected LayoutTransition mMediaLayoutTransition; @Before public void setup() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index e4e2b0a8d89f..948942fbce3a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -16,22 +16,27 @@ package com.android.keyguard; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.animation.LayoutTransition; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; -import com.android.systemui.res.R; +import com.android.app.animation.Interpolators; +import com.android.systemui.animation.ViewHierarchyAnimator; import com.android.systemui.plugins.ClockConfig; import com.android.systemui.plugins.ClockController; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -39,6 +44,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import java.lang.reflect.Field; + @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) @@ -81,7 +88,7 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll public void updatePosition_primaryClockAnimation() { ClockController mockClock = mock(ClockController.class); when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); - when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", false, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true)); mController.updatePosition(10, 15, 20f, true); @@ -96,7 +103,7 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll public void updatePosition_alternateClockAnimation() { ClockController mockClock = mock(ClockController.class); when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); - when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", true, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true)); mController.updatePosition(10, 15, 20f, true); @@ -142,19 +149,7 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll } @Test - public void onInit_addsOnLayoutChangeListenerToMediaHostContainer() { - when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn( - mMediaHostContainer); - - mController.onInit(); - - ArgumentCaptor<View.OnLayoutChangeListener> captor = - ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); - verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture()); - } - - @Test - public void clockSwitchHeightChanged_mediaChangingLayoutTransitionEnabled() { + public void clockSwitchHeightChanged_animatesMediaHostContainer() { when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn( mMediaHostContainer); @@ -167,6 +162,10 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`. // Below here is the actual test. + ViewHierarchyAnimator.Companion animator = ViewHierarchyAnimator.Companion; + ViewHierarchyAnimator.Companion spiedAnimator = spy(animator); + setCompanion(spiedAnimator); + View.OnLayoutChangeListener listener = captor.getValue(); mController.setSplitShadeEnabled(true); @@ -174,17 +173,20 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true); when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE); when(mMediaHostContainer.getHeight()).thenReturn(200); - when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition); when(mKeyguardClockSwitch.getHeight()).thenReturn(0); listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */ 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */ 0, /* oldBottom = */ 200); - verify(mMediaLayoutTransition).enableTransitionType(LayoutTransition.CHANGING); + verify(spiedAnimator).animateNextUpdate(mMediaHostContainer, + Interpolators.STANDARD, /* duration= */ 500L, /* animateChildren= */ false); + + // Resets ViewHierarchyAnimator.Companion to its original value + setCompanion(animator); } @Test - public void clockSwitchHeightNotChanged_mediaChangingLayoutTransitionNotEnabled() { + public void clockSwitchHeightNotChanged_doesNotAnimateMediaOutputContainer() { when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn( mMediaHostContainer); @@ -197,6 +199,10 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`. // Below here is the actual test. + ViewHierarchyAnimator.Companion animator = ViewHierarchyAnimator.Companion; + ViewHierarchyAnimator.Companion spiedAnimator = spy(animator); + setCompanion(spiedAnimator); + View.OnLayoutChangeListener listener = captor.getValue(); mController.setSplitShadeEnabled(true); @@ -204,36 +210,24 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true); when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE); when(mMediaHostContainer.getHeight()).thenReturn(200); - when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition); when(mKeyguardClockSwitch.getHeight()).thenReturn(200); listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */ 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */ 0, /* oldBottom = */ 200); - verify(mMediaLayoutTransition, never()).enableTransitionType(LayoutTransition.CHANGING); - } - - @Test - public void onMediaHostContainerLayout_disablesChangingLayoutTransition() { - when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn( - mMediaHostContainer); - - mController.onInit(); + verify(spiedAnimator, never()).animateNextUpdate(any(), any(), anyLong(), anyBoolean()); - ArgumentCaptor<View.OnLayoutChangeListener> captor = - ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); - verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture()); - - // Above here is the same as `onInit_addsOnLayoutChangeListenerToMediaHostContainer`. - // Below here is the actual test. - - View.OnLayoutChangeListener listener = captor.getValue(); - - when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition); + // Resets ViewHierarchyAnimator.Companion to its original value + setCompanion(animator); + } - when(mMediaLayoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).thenReturn( - true); - listener.onLayoutChange(mMediaHostContainer, 1, 2, 3, 4, 1, 2, 3, 4); - verify(mMediaLayoutTransition).disableTransitionType(LayoutTransition.CHANGING); + private void setCompanion(ViewHierarchyAnimator.Companion companion) { + try { + Field field = ViewHierarchyAnimator.class.getDeclaredField("Companion"); + field.setAccessible(true); + field.set(null, companion); + } catch (Exception e) { + throw new RuntimeException(e); + } } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt index 58d372c68c55..86439e557f8b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt @@ -1,6 +1,5 @@ package com.android.keyguard -import android.animation.LayoutTransition import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper @@ -36,20 +35,6 @@ class KeyguardStatusViewTest : SysuiTestCase() { } @Test - fun mediaViewHasLayoutTransitionInDisabledState() { - val layoutTransition = (mediaView as ViewGroup).layoutTransition - assertThat(layoutTransition).isNotNull() - assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_APPEARING)) - .isFalse() - assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_DISAPPEARING)) - .isFalse() - assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.APPEARING)).isFalse() - assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.DISAPPEARING)) - .isFalse() - assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).isFalse() - } - - @Test fun setChildrenTranslationYExcludingMediaView_mediaViewIsNotTranslated() { val translationY = 1234f diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index 21a28222ccf4..d61ca697642a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -39,10 +39,10 @@ import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.doze.util.BurnInHelperKt; import com.android.systemui.dump.DumpManager; @@ -51,6 +51,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; +import com.android.systemui.scene.SceneTestUtils; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -75,6 +77,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected MockitoSession mStaticMockSession; + protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this); + protected @Mock BouncerInteractor mBouncerInteractor; protected @Mock LockIconView mLockIconView; protected @Mock AnimatedStateListDrawable mIconDrawable; protected @Mock Context mContext; @@ -93,6 +97,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected @Mock AuthRippleController mAuthRippleController; protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); protected FakeFeatureFlags mFeatureFlags; + protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; protected LockIconViewController mUnderTest; @@ -148,6 +153,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mFeatureFlags.set(MIGRATE_LOCK_ICON, false); mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false); + mUnderTest = new LockIconViewController( mStatusBarStateController, mKeyguardUpdateMonitor, @@ -168,7 +174,9 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(), mFeatureFlags, mPrimaryBouncerInteractor, - mContext + mContext, + () -> mBouncerInteractor, + mSceneTestUtils.getSceneContainerFlags() ); } @@ -227,8 +235,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { setupLockIconViewMocks(); } - protected void init(boolean useMigrationFlag) { - mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag); + protected void init(boolean useDozeMigrationFlag) { + mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag); mUnderTest.setLockIconView(mLockIconView); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java index a2dc7763d0ea..4bacc3dfca0d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java @@ -17,13 +17,16 @@ package com.android.keyguard; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -41,6 +44,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; import com.android.systemui.doze.util.BurnInHelperKt; +import com.android.systemui.statusbar.StatusBarState; import org.junit.Test; import org.junit.runner.RunWith; @@ -180,13 +184,14 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { } @Test - public void testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() { + public void testLockIcon_clearsIconWhenUnlocked() { // GIVEN udfps not enrolled setupUdfps(); when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false); // GIVEN starting state for the lock icon setupShowLockIcon(); + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); // GIVEN lock icon controller is initialized and view is attached init(/* useMigrationFlag= */false); @@ -194,7 +199,7 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { reset(mLockIconView); // WHEN the dozing state changes - mStatusBarStateListener.onDozingChanged(true /* isDozing */); + mStatusBarStateListener.onDozingChanged(false /* isDozing */); // THEN the icon is cleared verify(mLockIconView).clearIcon(); @@ -400,6 +405,49 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { // THEN uses perform haptic feedback verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); + } + + @Test + public void longPress_showBouncer_sceneContainerNotEnabled() { + init(/* useMigrationFlag= */ false); + mSceneTestUtils.getSceneContainerFlags().setEnabled(false); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); + + // WHEN longPress + mUnderTest.onLongPress(); + + // THEN show primary bouncer via keyguard view controller, not scene container + verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean()); + verify(mBouncerInteractor, never()).showOrUnlockDevice(any()); + } + + @Test + public void longPress_showBouncer() { + init(/* useMigrationFlag= */ false); + mSceneTestUtils.getSceneContainerFlags().setEnabled(true); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); + + // WHEN longPress + mUnderTest.onLongPress(); + + // THEN show primary bouncer + verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); + verify(mBouncerInteractor).showOrUnlockDevice(any()); + } + + @Test + public void longPress_falsingTriggered_doesNotShowBouncer() { + init(/* useMigrationFlag= */ false); + mSceneTestUtils.getSceneContainerFlags().setEnabled(true); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true); + + // WHEN longPress + mUnderTest.onLongPress(); + // THEN don't show primary bouncer + verify(mBouncerInteractor, never()).showOrUnlockDevice(any()); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt index c372f4555f61..12135182ac15 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.statusbar.StatusBarState import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -42,7 +43,7 @@ class LockIconViewControllerWithCoroutinesTest : LockIconViewControllerBaseTest( /** After migration, replaces LockIconViewControllerTest version */ @Test - fun testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() = + fun testLockIcon_clearsIconWhenUnlocked() = runBlocking(IMMEDIATE) { // GIVEN udfps not enrolled setupUdfps() @@ -50,14 +51,14 @@ class LockIconViewControllerWithCoroutinesTest : LockIconViewControllerBaseTest( // GIVEN starting state for the lock icon setupShowLockIcon() + whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE) // GIVEN lock icon controller is initialized and view is attached init(/* useMigrationFlag= */ true) reset(mLockIconView) // WHEN the dozing state changes - mUnderTest.mIsDozingCallback.accept(true) - + mUnderTest.mIsDozingCallback.accept(false) // THEN the icon is cleared verify(mLockIconView).clearIcon() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt index 59c7e7669b63..8faf715521f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt @@ -166,6 +166,9 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { waitForIdleSync() verify(controller).onLaunchAnimationCancelled() verify(controller, never()).onLaunchAnimationStart(anyBoolean()) + verify(listener).onLaunchAnimationCancelled() + verify(listener, never()).onLaunchAnimationStart() + assertNull(runner.delegate) } @Test @@ -176,6 +179,9 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { waitForIdleSync() verify(controller).onLaunchAnimationCancelled() verify(controller, never()).onLaunchAnimationStart(anyBoolean()) + verify(listener).onLaunchAnimationCancelled() + verify(listener, never()).onLaunchAnimationStart() + assertNull(runner.delegate) } @Test @@ -194,6 +200,15 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { } } + @Test + fun disposeRunner_delegateDereferenced() { + val runner = activityLaunchAnimator.createRunner(controller) + assertNotNull(runner.delegate) + runner.dispose() + waitForIdleSync() + assertNull(runner.delegate) + } + private fun fakeWindow(): RemoteAnimationTarget { val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */) val taskInfo = ActivityManager.RunningTaskInfo() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt index a7e7dd074a33..2b51ac5e3187 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.animation import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.core.animation.doOnEnd +import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.doOnEnd @@ -30,6 +31,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @SmallTest @RunWithLooper +@FlakyTest(bugId = 302149604) class AnimatorTestRuleOrderTest : SysuiTestCase() { @get:Rule val animatorTestRule = AnimatorTestRule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index 212dad78d5b2..c2e6db362035 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -242,6 +242,40 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test + fun animatesRootOnly() { + setUpRootWithChildren() + + val success = ViewHierarchyAnimator.animate( + rootView, + animateChildren = false + ) + // Change all bounds. + rootView.measure( + View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) + ) + rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */) + + assertTrue(success) + assertNotNull(rootView.getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) + // The initial values for the root view should be those of the previous layout, while the + // children views should be at the final values from the beginning. + checkBounds(rootView, l = 0, t = 0, r = 200, b = 100) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100) + checkBounds(rootView.getChildAt(1), l = 90, t = 0, r = 180, b = 100) + endAnimation(rootView) + assertNull(rootView.getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) + // The end values should be those of the latest layout. + checkBounds(rootView, l = 10, t = 20, r = 200, b = 120) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100) + checkBounds(rootView.getChildAt(1), l = 90, t = 0, r = 180, b = 100) + } + + @Test fun animatesInvisibleViews() { rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) rootView.visibility = View.INVISIBLE diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt index 0283382d02be..6ac84bc503a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt @@ -21,6 +21,7 @@ package com.android.systemui.authentication.data.repository import android.app.admin.DevicePolicyManager import android.content.Intent import android.content.pm.UserInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityModel @@ -40,13 +41,12 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class AuthenticationRepositoryTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index a102890db7b0..2f5f4606c5f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.authentication.domain.interactor import android.app.admin.DevicePolicyManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel @@ -35,11 +36,10 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class AuthenticationInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) 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 2bc0171939b4..885abcb72f1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -17,12 +17,15 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -79,7 +82,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; @@ -114,7 +116,6 @@ import java.util.Random; @RunWith(AndroidJUnit4.class) @RunWithLooper @SmallTest -@RoboPilotTest public class AuthControllerTest extends SysuiTestCase { private static final long REQUEST_ID = 22; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt index 73654609ad7f..d68a3131da4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt @@ -16,36 +16,18 @@ package com.android.systemui.biometrics -import android.app.ActivityManager -import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.internal.logging.UiEventLogger -import com.android.keyguard.KeyguardUpdateMonitor +import com.android.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.telephony.data.repository.FakeTelephonyRepository -import com.android.systemui.telephony.domain.interactor.TelephonyInteractor -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.interactor.GuestUserInteractor -import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode -import com.android.systemui.user.domain.interactor.RefreshUsersScheduler -import com.android.systemui.user.domain.interactor.UserInteractor -import com.android.systemui.util.mockito.mock +import com.android.systemui.user.domain.UserDomainLayerModule +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -58,91 +40,27 @@ import org.mockito.MockitoAnnotations @SmallTest @OptIn(ExperimentalCoroutinesApi::class) class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { - private val utils = SceneTestUtils(this) - private val testScope = utils.testScope - private val testDispatcher = utils.testDispatcher - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val featureFlags = FakeFeatureFlags() - private val keyguardRepository = FakeKeyguardRepository() - private val shadeRepository = FakeShadeRepository() - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - private val userSetupRepository = FakeUserSetupRepository() - private val userRepository = FakeUserRepository() - private val configurationRepository = FakeConfigurationRepository() - private val sharedNotificationContainerInteractor = - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - - private lateinit var detector: AuthDialogPanelInteractionDetector - private lateinit var shadeInteractor: ShadeInteractor - private lateinit var userInteractor: UserInteractor + + private val testComponent: TestComponent = + DaggerAuthDialogPanelInteractionDetectorTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FACE_AUTH_REFACTOR, false) + set(Flags.FULL_SCREEN_USER_SWITCHER, true) + }, + ) @Mock private lateinit var action: Runnable - @Mock private lateinit var activityManager: ActivityManager - @Mock private lateinit var activityStarter: ActivityStarter - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - @Mock private lateinit var guestInteractor: GuestUserInteractor - @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var manager: UserManager - @Mock private lateinit var uiEventLogger: UiEventLogger + + private val testScope = testComponent.testScope + private val shadeRepository = testComponent.shadeRepository + private val detector = testComponent.detector @Before fun setUp() { MockitoAnnotations.initMocks(this) - - featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) - featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) - - val refreshUsersScheduler = - RefreshUsersScheduler( - applicationScope = testScope.backgroundScope, - mainDispatcher = testDispatcher, - repository = userRepository, - ) - userInteractor = - UserInteractor( - applicationContext = context, - repository = userRepository, - activityStarter = activityStarter, - keyguardInteractor = - KeyguardInteractorFactory.create(featureFlags = featureFlags) - .keyguardInteractor, - featureFlags = featureFlags, - manager = manager, - headlessSystemUserMode = headlessSystemUserMode, - applicationScope = testScope.backgroundScope, - telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), - broadcastDispatcher = fakeBroadcastDispatcher, - keyguardUpdateMonitor = keyguardUpdateMonitor, - backgroundDispatcher = testDispatcher, - activityManager = activityManager, - refreshUsersScheduler = refreshUsersScheduler, - guestUserInteractor = guestInteractor, - uiEventLogger = uiEventLogger, - userRestrictionChecker = mock(), - ) - shadeInteractor = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository, - deviceProvisionedController, - userInteractor, - sharedNotificationContainerInteractor, - shadeRepository, - ) - detector = AuthDialogPanelInteractionDetector(testScope, { shadeInteractor }) } @Test @@ -215,4 +133,27 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { // Clean up job detector.disable() } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val detector: AuthDialogPanelInteractionDetector + val shadeRepository: FakeShadeRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + ): TestComponent + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java index 8547fa33c9de..714461b715d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java @@ -38,7 +38,6 @@ import android.view.Surface; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import kotlin.Unit; @@ -53,7 +52,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class BiometricDisplayListenerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt index ab5d8bea585b..39f0d570cb26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.biometrics import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.logging.BiometricMessageDeferralLogger -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import org.junit.Assert.assertEquals @@ -34,7 +33,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class FaceHelpMessageDeferralTest : SysuiTestCase() { val threshold = .75f diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index 57cf834bfa38..ef06e0efa01d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -53,7 +53,6 @@ import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestableContext import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository @@ -98,7 +97,6 @@ private const val SENSOR_ID = 1 private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3 @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class SideFpsControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt index 469f65a6cae5..e2aa984de46b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.biometrics 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 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager @@ -35,7 +34,6 @@ import org.mockito.Mock import org.mockito.junit.MockitoJUnit @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class UdfpsBpViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 21e614f3db3a..ebe13feb0f6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -29,7 +29,6 @@ import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater import android.view.MotionEvent import android.view.Surface -import android.view.Surface.ROTATION_0 import android.view.Surface.Rotation import android.view.View import android.view.WindowManager @@ -38,7 +37,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams @@ -46,7 +44,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -83,7 +80,6 @@ private const val SENSOR_HEIGHT = 60 @ExperimentalCoroutinesApi @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class UdfpsControllerOverlayTest : SysuiTestCase() { @@ -143,7 +139,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { ) { controllerOverlay = UdfpsControllerOverlay( context, - fingerprintManager, inflater, windowManager, accessibilityManager, @@ -157,7 +152,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { keyguardStateController, unlockedScreenOffAnimationController, udfpsDisplayMode, - secureSettings, REQUEST_ID, reason, controllerCallback, @@ -167,7 +161,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, - udfpsUtils, udfpsKeyguardAccessibilityDelegate, udfpsKeyguardViewModels, ) @@ -214,8 +207,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { val lp = layoutParamsCaptor.value assertThat(lp.x).isEqualTo(0) assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(SENSOR_WIDTH) - assertThat(lp.height).isEqualTo(SENSOR_HEIGHT) + assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) + assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) } } @@ -232,8 +225,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { val lp = layoutParamsCaptor.value assertThat(lp.x).isEqualTo(0) assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(SENSOR_WIDTH) - assertThat(lp.height).isEqualTo(SENSOR_HEIGHT) + assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) + assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) } } @@ -249,9 +242,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { // Sensor should be in the bottom left corner in ROTATION_90. val lp = layoutParamsCaptor.value assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(DISPLAY_WIDTH - SENSOR_WIDTH) - assertThat(lp.width).isEqualTo(SENSOR_HEIGHT) - assertThat(lp.height).isEqualTo(SENSOR_WIDTH) + assertThat(lp.y).isEqualTo(0) + assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) + assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) } } @@ -266,10 +259,10 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { // Sensor should be in the top right corner in ROTATION_270. val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(DISPLAY_HEIGHT - SENSOR_HEIGHT) + assertThat(lp.x).isEqualTo(0) assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(SENSOR_HEIGHT) - assertThat(lp.height).isEqualTo(SENSOR_WIDTH) + assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) + assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) } } @@ -347,11 +340,10 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun smallOverlayOnEnrollmentWithA11y() = withRotation(ROTATION_0) { + fun smallOverlayOnEnrollmentWithA11y() = withRotation(Surface.ROTATION_0) { withReason(REASON_ENROLL_ENROLLING) { // When a11y enabled during enrollment whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true) - whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true) controllerOverlay.show(udfpsController, overlayParams) verify(windowManager).addView( @@ -365,22 +357,4 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height()) } } - - @Test - fun fullScreenOverlayWithNewTouchDetectionEnabled() = withRotation(ROTATION_0) { - withReason(REASON_AUTH_KEYGUARD) { - whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true) - - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager).addView( - eq(controllerOverlay.overlayView), - layoutParamsCaptor.capture() - ) - - // Layout params should use natural display width and height - val lp = layoutParamsCaptor.value - assertThat(lp.width).isEqualTo(overlayParams.naturalDisplayWidth) - assertThat(lp.height).isEqualTo(overlayParams.naturalDisplayHeight) - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index ee3bd0d79ad8..dcb53984ad87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -20,12 +20,15 @@ import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPR import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; + import static com.android.internal.util.FunctionalUtils.ThrowingConsumer; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; @@ -74,8 +77,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; @@ -87,13 +88,13 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -103,7 +104,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecution; import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.SystemClock; @@ -120,12 +120,10 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import javax.inject.Provider; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class UdfpsControllerTest extends SysuiTestCase { @@ -205,8 +203,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock - private AlternateUdfpsTouchProvider mAlternateTouchProvider; - @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor; @Mock private SinglePointerTouchProcessor mSinglePointerTouchProcessor; @@ -215,8 +211,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock - private SecureSettings mSecureSettings; - @Mock private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; @Mock private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels; @@ -238,7 +232,6 @@ public class UdfpsControllerTest extends SysuiTestCase { private ScreenLifecycle.Observer mScreenObserver; private FingerprintSensorPropertiesInternal mOpticalProps; private FingerprintSensorPropertiesInternal mUltrasonicProps; - private UdfpsUtils mUdfpsUtils; @Mock private InputManager mInputManager; @Mock @@ -249,8 +242,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mContext.getOrCreateTestableResources() .addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false); - mUdfpsUtils = new UdfpsUtils(); - when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)) .thenReturn(mUdfpsView); when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) @@ -291,24 +282,13 @@ public class UdfpsControllerTest extends SysuiTestCase { // Create a fake background executor. mBiometricExecutor = new FakeExecutor(new FakeSystemClock()); - initUdfpsController(true /* hasAlternateTouchProvider */); - } - - - private void initUdfpsController(boolean hasAlternateTouchProvider) { - initUdfpsController(mOpticalProps, hasAlternateTouchProvider); + initUdfpsController(mOpticalProps); } - private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps, - boolean hasAlternateTouchProvider) { + private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps) { reset(mFingerprintManager); reset(mScreenLifecycle); - final Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider = - hasAlternateTouchProvider ? Optional.of( - (Provider<AlternateUdfpsTouchProvider>) () -> mAlternateTouchProvider) - : Optional.empty(); - mUdfpsController = new UdfpsController( mContext, new FakeExecution(), @@ -338,15 +318,12 @@ public class UdfpsControllerTest extends SysuiTestCase { mSystemUIDialogManager, mLatencyTracker, mActivityLaunchAnimator, - alternateTouchProvider, mBiometricExecutor, mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker, mAlternateBouncerInteractor, - mSecureSettings, mInputManager, - mUdfpsUtils, mock(KeyguardFaceAuthInteractor.class), mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels @@ -373,17 +350,15 @@ public class UdfpsControllerTest extends SysuiTestCase { public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); // WHEN ACTION_DOWN is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); + MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); @@ -407,16 +382,14 @@ public class UdfpsControllerTest extends SysuiTestCase { throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); // WHEN ACTION_MOVE is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); if (stale) { mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId); @@ -435,22 +408,22 @@ public class UdfpsControllerTest extends SysuiTestCase { public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); - // WHEN multiple touches are received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + // GIVEN that the overlay is showing + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); + MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.second); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mBiometricExecutor.runAllReady(); moveEvent.recycle(); @@ -592,22 +565,17 @@ public class UdfpsControllerTest extends SysuiTestCase { private static class TestParams { public final FingerprintSensorPropertiesInternal sensorProps; - public final boolean hasAlternateTouchProvider; - TestParams(FingerprintSensorPropertiesInternal sensorProps, - boolean hasAlternateTouchProvider) { + TestParams(FingerprintSensorPropertiesInternal sensorProps) { this.sensorProps = sensorProps; - this.hasAlternateTouchProvider = hasAlternateTouchProvider; } } private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) { for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps, mUltrasonicProps)) { - for (boolean hasAlternateTouchProvider : new boolean[]{false, true}) { - initUdfpsController(sensorProps, hasAlternateTouchProvider); - testParamsConsumer.accept(new TestParams(sensorProps, hasAlternateTouchProvider)); - } + initUdfpsController(sensorProps); + testParamsConsumer.accept(new TestParams(sensorProps)); } } @@ -620,23 +588,33 @@ public class UdfpsControllerTest extends SysuiTestCase { private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized( TestParams testParams) throws RemoteException { reset(mUdfpsView); + when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner. + final int pointerId = 0; final int displayWidth = 1080; final int displayHeight = 1920; - final float scaleFactor = 0.75f; // This means the native resolution is 1440x2560. + final float scaleFactor = 1f; // This means the native resolution is 1440x2560. final float touchMinor = 10f; final float touchMajor = 20f; + final float orientation = 30f; // Expecting a touch at the very bottom right corner in native orientation and resolution. - final int expectedX = (int) (displayWidth / scaleFactor); - final int expectedY = (int) (displayHeight / scaleFactor); + final float expectedX = displayWidth / scaleFactor; + final float expectedY = displayHeight / scaleFactor; final float expectedMinor = touchMinor / scaleFactor; final float expectedMajor = touchMajor / scaleFactor; // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); + + // GIVEN a valid touch on sensor + NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth, + displayHeight, touchMinor, touchMajor, orientation, 0L, 0L); + TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch( + InteractionEvent.DOWN, 1, touchData); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + processorDownResult); // Show the overlay. mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, @@ -653,21 +631,12 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); mBiometricExecutor.runAllReady(); event.recycle(); - event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - if (testParams.hasAlternateTouchProvider) { - verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX), - eq(expectedY), eq(expectedMinor), eq(expectedMajor)); - } else { - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor)); - } + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), + eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), + anyBoolean()); // Test ROTATION_90 - reset(mAlternateTouchProvider); reset(mFingerprintManager); mUdfpsController.updateOverlayParams(testParams.sensorProps, new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, @@ -676,21 +645,12 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); mBiometricExecutor.runAllReady(); event.recycle(); - event = obtainMotionEvent(ACTION_MOVE, displayHeight, 0, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - if (testParams.hasAlternateTouchProvider) { - verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX), - eq(expectedY), eq(expectedMinor), eq(expectedMajor)); - } else { - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor)); - } + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), + eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), + anyBoolean()); // Test ROTATION_270 - reset(mAlternateTouchProvider); reset(mFingerprintManager); mUdfpsController.updateOverlayParams(testParams.sensorProps, new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, @@ -699,21 +659,12 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); mBiometricExecutor.runAllReady(); event.recycle(); - event = obtainMotionEvent(ACTION_MOVE, 0, displayWidth, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - if (testParams.hasAlternateTouchProvider) { - verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX), - eq(expectedY), eq(expectedMinor), eq(expectedMajor)); - } else { - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor)); - } + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), + eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), + anyBoolean()); // Test ROTATION_180 - reset(mAlternateTouchProvider); reset(mFingerprintManager); mUdfpsController.updateOverlayParams(testParams.sensorProps, new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, @@ -723,18 +674,10 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); mBiometricExecutor.runAllReady(); event.recycle(); - event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - if (testParams.hasAlternateTouchProvider) { - verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX), - eq(expectedY), eq(expectedMinor), eq(expectedMajor)); - } else { - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor)); - } + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), + eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), + anyBoolean()); } @Test @@ -743,46 +686,36 @@ public class UdfpsControllerTest extends SysuiTestCase { } private void fingerDownParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mLatencyTracker, + reset(mUdfpsView, mFingerprintManager, mLatencyTracker, mKeyguardUpdateMonitor); + when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - // GIVEN that the overlay is showing + final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, + 0L); + final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( + InteractionEvent.DOWN, 1 /* pointerId */, touchData); + + initUdfpsController(testParams.sensorProps); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); // WHEN ACTION_DOWN is received + verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + processorResultDown); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - mFgExecutor.runAllReady(); - // THEN the touch provider is notified about onPointerDown. - if (testParams.hasAlternateTouchProvider) { - verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f), - eq(0f)); - verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), - anyInt(), anyFloat(), anyFloat()); - verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID)); - } else { - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f)); - verify(mAlternateTouchProvider, never()).onPointerDown(anyInt(), anyInt(), anyInt(), - anyFloat(), anyFloat()); - } + verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); // AND display configuration begins if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { @@ -799,33 +732,20 @@ public class UdfpsControllerTest extends SysuiTestCase { // AND onDisplayConfigured notifies FingerprintManager about onUiReady mOnDisplayConfiguredCaptor.getValue().run(); mBiometricExecutor.runAllReady(); - if (testParams.hasAlternateTouchProvider) { - InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker); - inOrder.verify(mAlternateTouchProvider).onUiReady(); - inOrder.verify(mLatencyTracker).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mFingerprintManager, never()).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt()); - } else { - InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); - inOrder.verify(mFingerprintManager).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId)); - inOrder.verify(mLatencyTracker).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mAlternateTouchProvider, never()).onUiReady(); - } + InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); + inOrder.verify(mFingerprintManager).onUdfpsUiEvent( + eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID), + eq(testParams.sensorProps.sensorId)); + inOrder.verify(mLatencyTracker).onActionEnd( + eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); } else { verify(mFingerprintManager, never()).onUdfpsUiEvent( eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt()); - verify(mAlternateTouchProvider, never()).onUiReady(); verify(mLatencyTracker, never()).onActionEnd( eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); } } - - @Test public void aodInterrupt() { runWithAllParams(this::aodInterruptParameterized); @@ -833,8 +753,9 @@ public class UdfpsControllerTest extends SysuiTestCase { private void aodInterruptParameterized(TestParams testParams) throws RemoteException { mUdfpsController.cancelAodSendFingerUpAction(); - reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mKeyguardUpdateMonitor); + reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); + when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); // GIVEN that the overlay is showing and screen is on and fp is running mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, @@ -854,19 +775,8 @@ public class UdfpsControllerTest extends SysuiTestCase { } mBiometricExecutor.runAllReady(); - if (testParams.hasAlternateTouchProvider) { - verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), - eq(3f) /* minor */, eq(2f) /* major */); - verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), - anyInt(), anyFloat(), anyFloat()); - verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID)); - } else { - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */, - eq(2f) /* major */); - verify(mAlternateTouchProvider, never()).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat()); - } + verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); } @Test @@ -906,10 +816,12 @@ public class UdfpsControllerTest extends SysuiTestCase { private void onFingerUp_displayConfigurationParameterized(TestParams testParams) throws RemoteException { reset(mUdfpsView); + when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); + + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); @@ -917,7 +829,8 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.isDisplayConfigured()).thenReturn(true); // WHEN up-action received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.second); MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); mBiometricExecutor.runAllReady(); @@ -930,7 +843,8 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.isDisplayConfigured()).thenReturn(false); // WHEN up-action received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.second); MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); mBiometricExecutor.runAllReady(); @@ -1014,16 +928,19 @@ public class UdfpsControllerTest extends SysuiTestCase { private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams) throws RemoteException { reset(mUdfpsView); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); + when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, + BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { // Configure UdfpsView to accept the ACTION_UP event when(mUdfpsView.isDisplayConfigured()).thenReturn(true); @@ -1032,7 +949,8 @@ public class UdfpsControllerTest extends SysuiTestCase { } // WHEN ACTION_UP is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.second); MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); mBiometricExecutor.runAllReady(); @@ -1042,16 +960,13 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.isDisplayConfigured()).thenReturn(false); // WHEN ACTION_DOWN is received + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); - // WHEN ACTION_MOVE is received - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); mFgExecutor.runAllReady(); if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { @@ -1121,24 +1036,16 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException { - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - - // GIVEN that the overlay is showing and a11y touch exploration enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true); // WHEN ACTION_HOVER is received + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent); enterEvent.recycle(); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0); - mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent); - moveEvent.recycle(); // THEN tick haptic is played verify(mVibrator).vibrate( @@ -1158,24 +1065,16 @@ public class UdfpsControllerTest extends SysuiTestCase { public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled_oneWayHapticsEnabled() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - // GIVEN that the overlay is showing and a11y touch exploration enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true); // WHEN ACTION_HOVER is received - verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent); enterEvent.recycle(); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0); - mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent); - moveEvent.recycle(); // THEN context click haptic is played verify(mVibrator).performHapticFeedback( @@ -1186,26 +1085,16 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException { - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); // WHEN ACTION_DOWN is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); // THEN NO haptic played verify(mVibrator, never()).vibrate( @@ -1220,80 +1109,26 @@ public class UdfpsControllerTest extends SysuiTestCase { public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled__oneWayHapticsEnabled() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); // WHEN ACTION_DOWN is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + touchProcessorResult.first); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); // THEN NO haptic played verify(mVibrator, never()).performHapticFeedback(any(), anyInt()); } @Test - public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath() - throws RemoteException { - // Disable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(false); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // AND ACTION_MOVE is received - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // AND ACTION_UP is received - MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - - // THEN the old FingerprintManager path is invoked. - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(), - anyFloat(), anyFloat()); - verify(mFingerprintManager).onPointerUp(anyLong(), anyInt()); - } - - @Test public void fingerDown_falsingManagerInformed() throws RemoteException { final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenAcceptFingerDownEvent(); + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); // WHEN ACTION_DOWN is received when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( @@ -1307,85 +1142,46 @@ public class UdfpsControllerTest extends SysuiTestCase { verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION); } - @Test - public void onTouch_withNewTouchDetection_shouldCallNewFingerprintManagerPath() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> processorResultDownAndUp = - givenAcceptFingerDownEvent(); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDownAndUp.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // AND ACTION_UP is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDownAndUp.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - - // THEN the new FingerprintManager path is invoked. - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - } - - private Pair<TouchProcessorResult, TouchProcessorResult> givenAcceptFingerDownEvent() + private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent( + InteractionEvent event1, InteractionEvent event2, boolean a11y) throws RemoteException { final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, 0L); final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1 /* pointerId */, touchData); + event1, 1 /* pointerId */, touchData); final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.UP, 1 /* pointerId */, touchData); - - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); + event2, 1 /* pointerId */, touchData); // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); + initUdfpsController(mOpticalProps); // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); + when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + if (a11y) { + verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); + } else { + verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + } return new Pair<>(processorResultDown, processorResultUp); } @Test - public void onTouch_WithNewTouchDetection_forwardToKeyguard() throws RemoteException { + public void onTouch_forwardToKeyguard() throws RemoteException { final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, 0L); final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData); - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false); - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -1401,47 +1197,23 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN the touch is forwarded to Keyguard verify(mStatusBarKeyguardViewManager).onTouch(downEvent); - downEvent.recycle(); } @Test - public void onTouch_withNewTouchDetection_pilferPointer() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1 /* pointerId */, touchData); - - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + public void onTouch_pilferPointer() throws RemoteException { + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); // WHEN ACTION_DOWN is received when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); + touchProcessorResult.first); MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); mBiometricExecutor.runAllReady(); // WHEN ACTION_MOVE is received after - final TouchProcessorResult processorResultUnchanged = - new TouchProcessorResult.ProcessedTouch( - InteractionEvent.UNCHANGED, 1 /* pointerId */, touchData); when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultUnchanged); + touchProcessorResult.second); event.setAction(ACTION_MOVE); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); mBiometricExecutor.runAllReady(); @@ -1452,25 +1224,13 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void onTouch_withNewTouchDetection_doNotPilferPointer() throws RemoteException { + public void onTouch_doNotPilferPointer() throws RemoteException { final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, 0L); final TouchProcessorResult processorResultUnchanged = new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED, - 1 /* pointerId */, touchData); - - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); + -1 /* pointerId */, touchData); - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // Configure UdfpsView to not accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -1490,36 +1250,17 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void onTouch_withNewTouchDetection_pilferPointerWhenAltBouncerShowing() + public void onTouch_pilferPointerWhenAltBouncerShowing() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultUnchanged = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED, - 1 /* pointerId */, touchData); - - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // Configure UdfpsView to not accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); - // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); + // WHEN alternate bouncer is showing when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); // WHEN ACTION_DOWN is received and touch is not within sensor when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultUnchanged); + touchProcessorResult.first); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); @@ -1530,32 +1271,10 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void onTouch_withNewTouchDetection_doNotProcessTouchWhenPullingUpBouncer() + public void onTouch_doNotProcessTouchWhenPullingUpBouncer() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultMove = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - 1 /* pointerId */, touchData); - - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // Configure UdfpsView to accept the ACTION_MOVE event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - - // GIVEN that the alternate bouncer is not showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true); @@ -1563,7 +1282,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // WHEN ACTION_MOVE is received and touch is within sensor when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultMove); + touchProcessorResult.first); MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mBiometricExecutor.runAllReady(); @@ -1577,40 +1296,19 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test - public void onTouch_withNewTouchDetection_qsDrag_processesTouchWhenAlternateBouncerVisible() + public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultMove = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - 1 /* pointerId */, touchData); - - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // Configure UdfpsView to accept the ACTION_MOVE event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); + final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = + givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - // GIVEN swipe down for QS when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false); when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f); // WHEN ACTION_MOVE is received and touch is within sensor when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultMove); + touchProcessorResult.first); MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mBiometricExecutor.runAllReady(); @@ -1688,43 +1386,4 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN vibrate is used verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); } - - @Test - public void aodInterrupt_withNewTouchDetection() throws RemoteException { - mUdfpsController.cancelAodSendFingerUpAction(); - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - 1 /* pointerId */, touchData); - - // Enable new touch detection. - when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); - - // GIVEN that the overlay is showing and screen is on and fp is running - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, 0, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - - // WHEN fingerprint is requested because of AOD interrupt - mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); - - // Check case where touch driver sends touch to UdfpsView as well - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - - mBiometricExecutor.runAllReady(); - - // THEN only one onPointerDown is sent - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java index 280bfdf1aa8c..cd9189bef7f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java @@ -27,7 +27,6 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import org.junit.Test; @@ -38,7 +37,6 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest -@RoboPilotTest public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase { @Test public void testUdfpsBottomSpacerHeightForPortrait() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java index 1afb223b4089..5239966f1923 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java @@ -30,7 +30,6 @@ import android.testing.TestableLooper.RunWithLooper; 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.util.concurrency.FakeExecution; @@ -41,7 +40,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class UdfpsDisplayModeTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java index 3276e6624004..e512adcc0542 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java @@ -30,7 +30,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeExpansionChangeEvent; @@ -116,7 +115,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { } public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(false, false); + return createUdfpsKeyguardViewController(false); } public void captureKeyGuardViewManagerCallback() { @@ -126,8 +125,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { } protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController( - boolean useModernBouncer, boolean useExpandedOverlay) { - mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, useExpandedOverlay); + boolean useModernBouncer) { UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy( mView, mStatusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java index 8508f45b02f3..21928cd606ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java @@ -18,27 +18,22 @@ package com.android.systemui.biometrics; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.testing.TestableLooper; -import android.view.MotionEvent; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.statusbar.StatusBarState; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -46,8 +41,7 @@ public class UdfpsKeyguardViewLegacyControllerTest extends UdfpsKeyguardViewLegacyControllerBaseTest { @Override public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(/* useModernBouncer */ false, - /* useExpandedOverlay */ false); + return createUdfpsKeyguardViewController(/* useModernBouncer */ false); } @Test @@ -218,37 +212,4 @@ public class UdfpsKeyguardViewLegacyControllerTest extends sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); assertTrue(mController.shouldPauseAuth()); } - - @Test - // TODO(b/259264861): Tracking Bug - public void testUdfpsExpandedOverlayOn() { - // GIVEN view is attached and useExpandedOverlay is true - mController = createUdfpsKeyguardViewController(false, true); - mController.onViewAttached(); - captureKeyGuardViewManagerCallback(); - - // WHEN a touch is received - mKeyguardViewManagerCallback.onTouch( - MotionEvent.obtain(0, 0, 0, 0, 0, 0)); - - // THEN udfpsController onTouch is not called - assertTrue(mView.mUseExpandedOverlay); - verify(mUdfpsController, never()).onTouch(any()); - } - - @Test - // TODO(b/259264861): Tracking Bug - public void testUdfpsExpandedOverlayOff() { - // GIVEN view is attached and useExpandedOverlay is false - mController.onViewAttached(); - captureKeyGuardViewManagerCallback(); - - // WHEN a touch is received - mKeyguardViewManagerCallback.onTouch( - MotionEvent.obtain(0, 0, 0, 0, 0, 0)); - - // THEN udfpsController onTouch is called - assertFalse(mView.mUseExpandedOverlay); - verify(mUdfpsController).onTouch(any()); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index 17f435b75d5a..02ee53879ea0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -21,7 +21,6 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel -import com.android.systemui.RoboPilotTest import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor @@ -58,7 +57,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) @SmallTest -@RoboPilotTest @TestableLooper.RunWithLooper @kotlinx.coroutines.ExperimentalCoroutinesApi class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : @@ -108,10 +106,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : mock(SystemClock::class.java), mKeyguardUpdateMonitor, ) - return createUdfpsKeyguardViewController( - /* useModernBouncer */ true, /* useExpandedOverlay */ - false - ) + return createUdfpsKeyguardViewController(/* useModernBouncer */ true) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt index 6d552544fee7..8b374ae54127 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt @@ -21,7 +21,6 @@ import android.testing.TestableLooper import android.view.MotionEvent 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.biometrics.UdfpsController.UdfpsOverlayController import com.android.systemui.statusbar.commandline.CommandRegistry @@ -40,7 +39,6 @@ import org.mockito.Mockito.`when` as whenEver import org.mockito.junit.MockitoJUnit @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class UdfpsShellTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt index ebadfc78a079..9fbe09619ff1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt @@ -16,8 +16,6 @@ package com.android.systemui.biometrics -import android.graphics.PointF -import android.graphics.RectF import android.hardware.biometrics.SensorLocationInternal import android.testing.TestableLooper import android.testing.ViewUtils @@ -26,7 +24,6 @@ import android.view.Surface import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.util.mockito.any @@ -43,14 +40,12 @@ import org.mockito.Mockito.never import org.mockito.Mockito.nullable import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit -import org.mockito.Mockito.`when` as whenever private const val SENSOR_X = 50 private const val SENSOR_Y = 250 private const val SENSOR_RADIUS = 10 @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class UdfpsViewTest : SysuiTestCase() { @@ -82,56 +77,7 @@ class UdfpsViewTest : SysuiTestCase() { ViewUtils.detachView(view) } - @Test - fun layoutSizeFitsSensor() { - val params = withArgCaptor<RectF> { - verify(animationViewController).onSensorRectUpdated(capture()) - } - assertThat(params.width()).isAtLeast(2f * SENSOR_RADIUS) - assertThat(params.height()).isAtLeast(2f * SENSOR_RADIUS) - } - - @Test - fun isWithinSensorAreaAndPaused() = isWithinSensorArea(paused = true) - - @Test - fun isWithinSensorAreaAndNotPaused() = isWithinSensorArea(paused = false) - - private fun isWithinSensorArea(paused: Boolean) { - whenever(animationViewController.shouldPauseAuth()).thenReturn(paused) - whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f)) - val end = (SENSOR_RADIUS * 2) - 1 - for (x in 1 until end) { - for (y in 1 until end) { - assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isEqualTo(!paused) - } - } - } - - @Test - fun isWithinSensorAreaWhenTranslated() { - val offset = PointF(100f, 200f) - whenever(animationViewController.touchTranslation).thenReturn(offset) - val end = (SENSOR_RADIUS * 2) - 1 - for (x in 0 until offset.x.toInt() step 2) { - for (y in 0 until offset.y.toInt() step 2) { - assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isFalse() - } - } - for (x in offset.x.toInt() + 1 until offset.x.toInt() + end) { - for (y in offset.y.toInt() + 1 until offset.y.toInt() + end) { - assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isTrue() - } - } - } - - @Test - fun isNotWithinSensorArea() { - whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f)) - assertThat(view.isWithinSensorArea(SENSOR_RADIUS * 2.5f, SENSOR_RADIUS.toFloat())) - .isFalse() - assertThat(view.isWithinSensorArea(SENSOR_RADIUS.toFloat(), SENSOR_RADIUS * 2.5f)).isFalse() - } + // TODO: Add test to verify view is size of screen @Test fun startAndStopIllumination() { 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 0d172706b127..2d8adca04a5e 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 @@ -19,7 +19,6 @@ package com.android.systemui.bouncer.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AlternateBouncerInteractorTest : SysuiTestCase() { private lateinit var underTest: AlternateBouncerInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index a9ba36a9fde4..915661b8a776 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -38,11 +39,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BouncerInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt index a81ca86f338d..4aea4f329858 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.bouncer.domain.interactor import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import org.junit.Before import org.junit.Test @@ -29,7 +28,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() { private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt index cb0b74f3015d..2018e614ba00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.ui.BouncerView @@ -43,7 +42,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { private lateinit var repository: FakeKeyguardBouncerRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index b5177e1587ee..8e1f5ac58b68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -29,10 +30,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class AuthMethodBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index b75355a82406..2c97809bf367 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel @@ -35,11 +36,10 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt index 333bd214fe9a..802f8e6043fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -50,7 +49,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardBouncerViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 0926399a8617..ba8dcef683c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -35,11 +36,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PasswordBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 2e7c9aaa67e8..bfaa6edefdca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -38,11 +39,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PatternBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 255bbe3ae231..7873899f2d35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -37,11 +38,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PinBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -92,7 +92,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onShown() - assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(ENTER_YOUR_PIN) assertThat(pin).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(underTest.authenticationMethod) @@ -209,7 +209,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() assertThat(pin).isEmpty() - assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -227,7 +227,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onPinButtonClicked(4) underTest.onPinButtonClicked(5) // PIN is now wrong! underTest.onAuthenticateButtonClicked() - assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) assertThat(pin).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -273,7 +273,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { ) // PIN is now wrong! assertThat(pin).isEmpty() - assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt index 4c279ea08fd7..55016bb1fc07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.ui.viewmodel.EntryToken.ClearAll @@ -16,14 +17,13 @@ import java.lang.Character.isDigit import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 /** * This test uses a mnemonic code to create and verify PinInput instances: strings of digits [0-9] * for [Digit] tokens, as well as a `C` for the [ClearAll] token. */ @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PinInputViewModelTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt new file mode 100644 index 000000000000..30a5497d0a14 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt @@ -0,0 +1,115 @@ +/* + * 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.content.pm.UserInfo +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.FakeLogBuffer +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CommunalTutorialRepositoryImplTest : SysuiTestCase() { + private lateinit var secureSettings: FakeSettings + private lateinit var userRepository: FakeUserRepository + private lateinit var userTracker: FakeUserTracker + private lateinit var logBuffer: LogBuffer + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + logBuffer = FakeLogBuffer.Factory.create() + secureSettings = FakeSettings() + userRepository = FakeUserRepository() + val listOfUserInfo = listOf(MAIN_USER_INFO) + userRepository.setUserInfos(listOfUserInfo) + + userTracker = FakeUserTracker() + userTracker.set( + userInfos = listOfUserInfo, + selectedUserIndex = 0, + ) + } + + @Test + fun tutorialSettingState_defaultToNotStarted() = + testScope.runTest { + val repository = initCommunalTutorialRepository() + val tutorialSettingState = collectLastValue(repository.tutorialSettingState)() + assertThat(tutorialSettingState) + .isEqualTo(Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED) + } + + @Test + fun tutorialSettingState_whenTutorialSettingsUpdatedToStarted() = + testScope.runTest { + val repository = initCommunalTutorialRepository() + setTutorialStateSetting(Settings.Secure.HUB_MODE_TUTORIAL_STARTED) + val tutorialSettingState = collectLastValue(repository.tutorialSettingState)() + assertThat(tutorialSettingState).isEqualTo(Settings.Secure.HUB_MODE_TUTORIAL_STARTED) + } + + @Test + fun tutorialSettingState_whenTutorialSettingsUpdatedToCompleted() = + testScope.runTest { + val repository = initCommunalTutorialRepository() + setTutorialStateSetting(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) + val tutorialSettingState = collectLastValue(repository.tutorialSettingState)() + assertThat(tutorialSettingState).isEqualTo(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) + } + + private fun initCommunalTutorialRepository(): CommunalTutorialRepositoryImpl { + return CommunalTutorialRepositoryImpl( + testScope.backgroundScope, + testDispatcher, + userRepository, + secureSettings, + userTracker, + logBuffer + ) + } + + private fun setTutorialStateSetting( + @Settings.Secure.HubModeTutorialState state: Int, + user: UserInfo = MAIN_USER_INFO + ) { + secureSettings.putIntForUser(Settings.Secure.HUB_MODE_TUTORIAL_STATE, state, user.id) + } + + companion object { + private val MAIN_USER_INFO = + UserInfo(/* id= */ 0, /* name= */ "primary", /* flags= */ UserInfo.FLAG_MAIN) + } +} 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 index 7fa828fbfadd..3df9cbb29e4a 100644 --- 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 @@ -9,7 +9,6 @@ import android.os.UserHandle import android.os.UserManager 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.broadcast.BroadcastDispatcher import com.android.systemui.coroutines.collectLastValue @@ -40,7 +39,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CommunalWidgetRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var appWidgetManager: AppWidgetManager 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 index ddf788e47bf2..cdc42e096830 100644 --- 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 @@ -19,7 +19,6 @@ 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.FakeCommunalRepository import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository @@ -37,7 +36,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class CommunalInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt new file mode 100644 index 000000000000..0a9a15e06b1b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt @@ -0,0 +1,113 @@ +/* + * 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 android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED +import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED +import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.settings.UserTracker +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class CommunalTutorialInteractorTest : SysuiTestCase() { + + @Mock private lateinit var userTracker: UserTracker + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + private lateinit var underTest: CommunalTutorialInteractor + private lateinit var keyguardRepository: FakeKeyguardRepository + private lateinit var keyguardInteractor: KeyguardInteractor + private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + val withDeps = KeyguardInteractorFactory.create() + keyguardInteractor = withDeps.keyguardInteractor + keyguardRepository = withDeps.repository + communalTutorialRepository = FakeCommunalTutorialRepository() + + underTest = + CommunalTutorialInteractor( + keyguardInteractor = keyguardInteractor, + communalTutorialRepository = communalTutorialRepository, + ) + + whenever(userTracker.userHandle).thenReturn(mock()) + } + + @Test + fun tutorialUnavailable_whenKeyguardNotVisible() = + testScope.runTest { + val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable) + communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED) + keyguardRepository.setKeyguardShowing(false) + assertThat(isTutorialAvailable).isFalse() + } + + @Test + fun tutorialUnavailable_whenTutorialIsCompleted() = + testScope.runTest { + val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable) + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) + assertThat(isTutorialAvailable).isFalse() + } + + @Test + fun tutorialAvailable_whenTutorialNotStarted() = + testScope.runTest { + val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable) + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED) + assertThat(isTutorialAvailable).isTrue() + } + + @Test + fun tutorialAvailable_whenTutorialIsStarted() = + testScope.runTest { + val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable) + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED) + assertThat(isTutorialAvailable).isTrue() + } +} 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 index a10eb295476e..41a8be9663b7 100644 --- 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 @@ -5,7 +5,6 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet 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.ui.view.layout.sections.DefaultCommunalWidgetSection import org.junit.Before @@ -15,7 +14,6 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt index 68ea7ebb3358..6c2e13691fba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt @@ -25,7 +25,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.demomode.DemoMode.ACTION_DEMO import com.android.systemui.demomode.DemoMode.COMMAND_STATUS import com.android.systemui.dump.DumpManager -import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.FakeGlobalSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -48,7 +48,7 @@ class DemoModeControllerTest : SysuiTestCase() { @Mock private lateinit var dumpManager: DumpManager - private val globalSettings = FakeSettings() + private val globalSettings = FakeGlobalSettings() private val testDispatcher = UnconfinedTestDispatcher() private val testScope = TestScope(testDispatcher) diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt index 8e8cbe4a7cf2..2c80035873f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt @@ -1,6 +1,7 @@ package com.android.systemui.deviceentry.data.repository import android.content.pm.UserInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase @@ -11,6 +12,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking @@ -19,14 +21,14 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DeviceEntryRepositoryTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils @@ -54,6 +56,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { keyguardBypassController = keyguardBypassController, keyguardStateController = keyguardStateController, ) + testScope.runCurrent() } @Test @@ -66,8 +69,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { assertThat(isUnlocked).isFalse() val captor = argumentCaptor<KeyguardStateController.Callback>() - Mockito.verify(keyguardStateController, Mockito.atLeastOnce()) - .addCallback(captor.capture()) + verify(keyguardStateController, Mockito.atLeastOnce()).addCallback(captor.capture()) whenever(keyguardStateController.isUnlocked).thenReturn(true) captor.value.onUnlockedChanged() @@ -98,7 +100,12 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { testScope.runTest { whenever(keyguardBypassController.isBypassEnabled).thenAnswer { false } whenever(keyguardBypassController.bypassEnabled).thenAnswer { false } - assertThat(underTest.isBypassEnabled()).isFalse() + withArgCaptor { + verify(keyguardBypassController).registerOnBypassStateChangedListener(capture()) + } + .onBypassStateChanged(false) + runCurrent() + assertThat(underTest.isBypassEnabled.value).isFalse() } @Test @@ -106,7 +113,12 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { testScope.runTest { whenever(keyguardBypassController.isBypassEnabled).thenAnswer { true } whenever(keyguardBypassController.bypassEnabled).thenAnswer { true } - assertThat(underTest.isBypassEnabled()).isTrue() + withArgCaptor { + verify(keyguardBypassController).registerOnBypassStateChangedListener(capture()) + } + .onBypassStateChanged(true) + runCurrent() + assertThat(underTest.isBypassEnabled.value).isTrue() } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt index 55582e123969..c13fde75a068 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.deviceentry.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -14,11 +15,10 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DeviceEntryInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -218,14 +218,14 @@ class DeviceEntryInteractorTest : SysuiTestCase() { fun isBypassEnabled_enabledInRepository_true() = testScope.runTest { utils.deviceEntryRepository.setBypassEnabled(true) - assertThat(underTest.isBypassEnabled()).isTrue() + assertThat(underTest.isBypassEnabled.value).isTrue() } @Test fun isBypassEnabled_disabledInRepository_false() = testScope.runTest { utils.deviceEntryRepository.setBypassEnabled(false) - assertThat(underTest.isBypassEnabled()).isFalse() + assertThat(underTest.isBypassEnabled.value).isFalse() } private fun switchToScene(sceneKey: SceneKey) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt index 781ad6b10b70..8a35ef11a364 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt @@ -6,7 +6,6 @@ import android.animation.ValueAnimator import android.view.View 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.complication.ComplicationHostViewController import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel @@ -30,7 +29,6 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class DreamOverlayAnimationsControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt index 21192fa11da3..2c6c793c97f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.dreams import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -30,7 +29,6 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class DreamOverlayCallbackControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 7c36642f5417..2af6566e993a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -38,7 +38,6 @@ import androidx.test.filters.SmallTest; import com.android.dream.lowlight.LowLightTransitionCoordinator; import com.android.keyguard.BouncerPanelExpansionCalculator; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; @@ -53,7 +52,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java index be7638ef31e6..d379dc6f3dc1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java @@ -26,7 +26,6 @@ import android.service.notification.StatusBarNotification; 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.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; @@ -38,7 +37,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 8379f73402cf..e5f997257cfa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -49,7 +49,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.complication.ComplicationLayoutEngine; import com.android.systemui.dreams.complication.HideComplicationTouchHandler; @@ -72,7 +71,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayServiceTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java index 2ef227c245c3..365f67b5e566 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java @@ -29,7 +29,6 @@ import static org.mockito.Mockito.when; 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.complication.Complication; import com.android.systemui.flags.FeatureFlags; @@ -48,7 +47,6 @@ import org.mockito.MockitoAnnotations; import java.util.Collection; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayStateControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java index 12cb332c7cc5..7ff345cdcf7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.verify; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -36,7 +35,6 @@ import org.mockito.MockitoAnnotations; import java.util.List; import java.util.concurrent.Executor; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayStatusBarItemsProviderTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index 9566e81ed08d..39db2beb4c44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -46,11 +46,10 @@ import android.view.View; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.FakeLogBuffer; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.NextAlarmController; @@ -72,7 +71,6 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java index d32788df30be..315a24bfd945 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java @@ -30,7 +30,6 @@ import androidx.concurrent.futures.CallbackToFutureAdapter; 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.complication.Complication; import com.android.systemui.dreams.DreamOverlayStateController; @@ -49,7 +48,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class HideComplicationTouchHandlerTest extends SysuiTestCase { 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 4a7700fe1ca2..e0c6ab20c6e1 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 @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify; 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.assist.AssistManager; import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations; import kotlinx.coroutines.CoroutineScope; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class AssistantAttentionConditionTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java index cd2efded7e85..480754c17616 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java @@ -31,7 +31,6 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.shared.condition.Condition; @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations; import kotlinx.coroutines.CoroutineScope; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamConditionTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index ffcaeee52596..3d1efa59a11b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams.touch; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; @@ -40,7 +41,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dreams.touch.scrim.ScrimController; @@ -64,7 +64,6 @@ import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.Optional; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java index ff6d97d3b380..6aa821f15ab1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java @@ -27,7 +27,6 @@ import android.view.MotionEvent; 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.shade.ShadeViewController; import com.android.systemui.shared.system.InputChannelCompat; @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class ShadeTouchHandlerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java index da393818c6f7..017fdbe8ac01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java @@ -26,7 +26,6 @@ import android.os.PowerManager; 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.shade.ShadeExpansionChangeEvent; import com.android.systemui.util.concurrency.FakeExecutor; @@ -38,7 +37,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class BouncerlessScrimControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java index 81f6fe3e24d3..4ee4a60fbeaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.when; 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.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -38,7 +37,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class ScrimManagerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt index 14c5ec0361f6..c12a581fb2c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt @@ -36,7 +36,6 @@ import java.util.function.Consumer import org.junit.Assert import org.junit.Before import org.junit.Test -import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyString @@ -479,7 +478,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { verify(flagManager, times(numReads)) .readFlagValue(eq(name), any<FlagSerializer<*>>()) verify(flagManager).nameToSettingsKey(eq(name)) - verify(globalSettings).putStringForUser(eq("key-$name"), eq(data), anyInt()) + verify(globalSettings).putString(eq("key-$name"), eq(data)) verify(flagManager).dispatchListenersAndMaybeRestart(eq(name), any()) } .verifyNoMoreInteractions() diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index b1cf0517ddd1..2d3f69d95204 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -78,6 +78,8 @@ import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.settings.FakeGlobalSettings; +import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SecureSettings; @@ -105,8 +107,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private LockPatternUtils mLockPatternUtils; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private TelephonyListenerManager mTelephonyListenerManager; - @Mock private GlobalSettings mGlobalSettings; - @Mock private SecureSettings mSecureSettings; + private GlobalSettings mGlobalSettings; + private SecureSettings mSecureSettings; @Mock private Resources mResources; @Mock private ConfigurationController mConfigurationController; @Mock private UserTracker mUserTracker; @@ -148,6 +150,9 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { when(mResources.getConfiguration()).thenReturn( getContext().getResources().getConfiguration()); + mGlobalSettings = new FakeGlobalSettings(); + mSecureSettings = new FakeSettings(); + mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext, mWindowManagerFuncs, mAudioManager, @@ -592,8 +597,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { UserInfo currentUser = mockCurrentUser(FLAG_ADMIN); when(mGlobalActionsDialogLite.getCurrentUser()).thenReturn(currentUser); - when(mGlobalSettings.getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, - 0, currentUser.id)).thenReturn(1); + mSecureSettings.putIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 1, + currentUser.id); GlobalActionsDialogLite.BugReportAction bugReportAction = mGlobalActionsDialogLite.makeBugReportActionForTesting(); @@ -605,8 +610,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { UserInfo currentUser = mockCurrentUser(0); when(mGlobalActionsDialogLite.getCurrentUser()).thenReturn(currentUser); - doReturn(1).when(mGlobalSettings) - .getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 0, currentUser.id); + mSecureSettings.putIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 1, + currentUser.id); GlobalActionsDialogLite.BugReportAction bugReportAction = mGlobalActionsDialogLite.makeBugReportActionForTesting(); 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 index 632d149c9520..f37306276848 100644 --- 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 @@ -16,23 +16,17 @@ 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.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @SmallTest @@ -40,108 +34,27 @@ import org.mockito.junit.MockitoJUnit 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 repository: FakeKeyEventRepository private lateinit var underTest: KeyEventInteractor @Before fun setup() { - keyguardInteractorWithDependencies = KeyguardInteractorFactory.create() + repository = FakeKeyEventRepository() underTest = KeyEventInteractor( - backActionInteractor, - keyguardKeyEventInteractor, + repository, ) } @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() - } + fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() = + runTest { + val isPowerDown by collectLastValue(underTest.isPowerButtonDown) + repository.setPowerButtonDown(false) + assertThat(isPowerDown).isFalse() + + repository.setPowerButtonDown(true) + assertThat(isPowerDown).isTrue() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt new file mode 100644 index 000000000000..af00a48d571b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.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 SysUIKeyEventHandlerTest : 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: SysUIKeyEventHandler + + @Before + fun setup() { + keyguardInteractorWithDependencies = KeyguardInteractorFactory.create() + underTest = + SysUIKeyEventHandler( + 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/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 291dda20fdc3..a6468234b9b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -16,7 +16,6 @@ package com.android.systemui.keyguard; -import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION; import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT; @@ -635,29 +634,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { } @Test - public void lockAfterSpecifiedAfterDreamStarted() { - int currentUserId = 99; - int userSpecificTimeout = 5999; - KeyguardUpdateMonitor.setCurrentUser(currentUserId); - - // set mDeviceInteractive to true - mViewMediator.onStartedWakingUp(WAKE_REASON_WAKE_MOTION, false); - mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false); - when(mLockPatternUtils.isSecure(currentUserId)).thenReturn(true); - when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L); - when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT, - KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, currentUserId)).thenReturn(userSpecificTimeout); - mSystemClock.setElapsedRealtime(0L); - ArgumentCaptor<PendingIntent> pendingIntent = ArgumentCaptor.forClass(PendingIntent.class); - - mViewMediator.onDreamingStarted(); - - verify(mAlarmManager).setExactAndAllowWhileIdle(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), - eq(Long.valueOf(userSpecificTimeout)), pendingIntent.capture()); - assertEquals(DELAYED_KEYGUARD_ACTION, pendingIntent.getValue().getIntent().getAction()); - } - - @Test public void testHideSurfaceBehindKeyguardMarksKeyguardNotGoingAway() { mViewMediator.hideSurfaceBehindKeyguard(); @@ -1023,7 +999,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator.setShowingLocked(false); when(mKeyguardStateController.isShowing()).thenReturn(false); - mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false); mViewMediator.onDreamingStarted(); assertFalse(mViewMediator.isShowingAndNotOccluded()); } @@ -1034,7 +1009,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator.setShowingLocked(true); when(mKeyguardStateController.isShowing()).thenReturn(true); - mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, true); mViewMediator.onDreamingStarted(); assertTrue(mViewMediator.isShowingAndNotOccluded()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt index cfee3b8f06ea..e20d3afaca53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt @@ -23,7 +23,6 @@ import android.content.Context import android.content.pm.PackageManager 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.camera.CameraGestureHelper import com.android.systemui.settings.UserTracker @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CameraQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt index 49168d087958..977f1db44258 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt @@ -25,14 +25,13 @@ import android.provider.Settings.Secure.ZEN_DURATION_PROMPT import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.EnableZenModeDialog -import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.shared.quickaffordance.ActivationState +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.ZenModeController import com.android.systemui.util.mockito.argumentCaptor @@ -61,7 +60,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt index c85c7f678536..548b5646f5d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.data.quickaffordance -import com.android.systemui.RoboPilotTest import com.android.systemui.animation.Expandable import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult import kotlinx.coroutines.flow.Flow @@ -25,7 +24,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.yield /** Fake implementation of a quick affordance data source. */ -@RoboPilotTest class FakeKeyguardQuickAffordanceConfig( override val key: String, private val pickerName: String = key, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt index c3e28ae84805..4ae144c03314 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt @@ -21,7 +21,6 @@ import android.content.Context import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.common.shared.model.Icon import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.statusbar.policy.FlashlightController @@ -42,7 +41,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt index ef56a9892060..7d68cc0a3560 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.data.quickaffordance import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.controls.controller.ControlsController @@ -41,7 +40,6 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt index 4f071bd9904f..26fcb234843d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt @@ -22,9 +22,8 @@ import android.content.res.Resources import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R import com.android.systemui.settings.FakeUserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.util.FakeSharedPreferences @@ -49,7 +48,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { @@ -72,8 +70,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { val resources: Resources = mock() whenever(resources.getStringArray(R.array.config_keyguardQuickAffordanceDefaults)) .thenReturn(emptyArray()) - whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)) - .thenReturn(true) + whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)).thenReturn(true) whenever(context.resources).thenReturn(resources) testDispatcher = UnconfinedTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt index bd0b71df3e5c..99a01858471c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt @@ -23,7 +23,6 @@ import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.backup.BackupHelper import com.android.systemui.settings.FakeUserTracker @@ -53,7 +52,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt index 0797d07807b4..a1c9f87ee7bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt @@ -21,7 +21,6 @@ import android.content.pm.UserInfo import android.os.UserHandle 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.settings.FakeUserTracker import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt index d8c0341e1979..b15352bfe6ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt @@ -21,7 +21,6 @@ import android.content.Context import android.media.AudioManager 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.settings.UserFileManager import com.android.systemui.settings.UserTracker @@ -46,7 +45,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class MuteQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index 9d983b8c8943..521dea34513e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.data.quickaffordance import android.content.Intent 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.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult import com.android.systemui.qrcodescanner.controller.QRCodeScannerController @@ -40,7 +39,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt index 613c4ce4ccef..02db0d7a9a50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt @@ -24,7 +24,6 @@ import android.service.quickaccesswallet.WalletCard import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.Expandable @@ -51,7 +50,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt index 1414bace3090..a9b9c9011636 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt @@ -21,7 +21,6 @@ import android.app.admin.DevicePolicyManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.ActivityIntentHelper -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.camera.CameraIntentsWrapper import com.android.systemui.coroutines.collectLastValue @@ -45,7 +44,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { 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 944b059450c9..d8cdf29284ed 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 @@ -32,7 +32,6 @@ import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.data.repository.FaceSensorInfo @@ -79,7 +78,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) class BiometricSettingsRepositoryTest : SysuiTestCase() { 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 307204da1761..819d08a2086e 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 @@ -44,7 +44,6 @@ import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PAN import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository @@ -122,7 +121,6 @@ import java.io.StringWriter @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt index def016ad8381..a58bc52bf8e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt @@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.coroutines.collectLastValue @@ -49,7 +48,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() { @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt index 7eb8a26d9ab6..9be5558f5cc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.data.repository 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.coroutines.collectLastValue import com.android.systemui.keyguard.shared.model.DevicePosture @@ -39,7 +38,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DevicePostureRepositoryTest : SysuiTestCase() { private lateinit var underTest: DevicePostureRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt index 126b841610b4..567e0a9717fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -22,7 +22,6 @@ import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig @@ -56,7 +55,6 @@ import org.mockito.ArgumentMatchers.anyString @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 7983e30c66fb..b9119e10b720 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.common.shared.model.Position @@ -64,7 +63,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardRepositoryImplTest : SysuiTestCase() { 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 4942cf84a6fc..799bd5ac5739 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 @@ -20,7 +20,6 @@ import android.graphics.Point 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 import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.coroutines.collectLastValue @@ -46,7 +45,6 @@ import org.junit.runner.RunWith import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class LightRevealScrimRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt index 7f784d88da6d..ee47c58f4002 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt @@ -21,7 +21,6 @@ import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.logging.TrustRepositoryLogger -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue @@ -46,7 +45,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class TrustRepositoryTest : SysuiTestCase() { @Mock private lateinit var trustManager: TrustManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt index 181cc884c9f5..d6e19cbcb826 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.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.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardDismissActionInteractorTest : SysuiTestCase() { private lateinit var keyguardRepository: FakeKeyguardRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt index c407b14dcf65..a5cfbbfda196 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt @@ -22,7 +22,6 @@ import android.service.trust.TrustAgentService import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.TrustGrantFlags -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.shared.model.DismissAction @@ -40,7 +39,6 @@ import org.junit.runner.RunWith import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardDismissInteractorTest : SysuiTestCase() { private lateinit var dispatcher: TestDispatcher diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index b527510fc56f..06eb0dd364b5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -28,8 +28,10 @@ import com.android.keyguard.FaceWakeUpTriggersConfig import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor 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.LockoutMode +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.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -156,7 +158,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { fakeDeviceEntryFingerprintAuthRepository, fakeUserRepository, facePropertyRepository, - fakeKeyguardRepository, faceWakeUpTriggersConfig, powerInteractor, ) @@ -440,6 +441,43 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { } @Test + fun faceAuthIsRequestedWhenWalletIsLaunchedAndIfFaceAuthIsStrong() = + testScope.runTest { + underTest.start() + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) + + underTest.onWalletLaunched() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true)) + } + + @Test + fun faceAuthIsNotTriggeredIfFaceAuthIsWeak() = + testScope.runTest { + underTest.start() + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.WEAK)) + + underTest.onWalletLaunched() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + + @Test + fun faceAuthIsNotTriggeredIfFaceAuthIsConvenience() = + testScope.runTest { + underTest.start() + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.CONVENIENCE)) + + underTest.onWalletLaunched() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + + @Test fun faceUnlockIsDisabledWhenFpIsLockedOut() = testScope.runTest { underTest.start() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index ca52cdb227e5..9ee22c89405d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.domain.interactor import android.app.StatusBarManager 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.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository @@ -47,7 +46,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardInteractorTest : SysuiTestCase() { @@ -206,7 +204,8 @@ class KeyguardInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) runCurrent() assertThat(isAnimate).isFalse() 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 index bbe68234f055..e10815d9de61 100644 --- 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 @@ -35,6 +35,7 @@ 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 kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before import org.junit.Rule import org.junit.Test @@ -46,6 +47,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +@ExperimentalCoroutinesApi @SmallTest @RunWith(AndroidJUnit4::class) class KeyguardKeyEventInteractorTest : SysuiTestCase() { @@ -57,8 +59,6 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP) private val backKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK) - private lateinit var keyguardInteractorWithDependencies: - KeyguardInteractorFactory.WithDependencies private lateinit var powerInteractor: PowerInteractor @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @@ -73,14 +73,12 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { fun setup() { whenever(mediaSessionLegacyHelperWrapper.getHelper(any())) .thenReturn(mediaSessionLegacyHelper) - keyguardInteractorWithDependencies = KeyguardInteractorFactory.create() powerInteractor = PowerInteractorFactory.create().powerInteractor underTest = KeyguardKeyEventInteractor( context, statusBarStateController, - keyguardInteractorWithDependencies.keyguardInteractor, statusBarKeyguardViewManager, shadeController, mediaSessionLegacyHelperWrapper, @@ -132,58 +130,73 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { } @Test - fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() { + fun dispatchKeyEvent_menuActionUp_awakeKeyguard_showsPrimaryBouncer() { powerInteractor.setAwakeForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() - verify(shadeController).animateCollapseShadeForced() + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU) } @Test - fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() { + fun dispatchKeyEvent_menuActionUp_awakeShadeLocked_collapsesShade() { powerInteractor.setAwakeForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) - // action down: does NOT collapse the shade - val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() - verify(shadeController, never()).animateCollapseShadeForced() - - // action up: collapses the shade - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() - verify(shadeController).animateCollapseShadeForced() + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU) } @Test - fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() { + fun dispatchKeyEvent_menuActionUp_asleepKeyguard_neverCollapsesShade() { powerInteractor.setAsleepForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse() - verify(shadeController, never()).animateCollapseShadeForced() + verifyActionsDoNothing(KeyEvent.KEYCODE_MENU) } @Test - fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() { + fun dispatchKeyEvent_spaceActionUp_awakeKeyguard_collapsesShade() { powerInteractor.setAwakeForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false) - // action down: does NOT collapse the shade - val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE) - assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() - verify(shadeController, never()).animateCollapseShadeForced() + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE) + } - // action up: collapses the shade - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() - verify(shadeController).animateCollapseShadeForced() + @Test + fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() { + powerInteractor.setAwakeForTest() + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE) + } + + @Test + fun dispatchKeyEvent_enterActionUp_awakeKeyguard_showsPrimaryBouncer() { + powerInteractor.setAwakeForTest() + whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER) + } + + @Test + fun dispatchKeyEvent_enterActionUp_awakeKeyguard_primaryBouncerAlreadyShowing() { + powerInteractor.setAwakeForTest() + whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(true) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + verifyActionsDoNothing(KeyEvent.KEYCODE_ENTER) + } + + @Test + fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() { + powerInteractor.setAwakeForTest() + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER) } @Test @@ -253,4 +266,42 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { .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/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt index 13025a016eff..0c74a38fea04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt @@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags @@ -51,7 +50,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardLongPressInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 8c13bb465103..347d580abf5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -23,7 +23,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.common.shared.model.ContentDescription @@ -73,7 +72,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt index fdcc66b6c974..71fcf6f9955c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.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.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository @@ -41,7 +40,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index 9e9c25eafa33..29b546bd49ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.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.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -42,7 +41,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardTransitionInteractorTest : SysuiTestCase() { @@ -230,4 +228,432 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { assertThat(startedSteps).isEqualTo(listOf(0f, 0.5f, 1f, 1f, 0.5f, 0f)) } + + @Test + fun isInTransitionToState() = testScope.runTest { + val results by collectValues(underTest.isInTransitionToState(GONE)) + + sendSteps( + TransitionStep(AOD, DOZING, 0f, STARTED), + TransitionStep(AOD, DOZING, 0.5f, RUNNING), + TransitionStep(AOD, DOZING, 1f, FINISHED), + ) + + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(GONE, DOZING, 0f, STARTED), + TransitionStep(GONE, DOZING, 0f, RUNNING), + TransitionStep(GONE, DOZING, 1f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + true, + )) + } + + @Test + fun isInTransitionFromState() = testScope.runTest { + val results by collectValues(underTest.isInTransitionFromState(DOZING)) + + sendSteps( + TransitionStep(AOD, DOZING, 0f, STARTED), + TransitionStep(AOD, DOZING, 0.5f, RUNNING), + TransitionStep(AOD, DOZING, 1f, FINISHED), + ) + + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(GONE, DOZING, 0f, STARTED), + TransitionStep(GONE, DOZING, 0f, RUNNING), + TransitionStep(GONE, DOZING, 1f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + true, + )) + } + + @Test + fun isInTransitionFromStateWhere() = testScope.runTest { + val results by collectValues(underTest.isInTransitionFromStateWhere { + it == DOZING + }) + + sendSteps( + TransitionStep(AOD, DOZING, 0f, STARTED), + TransitionStep(AOD, DOZING, 0.5f, RUNNING), + TransitionStep(AOD, DOZING, 1f, FINISHED), + ) + + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(GONE, DOZING, 0f, STARTED), + TransitionStep(GONE, DOZING, 0f, RUNNING), + TransitionStep(GONE, DOZING, 1f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + true, + )) + } + + @Test + fun isInTransitionWhere() = testScope.runTest { + val results by collectValues(underTest.isInTransitionWhere( + fromStatePredicate = { it == DOZING }, + toStatePredicate = { it == GONE }, + )) + + sendSteps( + TransitionStep(AOD, DOZING, 0f, STARTED), + TransitionStep(AOD, DOZING, 0.5f, RUNNING), + TransitionStep(AOD, DOZING, 1f, FINISHED), + ) + + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(GONE, DOZING, 0f, STARTED), + TransitionStep(GONE, DOZING, 0f, RUNNING), + TransitionStep(GONE, DOZING, 1f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + true, + )) + } + + @Test + fun isFinishedInStateWhere() = testScope.runTest { + val results by collectValues(underTest.isFinishedInStateWhere { it == GONE } ) + + sendSteps( + TransitionStep(AOD, DOZING, 0f, STARTED), + TransitionStep(AOD, DOZING, 0.5f, RUNNING), + TransitionStep(AOD, DOZING, 1f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, // Finished in DOZING, not GONE. + )) + + sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED)) + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING)) + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED)) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(GONE, DOZING, 0f, STARTED), + TransitionStep(GONE, DOZING, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps(TransitionStep(GONE, DOZING, 1f, FINISHED)) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED)) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + true, + )) + } + + @Test + fun isFinishedInState() = testScope.runTest { + val results by collectValues(underTest.isFinishedInState(GONE)) + + sendSteps( + TransitionStep(AOD, DOZING, 0f, STARTED), + TransitionStep(AOD, DOZING, 0.5f, RUNNING), + TransitionStep(AOD, DOZING, 1f, FINISHED), + ) + + assertThat(results).isEqualTo(listOf( + false, // Finished in DOZING, not GONE. + )) + + sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED)) + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING)) + + assertThat(results).isEqualTo(listOf( + false, + )) + + sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED)) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps( + TransitionStep(GONE, DOZING, 0f, STARTED), + TransitionStep(GONE, DOZING, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + )) + + sendSteps(TransitionStep(GONE, DOZING, 1f, FINISHED)) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps( + TransitionStep(DOZING, GONE, 0f, STARTED), + TransitionStep(DOZING, GONE, 0f, RUNNING), + ) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + )) + + sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED)) + + assertThat(results).isEqualTo(listOf( + false, + true, + false, + true, + )) + } + + private suspend fun sendSteps(vararg steps: TransitionStep) { + steps.forEach { + repository.sendTransitionStep(it) + testScope.runCurrent() + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt index 906d94859140..c02add1d0ecb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.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.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository @@ -45,7 +44,6 @@ import org.mockito.MockitoAnnotations import org.mockito.Spy @SmallTest -@RoboPilotTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class LightRevealScrimInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index 73ecae5af9b3..16f2fa22d5fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.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.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -38,7 +37,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt index a22f603ae92a..5b29a867ceaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt @@ -20,7 +20,6 @@ import android.testing.TestableLooper.RunWithLooper import android.view.RemoteAnimationTarget import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardViewController -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor @@ -41,7 +40,6 @@ import org.mockito.Mockito.doAnswer import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWithLooper(setAsMainLooper = true) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardSurfaceBehindParamsApplierTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt index 7a17435b9165..9b2db3e5316c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.binder import android.app.IActivityTaskManager 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.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.statusbar.policy.KeyguardStateController @@ -35,7 +34,6 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt index 7940b450b932..50ee02609635 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt @@ -23,6 +23,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection import com.android.systemui.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.view.KeyguardRootView @@ -65,6 +66,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { @Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines @Mock private lateinit var aodNotificationIconsSection: AodNotificationIconsSection @Mock private lateinit var aodBurnInSection: AodBurnInSection + @Mock private lateinit var communalTutorialIndicatorSection: CommunalTutorialIndicatorSection @Before fun setup() { @@ -83,6 +85,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { splitShadeGuidelines, aodNotificationIconsSection, aodBurnInSection, + communalTutorialIndicatorSection, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt new file mode 100644 index 000000000000..1768f8c4385b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt @@ -0,0 +1,197 @@ +/* + * 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.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.google.common.collect.Range +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@ExperimentalCoroutinesApi +@RunWith(JUnit4::class) +@SmallTest +class AlternateBouncerViewModelTest : SysuiTestCase() { + + private lateinit var testScope: TestScope + + @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var falsingManager: FalsingManager + + private lateinit var transitionRepository: FakeKeyguardTransitionRepository + private lateinit var transitionInteractor: KeyguardTransitionInteractor + private lateinit var underTest: AlternateBouncerViewModel + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + testScope = TestScope() + + val transitionInteractorWithDependencies = + KeyguardTransitionInteractorFactory.create(testScope.backgroundScope) + transitionInteractor = transitionInteractorWithDependencies.keyguardTransitionInteractor + transitionRepository = transitionInteractorWithDependencies.repository + underTest = + AlternateBouncerViewModel( + statusBarKeyguardViewManager, + transitionInteractor, + falsingManager, + ) + } + + @Test + fun transitionToAlternateBouncer_scrimAlphaUpdate() = + runTest(UnconfinedTestDispatcher()) { + val scrimAlphas by collectValues(underTest.scrimAlpha) + + transitionRepository.sendTransitionStep( + stepToAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.4f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f)) + + assertThat(scrimAlphas.size).isEqualTo(4) + scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } + } + + @Test + fun transitionFromAlternateBouncer_scrimAlphaUpdate() = + runTest(UnconfinedTestDispatcher()) { + val scrimAlphas by collectValues(underTest.scrimAlpha) + + transitionRepository.sendTransitionStep( + stepFromAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.4f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f)) + + assertThat(scrimAlphas.size).isEqualTo(4) + scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } + } + + @Test + fun clickListenerUpdate() = + runTest(UnconfinedTestDispatcher()) { + val clickListener by collectLastValue(underTest.onClickListener) + + // keyguard state => ALTERNATE_BOUNCER + transitionRepository.sendTransitionStep( + stepToAlternateBouncer(0f, TransitionState.STARTED) + ) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f)) + assertThat(clickListener).isNotNull() + + // ALTERNATE_BOUNCER -> keyguard state + transitionRepository.sendTransitionStep( + stepFromAlternateBouncer(0f, TransitionState.STARTED) + ) + assertThat(clickListener).isNotNull() + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f)) + assertThat(clickListener).isNull() + } + + @Test + fun forcePluginOpen() = + runTest(UnconfinedTestDispatcher()) { + val forcePluginOpen by collectLastValue(underTest.forcePluginOpen) + transitionRepository.sendTransitionStep( + stepToAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f)) + assertThat(forcePluginOpen).isTrue() + + transitionRepository.sendTransitionStep( + stepFromAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f)) + assertThat(forcePluginOpen).isFalse() + } + + private fun stepToAlternateBouncer( + value: Float, + state: TransitionState = TransitionState.RUNNING + ): TransitionStep { + return step( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.ALTERNATE_BOUNCER, + value = value, + transitionState = state, + ) + } + + private fun stepFromAlternateBouncer( + value: Float, + state: TransitionState = TransitionState.RUNNING + ): TransitionStep { + return step( + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.LOCKSCREEN, + value = value, + transitionState = state, + ) + } + + private fun step( + from: KeyguardState, + to: KeyguardState, + value: Float, + transitionState: TransitionState + ): TransitionStep { + return TransitionStep( + from = from, + to = to, + value = value, + transitionState = transitionState, + ownerName = "AlternateBouncerViewModelTest" + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt index bfc6f31c57db..6d47aed58dac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel 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.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -46,7 +45,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: DreamingToLockscreenTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt index 75c8bff326b0..cf2012989624 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel 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.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class GoneToDreamingTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: GoneToDreamingTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 6e94691d42a9..7de28de4d436 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -28,10 +29,9 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class LockscreenSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt index 12fe07f0827e..89a1d2b3011d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel 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.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: LockscreenToDreamingTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt index 83ae631ed164..41f8856d5ca2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel 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.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: LockscreenToOccludedTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt index 88603999cdd7..ec95cb8c43f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel 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.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: OccludedToLockscreenTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index da372eaf158c..d7802aabb298 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel 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.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.coroutines.collectValues @@ -46,7 +45,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel @@ -62,10 +60,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) repository = FakeKeyguardTransitionRepository() val featureFlags = - FakeFeatureFlags().apply { - set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) - set(Flags.UDFPS_NEW_TOUCH_DETECTION, true) - } + FakeFeatureFlags().apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) } val interactor = KeyguardTransitionInteractorFactory.create( scope = TestScope().backgroundScope, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt index 9ab9b3d160d1..32acefebfa68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel 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.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository @@ -46,7 +45,6 @@ import org.mockito.MockitoAnnotations @ExperimentalCoroutinesApi @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class UdfpsAodViewModelTest : SysuiTestCase() { private val defaultPadding = 12 diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt index fb0a4be25aa0..59d81049ad46 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt @@ -48,7 +48,6 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.logging.InstanceId import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.InstanceIdSequenceFake -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager @@ -63,6 +62,7 @@ import com.android.systemui.media.controls.util.MediaControllerFactory import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor @@ -2204,6 +2204,85 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() { + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + addPlaybackStateAction() + + // When a media control with PlaybackState actions is added, times out, + // and then the session is destroyed + addNotificationAndLoad() + val data = mediaDataCaptor.value + assertThat(data.active).isTrue() + mediaDataManager.setTimedOut(KEY, timedOut = true) + sessionCallbackCaptor.value.invoke(KEY) + + // It is fully removed. + verify(listener).onMediaDataRemoved(eq(KEY)) + verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId)) + verify(listener, never()) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(KEY), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + } + + @Test + fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() { + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + addPlaybackStateAction() + + // When a media control using session actions is added, and then the session is destroyed + // without timing out first + addNotificationAndLoad() + val data = mediaDataCaptor.value + assertThat(data.active).isTrue() + sessionCallbackCaptor.value.invoke(KEY) + + // It is fully removed + verify(listener).onMediaDataRemoved(eq(KEY)) + verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId)) + verify(listener, never()) + .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) + } + + @Test + fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() { + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + addPlaybackStateAction() + + // When a media control using session actions and that does allow resumption is added, + addNotificationAndLoad() + val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {}) + mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable) + + // And then the session is destroyed without timing out first + sessionCallbackCaptor.value.invoke(KEY) + + // It is converted to a resume player + verify(listener) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(KEY), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + assertThat(mediaDataCaptor.value.resumption).isTrue() + assertThat(mediaDataCaptor.value.active).isFalse() + verify(logger) + .logActiveConvertedToResume( + anyInt(), + eq(PACKAGE_NAME), + eq(mediaDataCaptor.value.instanceId) + ) + } + + @Test fun testSessionDestroyed_noNotificationKey_stillRemoved() { whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt index de57b603c7fe..5296f1abd756 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt @@ -24,7 +24,6 @@ import android.view.ViewGroup import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardViewController -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq import com.android.systemui.dreams.DreamOverlayStateController @@ -32,6 +31,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.media.dream.MediaDreamComplication import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt index 906420d03bed..9630ee32d456 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt @@ -46,6 +46,7 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() { private val mockContext = mock<Context>() private val resources = mock<Resources>() private val windowManager = mock<WindowManager>() + private val windowMetricsProvider = mock<WindowMetricsProvider>() private val sizeUpdates = arrayListOf<Rect>() private val testConfigurationController = FakeConfigurationController() @@ -112,13 +113,12 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() { } private fun givenTaskbarSize(size: Int) { - val windowInsets = - WindowInsets.Builder() - .setInsets(Type.tappableElement(), Insets.of(Rect(0, 0, 0, size))) - .build() + val insets = Insets.of(Rect(0, 0, 0, size)) + val windowInsets = WindowInsets.Builder().setInsets(Type.tappableElement(), insets).build() val windowMetrics = WindowMetrics(windowManager.maximumWindowMetrics.bounds, windowInsets) whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics) whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics) + whenever(windowMetricsProvider.currentWindowInsets).thenReturn(insets) } private fun givenDisplay(width: Int, height: Int, isTablet: Boolean = false) { @@ -126,6 +126,7 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() { val windowMetrics = WindowMetrics(bounds, { null }, 1.0f) whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics) whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics) + whenever(windowMetricsProvider.maximumWindowBounds).thenReturn(bounds) val minDimension = min(width, height) @@ -147,7 +148,11 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() { } } - return TaskPreviewSizeProvider(mockContext, windowManager, testConfigurationController) + return TaskPreviewSizeProvider( + mockContext, + windowMetricsProvider, + testConfigurationController + ) .also { it.addCallback(listener) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt new file mode 100644 index 000000000000..fe18454ec06c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt @@ -0,0 +1,44 @@ +package com.android.systemui.mediaprojection.appselector.view + +import android.graphics.Insets +import android.graphics.Rect +import android.view.WindowManager +import android.view.WindowMetrics +import androidx.core.view.WindowInsetsCompat +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +@SmallTest +class WindowMetricsProviderImplTest : SysuiTestCase() { + + private val windowManager = mock<WindowManager>() + private val windowMetricsProvider = WindowMetricsProviderImpl(windowManager) + + @Test + fun getMaximumWindowBounds_returnsValueFromWMMaxWindowMetrics() { + val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400) + val metrics = + WindowMetrics(bounds, /* windowInsetsSupplier= */ { null }, /* density= */ 1.0f) + whenever(windowManager.maximumWindowMetrics).thenReturn(metrics) + + assertThat(windowMetricsProvider.maximumWindowBounds).isEqualTo(bounds) + } + + @Test + fun getCurrentWindowInsets_returnsFromWMCurrentWindowMetrics() { + val bounds = Rect() + val insets = + Insets.of(Rect(/* left= */ 123, /* top= */ 456, /* right= */ 789, /* bottom= */ 1012)) + val windowInsets = + android.view.WindowInsets.Builder() + .setInsets(WindowInsetsCompat.Type.tappableElement(), insets) + .build() + whenever(windowManager.currentWindowMetrics).thenReturn(WindowMetrics(bounds, windowInsets)) + + assertThat(windowMetricsProvider.currentWindowInsets).isEqualTo(insets) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index 8019fb513be2..6248bb1009dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -57,7 +57,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.NotificationChannels; -import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.FakeGlobalSettings; import com.android.systemui.util.settings.GlobalSettings; import org.junit.Before; @@ -77,7 +77,7 @@ public class PowerNotificationWarningsTest extends SysuiTestCase { public static final String FORMATTED_45M = "0h 45m"; public static final String FORMATTED_HOUR = "1h 0m"; private final NotificationManager mMockNotificationManager = mock(NotificationManager.class); - private final GlobalSettings mGlobalSettings = new FakeSettings(); + private final GlobalSettings mGlobalSettings = new FakeGlobalSettings(); private PowerNotificationWarnings mPowerNotificationWarnings; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java index d57765c3ecf2..c8c134a9474a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java @@ -16,6 +16,8 @@ package com.android.systemui.qs; +import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; + import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; @@ -41,6 +43,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -49,7 +52,6 @@ import androidx.lifecycle.Lifecycle; import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; @@ -60,6 +62,7 @@ import com.android.systemui.qs.external.TileServiceRequestController; import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.res.R; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.CommandQueue; @@ -110,6 +113,9 @@ public class QSImplTest extends SysuiTestCase { @Mock private FeatureFlagsClassic mFeatureFlags; private View mQsView; + private final CommandQueue mCommandQueue = + new CommandQueue(mContext, new FakeDisplayTracker(mContext)); + private QSImpl mUnderTest; @@ -120,6 +126,16 @@ public class QSImplTest extends SysuiTestCase { mUnderTest.onComponentCreated(mQsComponent, null); } + /* + * Regression test for b/303180152. + */ + @Test + public void testDisableCallbackOnDisabledQuickSettingsUponCreationDoesntCrash() { + QSImpl other = instantiate(); + mCommandQueue.disable(Display.DEFAULT_DISPLAY, 0, DISABLE2_QUICK_SETTINGS); + + other.onComponentCreated(mQsComponent, null); + } @Test public void testSaveState() { @@ -473,7 +489,6 @@ public class QSImplTest extends SysuiTestCase { private QSImpl instantiate() { MockitoAnnotations.initMocks(this); - CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); setupQsComponent(); setUpViews(); @@ -484,11 +499,11 @@ public class QSImplTest extends SysuiTestCase { return new QSImpl( new RemoteInputQuickSettingsDisabler( mContext, - commandQueue, + mCommandQueue, new ResourcesSplitShadeStateController(), mock(ConfigurationController.class)), mStatusBarStateController, - commandQueue, + mCommandQueue, mQSMediaHost, mQQSMediaHost, mBypassController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index e2ac7bc4fc6e..b825c0840e01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -42,7 +43,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.UiEventLoggerFake; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.media.controls.ui.MediaHost; @@ -50,6 +50,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.util.animation.DisappearParameters; @@ -341,11 +342,92 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { } @Test - public void onViewDetached_removesJustTheAssociatedCallback() { + public void onDestroy_removesJustTheAssociatedCallback() { QSPanelControllerBase.TileRecord record = mController.mRecords.get(0); - mController.onViewDetached(); + mController.destroy(); verify(mQSTile).removeCallback(record.callback); verify(mQSTile, never()).removeCallbacks(); + + assertThat(mController.mRecords).isEmpty(); + } + + @Test + public void onViewDettached_callbackNotRemoved() { + QSPanelControllerBase.TileRecord record = mController.mRecords.get(0); + + mController.onViewDetached(); + verify(mQSTile, never()).removeCallback(record.callback); + verify(mQSTile, never()).removeCallbacks(); + } + + @Test + public void onInit_qsHostCallbackAdded() { + verify(mQSHost).addCallback(any()); + } + + @Test + public void onViewDettached_qsHostCallbackNotRemoved() { + mController.onViewDetached(); + verify(mQSHost, never()).removeCallback(any()); + } + + @Test + public void onDestroy_qsHostCallbackRemoved() { + mController.destroy(); + verify(mQSHost).removeCallback(any()); + } + + @Test + public void setTiles_sameTiles_doesntRemoveAndReaddViews() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + mController.setTiles(); + verify(mQSPanel, never()).removeTile(any()); + verify(mQSPanel, never()).addTile(any()); + } + + @Test + public void setTiles_differentTiles_allTilesRemovedAndNewTilesAdded() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile)); + mController.setTiles(); + + verify(mQSPanel, times(2)).removeTile(any()); + verify(mQSPanel).addTile(any()); + } + + @Test + public void detachAndReattach_sameTiles_doesntRemoveAndReAddViews() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + mController.onViewDetached(); + mController.onViewAttached(); + verify(mQSPanel, never()).removeTile(any()); + verify(mQSPanel, never()).addTile(any()); + } + + @Test + public void setTiles_sameTilesDifferentOrder_removesAndReadds() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + when(mQSHost.getTiles()).thenReturn(List.of(mOtherTile, mQSTile)); + mController.setTiles(); + + verify(mQSPanel, times(2)).removeTile(any()); + verify(mQSPanel, times(2)).addTile(any()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt index 4be689065d3f..8f06fe2e3050 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt @@ -35,7 +35,7 @@ private typealias Callback = (Int, Boolean) -> Unit @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper -class SettingObserverTest : SysuiTestCase() { +class UserSettingObserverTest : SysuiTestCase() { companion object { private const val TEST_SETTING = "setting" @@ -46,7 +46,7 @@ class SettingObserverTest : SysuiTestCase() { } private lateinit var testableLooper: TestableLooper - private lateinit var setting: SettingObserver + private lateinit var setting: UserSettingObserver private lateinit var secureSettings: SecureSettings private lateinit var callback: Callback @@ -56,17 +56,19 @@ class SettingObserverTest : SysuiTestCase() { testableLooper = TestableLooper.get(this) secureSettings = FakeSettings() - setting = object : SettingObserver( - secureSettings, - Handler(testableLooper.looper), - TEST_SETTING, - USER, - DEFAULT_VALUE - ) { - override fun handleValueChanged(value: Int, observedChange: Boolean) { - callback(value, observedChange) + setting = + object : + UserSettingObserver( + secureSettings, + Handler(testableLooper.looper), + TEST_SETTING, + USER, + DEFAULT_VALUE + ) { + override fun handleValueChanged(value: Int, observedChange: Boolean) { + callback(value, observedChange) + } } - } // Default empty callback callback = { _, _ -> Unit } @@ -162,4 +164,4 @@ class SettingObserverTest : SysuiTestCase() { setting.isListening = true assertThat(setting.value).isEqualTo(DEFAULT_VALUE) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt index 4ada44c40f49..f6715fae72b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt @@ -25,7 +25,6 @@ import android.view.ContextThemeWrapper import androidx.test.filters.SmallTest import com.android.settingslib.Utils import com.android.settingslib.drawable.UserIconDrawable -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.shared.model.ContentDescription @@ -35,6 +34,7 @@ import com.android.systemui.qs.FakeFgsManagerController import com.android.systemui.qs.QSSecurityFooterUtils import com.android.systemui.qs.footer.FooterActionsTestUtils import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig +import com.android.systemui.res.R import com.android.systemui.security.data.model.SecurityModel import com.android.systemui.settings.FakeUserTracker import com.android.systemui.statusbar.policy.FakeSecurityController @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.policy.MockUserSwitcherControllerWrapper import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.nullable -import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.FakeGlobalSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch @@ -128,7 +128,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { fun userSwitcher() = runTest { val picture: Drawable = mock() val userInfoController = FakeUserInfoController(FakeInfo(picture = picture)) - val settings = FakeSettings() + val settings = FakeGlobalSettings() val userId = 42 val userTracker = FakeUserTracker(userId) val userSwitcherControllerWrapper = @@ -167,14 +167,9 @@ class FooterActionsViewModelTest : SysuiTestCase() { // for the current user will be fired to notify us of that change. isUserSwitcherEnabled = true - // Update the setting for a random user: nothing should change, given that at this point we - // weren't notified of the change yet. - utils.setUserSwitcherEnabled(settings, true, 3) - assertThat(currentUserSwitcher()).isNull() - // Update the setting for the observed user: now we will be notified and the button should // be there. - utils.setUserSwitcherEnabled(settings, true, userId) + utils.setUserSwitcherEnabled(settings, true) val userSwitcher = currentUserSwitcher() assertThat(userSwitcher).isNotNull() assertThat(userSwitcher!!.icon) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt index 9a55f722c13c..d277fcab3690 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.pipeline.data.repository import android.provider.Settings 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.shared.TileSpec @@ -39,7 +38,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AutoAddSettingsRepositoryTest : SysuiTestCase() { private val secureSettings = FakeSettings() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt index 30f5811c3731..3db676d68f42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt @@ -20,7 +20,6 @@ import android.content.ComponentName import android.content.SharedPreferences 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.settings.UserFileManager import com.android.systemui.util.FakeSharedPreferences @@ -30,7 +29,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt index 995de662aaf0..070e07a75d23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt @@ -32,7 +32,6 @@ import android.service.quicksettings.TileService 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 import com.android.systemui.coroutines.collectLastValue import com.android.systemui.util.mockito.any @@ -62,7 +61,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt index dc09a33adc1c..ff8a9bd019fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt @@ -4,7 +4,6 @@ import android.content.Intent import android.provider.Settings 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.broadcast.FakeBroadcastDispatcher import com.android.systemui.coroutines.collectLastValue @@ -24,7 +23,6 @@ import org.mockito.MockitoAnnotations @SmallTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) -@RoboPilotTest class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() { private val dispatcher = StandardTestDispatcher() private val testScope = TestScope(dispatcher) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt index 08adebb87b1b..f7c3b213730c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt @@ -20,7 +20,6 @@ import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.shared.TileSpec @@ -40,7 +39,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class TileSpecSettingsRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt index 20876237b476..9516c2181ac0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt @@ -2,14 +2,12 @@ package com.android.systemui.qs.pipeline.data.repository 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.qs.pipeline.shared.TileSpec import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class TilesSettingConverterTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt index 81fd72b11227..36e860e37ffa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt @@ -3,7 +3,6 @@ package com.android.systemui.qs.pipeline.data.repository import android.provider.Settings 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.data.model.RestoreData @@ -24,7 +23,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class UserAutoAddRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt index 389580c1326b..d4a9fabd6806 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt @@ -3,7 +3,6 @@ package com.android.systemui.qs.pipeline.data.repository import android.provider.Settings 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.data.model.RestoreData @@ -23,7 +22,6 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@RoboPilotTest @SmallTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt index 15e401d24b02..4454a3cb15fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt @@ -20,7 +20,6 @@ import android.content.ComponentName import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.util.mockito.mock @@ -29,7 +28,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AutoAddableSettingListTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt index 7c6dd242a569..d153e9d1d361 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues @@ -36,7 +35,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AutoAddableSettingTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt index 469eee363889..ec139e4c515e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -36,7 +35,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CallbackControllerAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt index b6eaa3922278..4fae532d4174 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -42,7 +41,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CastAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt index a755fbbdfd7f..9e2d1f885e2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DataSaverAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt index daacca51a915..0116bd9575d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DeviceControlsAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt index 4b5f7f6d07ef..e7ea9a66450c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class HotspotAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt index 32d9db24a3b2..20fd3601f9ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import android.hardware.display.NightDisplayListener 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.coroutines.collectLastValue import com.android.systemui.dagger.NightDisplayListenerModule @@ -50,7 +49,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class NightDisplayAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt index fb513a6bd197..19ac63c36cab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.qs.ReduceBrightColorsController @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class ReduceBrightColorsAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt index 5ce15fa57b1f..d645ee34619b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt @@ -21,7 +21,6 @@ import android.content.pm.PackageManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues @@ -52,7 +51,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class SafetyCenterAutoAddableTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt index 1c8cb541e613..83ff35d8022d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -38,7 +37,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WalletAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt index de1d29fdcbe5..adccc84e494b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt @@ -23,7 +23,6 @@ import android.content.pm.UserInfo.FLAG_PRIMARY import android.content.pm.UserInfo.FLAG_PROFILE 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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WorkTileAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt index bb18115c960c..41a7ec03408d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.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.coroutines.collectLastValue import com.android.systemui.dump.DumpManager @@ -47,7 +46,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class AutoAddInteractorTest : SysuiTestCase() { 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 a7505240caeb..a89338a38e89 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 @@ -24,7 +24,6 @@ import android.os.UserHandle import android.service.quicksettings.Tile 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.coroutines.collectLastValue import com.android.systemui.dump.nano.SystemUIProtoDump @@ -71,7 +70,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class CurrentTilesInteractorImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt index 151b256c4f5c..0d9711588a1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.qs.pipeline.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.shade.ShadeController import org.junit.Before @@ -27,7 +26,6 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @RunWith(AndroidJUnit4::class) @SmallTest class PanelInteractorImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt index 2e6b50b637dd..f73cab8a10a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt @@ -2,7 +2,6 @@ package com.android.systemui.qs.pipeline.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.coroutines.collectLastValue import com.android.systemui.qs.pipeline.data.model.RestoreData @@ -20,7 +19,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.MockitoAnnotations -@RoboPilotTest @RunWith(AndroidJUnit4::class) @SmallTest class RestoreReconciliationInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt index 34c4c9842986..558e7694b54c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt @@ -19,14 +19,12 @@ package com.android.systemui.qs.pipeline.shared import android.content.ComponentName import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class TileSpecTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt index 06b7a9fde873..5659f0173860 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.tiles.base.actions import android.content.Intent 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.plugins.ActivityStarter import org.junit.Before @@ -32,7 +31,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QSTileIntentUserActionHandlerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt index 2c4e10eadfc9..9861606fd1b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt @@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.internal.logging.UiEventLogger -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.qs.QSEvent import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder @@ -34,7 +33,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QSTileAnalyticsTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt index 4f25d12aea49..a6199c25874b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt @@ -24,7 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter import com.android.systemui.util.mockito.any @@ -46,7 +45,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DisabledByPolicyInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt index 4401e0d60da6..f1fcee318141 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.tiles.base.logging 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.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -39,7 +38,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QSTileLoggerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt index a0ff2ab330b8..dcda005b6109 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.util.mockito.nullable import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.test.TestCoroutineScheduler @@ -95,8 +96,7 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { testScope.backgroundScope, dispatcher, ) - `when`(deviceItemInteractor.deviceItemUpdate) - .thenReturn(MutableStateFlow(null).asStateFlow()) + `when`(deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow()) `when`(bluetoothStateInteractor.bluetoothStateUpdate) .thenReturn(MutableStateFlow(null).asStateFlow()) `when`(deviceItemInteractor.deviceItemUpdateRequest) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt index 3593075c70fd..428f79cf9b1b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt @@ -27,10 +27,11 @@ import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule @@ -78,7 +79,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { @Before fun setUp() { - dispatcher = StandardTestDispatcher() + dispatcher = UnconfinedTestDispatcher() testScope = TestScope(dispatcher) interactor = DeviceItemInteractor( @@ -107,9 +108,10 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ true }, deviceItem1)) ) + val latest by collectLastValue(interactor.deviceItemUpdate) interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemUpdate.value).isEmpty() + assertThat(latest).isEqualTo(emptyList<DeviceItem>()) } } @@ -121,9 +123,10 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ false }, deviceItem1)) ) + val latest by collectLastValue(interactor.deviceItemUpdate) interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemUpdate.value).isEmpty() + assertThat(latest).isEqualTo(emptyList<DeviceItem>()) } } @@ -135,10 +138,10 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ true }, deviceItem1)) ) + val latest by collectLastValue(interactor.deviceItemUpdate) interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemUpdate.value).hasSize(1) - assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem1) + assertThat(latest).isEqualTo(listOf(deviceItem1)) } } @@ -150,11 +153,10 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ false }, deviceItem1), createFactory({ true }, deviceItem2)) ) + val latest by collectLastValue(interactor.deviceItemUpdate) interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemUpdate.value).hasSize(2) - assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem2) - assertThat(interactor.deviceItemUpdate.value!![1]).isEqualTo(deviceItem2) + assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem2)) } } @@ -177,10 +179,10 @@ class DeviceItemInteractorTest : SysuiTestCase() { `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) + val latest by collectLastValue(interactor.deviceItemUpdate) interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemUpdate.value) - .isEqualTo(listOf(deviceItem2, deviceItem1)) + assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1)) } } @@ -200,10 +202,10 @@ class DeviceItemInteractorTest : SysuiTestCase() { `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) + val latest by collectLastValue(interactor.deviceItemUpdate) interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemUpdate.value) - .isEqualTo(listOf(deviceItem2, deviceItem1)) + assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt index 4760dfa3561d..2084aeb7fe83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt @@ -20,7 +20,6 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.android.internal.logging.InstanceId -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.common.shared.model.ContentDescription @@ -49,7 +48,6 @@ import org.mockito.MockitoAnnotations // TODO(b/299909368): Add more tests @MediumTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index f1c99d71b4e5..58e36be2b9fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -38,11 +39,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class QuickSettingsSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt index 421c35595026..fe80f702a3ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt @@ -21,7 +21,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.FakeGlobalSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher @@ -36,7 +36,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) class RetailModeSettingsRepositoryTest : SysuiTestCase() { - private val globalSettings = FakeSettings() + private val globalSettings = FakeGlobalSettings() private val testDispatcher = StandardTestDispatcher() private val testScope = TestScope(testDispatcher) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 91f38656e02d..2e16577bb24f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository @@ -51,6 +52,7 @@ import com.google.common.truth.Truth.assertWithMessage import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -58,7 +60,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 /** * Integration test cases for the Scene Framework. @@ -80,7 +81,7 @@ import org.junit.runners.JUnit4 */ @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneFrameworkIntegrationTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -462,7 +463,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { fromScene = getCurrentSceneInUi(), toScene = to.key, progress = progressFlow, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index ff28d2dbccdd..740c6d9329df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.data.repository +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -28,13 +29,13 @@ import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneContainerRepositoryTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -119,7 +120,8 @@ class SceneContainerRepositoryTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt index 2187f3657848..7ae501d05fcd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.data.repository +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase @@ -24,9 +25,11 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Test +import org.junit.runner.RunWith import org.mockito.Mockito.verify @SmallTest +@RunWith(AndroidJUnit4::class) class WindowRootViewVisibilityRepositoryTest : SysuiTestCase() { private val iStatusBarService = mock<IStatusBarService>() private val executor = FakeExecutor(FakeSystemClock()) 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 afc0e6944839..31d26c0d88f9 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 @@ -14,10 +14,9 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) - package com.android.systemui.scene.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -26,16 +25,14 @@ 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.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -86,7 +83,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) @@ -124,7 +122,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = underTest.desiredScene.value.key, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(transitionTo).isEqualTo(SceneKey.Shade) @@ -161,7 +160,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) val transitioning by @@ -180,7 +180,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) underTest.setTransitionState(transitionState) @@ -197,7 +198,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) val transitioning by @@ -225,7 +227,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(transitioning).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt index 3785fd7436c1..8be4eeb7be7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase @@ -36,16 +37,16 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock 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.Test +import org.junit.runner.RunWith import org.mockito.Mockito.reset import org.mockito.Mockito.verify @SmallTest -@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) class WindowRootViewVisibilityInteractorTest : SysuiTestCase() { private val testScope = TestScope() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 0216a0a98d84..3b9621e5c6e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.scene.domain.startable import android.os.PowerManager import android.view.Display +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.domain.model.AuthenticationMethodModel @@ -46,14 +47,13 @@ import kotlinx.coroutines.test.runTest import org.junit.Assume.assumeTrue import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneContainerStartableTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -109,7 +109,8 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason") @@ -122,7 +123,8 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Gone, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason") 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 0b56a59ca467..c89cd9e0c1f1 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,6 +18,7 @@ package com.android.systemui.scene.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -29,10 +30,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneContainerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt index 1bb2ff825115..263b0017221a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt @@ -42,6 +42,7 @@ class UserTrackerImplReceiveTest : SysuiTestCase() { @Parameterized.Parameters fun data(): Iterable<String> = listOf( + Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_USER_INFO_CHANGED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt index 52b25f85b870..c32d2597bd61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt @@ -177,7 +177,8 @@ class UserTrackerImplTest : SysuiTestCase() { verify(context) .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler)) with(captor.value) { - assertThat(countActions()).isEqualTo(6) + assertThat(countActions()).isEqualTo(7) + assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue() assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue() assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue() assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 7b12931b2b47..8d8c70e26ab2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -79,7 +79,6 @@ import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.keyguard.logging.KeyguardLogger; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -120,6 +119,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSFragmentLegacy; +import com.android.systemui.res.R; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.data.repository.ShadeRepository; @@ -153,7 +153,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; @@ -170,6 +169,7 @@ import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; @@ -182,6 +182,8 @@ import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; +import dagger.Lazy; + import org.junit.After; import org.junit.Before; import org.mockito.ArgumentCaptor; @@ -193,7 +195,6 @@ import org.mockito.stubbing.Answer; import java.util.List; import java.util.Optional; -import dagger.Lazy; import kotlinx.coroutines.CoroutineDispatcher; public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @@ -208,7 +209,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; @Mock protected ViewPropertyAnimator mViewPropertyAnimator; @Mock protected KeyguardBottomAreaView mQsFrame; - @Mock protected HeadsUpManagerPhone mHeadsUpManager; + @Mock protected HeadsUpManager mHeadsUpManager; @Mock protected NotificationShelfController mNotificationShelfController; @Mock protected NotificationGutsManager mGutsManager; @Mock protected KeyguardStatusBarView mKeyguardStatusBar; @@ -527,7 +528,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { NotificationWakeUpCoordinator coordinator = new NotificationWakeUpCoordinator( mDumpManager, - mock(HeadsUpManagerPhone.class), + mock(HeadsUpManager.class), new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager, mInteractionJankMonitor, mShadeExpansionStateManager), mKeyguardBypassController, @@ -547,7 +548,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mLockscreenShadeTransitionController, new FalsingCollectorFake(), mDumpManager); - when(mKeyguardStatusViewComponentFactory.build(any())) + when(mKeyguardStatusViewComponentFactory.build(any(), any())) .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) .thenReturn(mKeyguardClockSwitchController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 2ed20908ba2f..eb006100d535 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -47,18 +47,34 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; +import com.android.keyguard.KeyguardSecurityModel; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardViewMediator; +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.domain.interactor.FromLockscreenTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.res.R; import com.android.systemui.scene.FakeWindowRootViewComponent; import com.android.systemui.scene.SceneTestUtils; +import com.android.systemui.scene.data.repository.SceneContainerRepository; +import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.StatusBarState; @@ -71,9 +87,9 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; 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.ResourcesSplitShadeStateController; +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.user.domain.interactor.UserInteractor; import com.google.common.util.concurrent.MoreExecutors; @@ -109,6 +125,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private SysuiColorExtractor mColorExtractor; @Mock ColorExtractor.GradientColors mGradientColors; @Mock private DumpManager mDumpManager; + @Mock private KeyguardSecurityModel mKeyguardSecurityModel; @Mock private KeyguardStateController mKeyguardStateController; @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private AuthController mAuthController; @@ -123,6 +140,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private float mPreferredRefreshRate = -1; + private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor; + private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor; @Before public void setUp() { @@ -137,22 +156,86 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { when(mDozeParameters.getAlwaysOn()).thenReturn(true); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); + FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository(); + FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic(); + FakeShadeRepository shadeRepository = new FakeShadeRepository(); + FakePowerRepository powerRepository = new FakePowerRepository(); + + PowerInteractor powerInteractor = new PowerInteractor( + powerRepository, + new FalsingCollectorFake(), + mScreenOffAnimationController, + mStatusBarStateController); + + SceneInteractor sceneInteractor = new SceneInteractor( + mTestScope.getBackgroundScope(), + new SceneContainerRepository( + mTestScope.getBackgroundScope(), + mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())), + powerRepository, + mock(SceneLogger.class)); + + FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); + FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); + KeyguardInteractor keyguardInteractor = new KeyguardInteractor( + keyguardRepository, + new FakeCommandQueue(), + powerInteractor, + featureFlags, + sceneContainerFlags, + new FakeDeviceEntryRepository(), + new FakeKeyguardBouncerRepository(), + configurationRepository, + shadeRepository, + () -> sceneInteractor); + + FakeKeyguardTransitionRepository keyguardTransitionRepository = + new FakeKeyguardTransitionRepository(); + + KeyguardTransitionInteractor keyguardTransitionInteractor = + new KeyguardTransitionInteractor( + mTestScope.getBackgroundScope(), + keyguardTransitionRepository, + () -> keyguardInteractor, + () -> mFromLockscreenTransitionInteractor, + () -> mFromPrimaryBouncerTransitionInteractor); + + mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + shadeRepository, + powerInteractor); + + mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mKeyguardSecurityModel, + powerInteractor); + mShadeInteractor = new ShadeInteractor( mTestScope.getBackgroundScope(), + new FakeDeviceProvisioningRepository(), new FakeDisableFlagsRepository(), - new FakeSceneContainerFlags(), - mUtils::sceneInteractor, - new FakeKeyguardRepository(), + mock(DozeParameters.class), + sceneContainerFlags, + () -> sceneInteractor, + keyguardRepository, + keyguardTransitionInteractor, + powerInteractor, new FakeUserSetupRepository(), - mock(DeviceProvisionedController.class), mock(UserInteractor.class), new SharedNotificationContainerInteractor( - new FakeConfigurationRepository(), + configurationRepository, mContext, new ResourcesSplitShadeStateController()), - new FakeShadeRepository() - ); + shadeRepository); mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl( mContext, 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 96510721baa5..677d9db0b97c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -48,7 +49,7 @@ import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.flags.SystemPropertiesHelper -import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor +import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository @@ -83,7 +84,6 @@ import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.TestScope @@ -97,8 +97,9 @@ import org.mockito.Mockito.anyFloat import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import java.util.Optional +import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -143,7 +144,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var dragDownHelper: DragDownHelper @Mock lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel - @Mock lateinit var keyEventInteractor: KeyEventInteractor + @Mock lateinit var sysUIKeyEventHandler: SysUIKeyEventHandler + @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor private val notificationExpansionRepository = NotificationExpansionRepository() private lateinit var fakeClock: FakeSystemClock @@ -176,6 +179,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) featureFlags.set(Flags.MIGRATE_NSSL, false) + featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false) testScope = TestScope() fakeClock = FakeSystemClock() @@ -247,7 +251,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { securityModel = mock(KeyguardSecurityModel::class.java), ), BouncerLogger(logcatLogBuffer("BouncerLog")), - keyEventInteractor, + sysUIKeyEventHandler, + primaryBouncerInteractor, + alternateBouncerInteractor, ) underTest.setupExpandedStatusBar() underTest.setDragDownHelper(dragDownHelper) @@ -425,29 +431,37 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { } @Test - fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() { - // GIVEN not dozing - whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false) + fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() { + // GIVEN dozing + whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) // AND alternate bouncer doesn't want the touch whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) .thenReturn(false) - // AND the lock icon doesn't want the touch - whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false) - // AND the notification panel can accept touches - whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true) - whenever(dragDownHelper.isDragDownEnabled).thenReturn(true) - whenever(centralSurfaces.isBouncerShowing()).thenReturn(false) - - // AND the drag down helper doesn't want the touch (to pull the shade down) - whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false) + // AND the lock icon wants the touch + whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)) + .thenReturn(true) featureFlags.set(Flags.MIGRATE_NSSL, true) - // WHEN asked if should intercept touch - interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT) + // THEN touch should NOT be intercepted by NotificationShade + assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse() + } + + @Test + fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() { + // GIVEN dozing + whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) + // AND alternate bouncer doesn't want the touch + whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) + .thenReturn(false) + // AND the lock icon does NOT want the touch + whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)) + .thenReturn(false) + + featureFlags.set(Flags.MIGRATE_NSSL, true) - // Verify that NPVC gets a chance to use the touch - verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT) + // THEN touch should NOT be intercepted by NotificationShade + assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() } @Test @@ -461,21 +475,21 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { fun forwardsDispatchKeyEvent() { val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B) interactionEventHandler.dispatchKeyEvent(keyEvent) - verify(keyEventInteractor).dispatchKeyEvent(keyEvent) + verify(sysUIKeyEventHandler).dispatchKeyEvent(keyEvent) } @Test fun forwardsDispatchKeyEventPreIme() { val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B) interactionEventHandler.dispatchKeyEventPreIme(keyEvent) - verify(keyEventInteractor).dispatchKeyEventPreIme(keyEvent) + verify(sysUIKeyEventHandler).dispatchKeyEventPreIme(keyEvent) } @Test fun forwardsInterceptMediaKey() { val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP) interactionEventHandler.interceptMediaKey(keyEvent) - verify(keyEventInteractor).interceptMediaKey(keyEvent) + verify(sysUIKeyEventHandler).interceptMediaKey(keyEvent) } companion object { 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 00230202d941..a4a2ca0a6a21 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -47,7 +48,7 @@ import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.flags.SystemPropertiesHelper -import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor +import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository @@ -141,6 +142,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { Optional<UnfoldTransitionProgressProvider> @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock private lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel @@ -180,6 +183,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) featureFlags.set(Flags.MIGRATE_NSSL, false) + featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false) testScope = TestScope() controller = NotificationShadeWindowViewController( @@ -249,7 +253,9 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { securityModel = Mockito.mock(KeyguardSecurityModel::class.java), ), BouncerLogger(logcatLogBuffer("BouncerLog")), - Mockito.mock(KeyEventInteractor::class.java), + Mockito.mock(SysUIKeyEventHandler::class.java), + primaryBouncerInteractor, + alternateBouncerInteractor, ) controller.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index 8138b328a3c5..65174bab7f63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -32,23 +32,39 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; +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.domain.interactor.FromLockscreenTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSFragmentLegacy; import com.android.systemui.res.R; import com.android.systemui.scene.SceneTestUtils; +import com.android.systemui.scene.data.repository.SceneContainerRepository; +import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; @@ -64,19 +80,21 @@ import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFl import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor; +import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.user.domain.interactor.UserInteractor; import com.android.systemui.util.kotlin.JavaAdapter; @@ -103,8 +121,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { protected SceneTestUtils mUtils = new SceneTestUtils(this); protected TestScope mTestScope = mUtils.getTestScope(); - @Mock - protected Resources mResources; + @Mock protected Resources mResources; @Mock protected KeyguardBottomAreaView mQsFrame; @Mock protected KeyguardStatusBarView mKeyguardStatusBar; @Mock protected QS mQs; @@ -126,6 +143,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { @Mock protected NotificationShadeDepthController mNotificationShadeDepthController; @Mock protected ShadeHeaderController mShadeHeaderController; @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock protected DozeParameters mDozeParameters; @Mock protected KeyguardStateController mKeyguardStateController; @Mock protected KeyguardBypassController mKeyguardBypassController; @Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -144,7 +162,6 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { @Mock protected DumpManager mDumpManager; @Mock protected UiEventLogger mUiEventLogger; @Mock protected CastController mCastController; - @Mock protected DeviceProvisionedController mDeviceProvisionedController; @Mock protected UserInteractor mUserInteractor; protected FakeDisableFlagsRepository mDisableFlagsRepository = new FakeDisableFlagsRepository(); @@ -161,6 +178,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { new ShadeExpansionStateManager(); protected FragmentHostManager.FragmentListener mFragmentListener; + private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor; + private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor; @Before public void setup() { @@ -169,21 +188,89 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, mInteractionJankMonitor, mShadeExpansionStateManager); - when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + FakeDeviceProvisioningRepository deviceProvisioningRepository = + new FakeDeviceProvisioningRepository(); + deviceProvisioningRepository.setDeviceProvisioned(true); + FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic(); + FakePowerRepository powerRepository = new FakePowerRepository(); + FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); + + PowerInteractor powerInteractor = new PowerInteractor( + powerRepository, + new FalsingCollectorFake(), + mock(ScreenOffAnimationController.class), + mStatusBarStateController); + + SceneInteractor sceneInteractor = new SceneInteractor( + mTestScope.getBackgroundScope(), + new SceneContainerRepository( + mTestScope.getBackgroundScope(), + mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())), + powerRepository, + mock(SceneLogger.class)); + + FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); + KeyguardInteractor keyguardInteractor = new KeyguardInteractor( + mKeyguardRepository, + new FakeCommandQueue(), + powerInteractor, + featureFlags, + sceneContainerFlags, + new FakeDeviceEntryRepository(), + new FakeKeyguardBouncerRepository(), + configurationRepository, + mShadeRepository, + () -> sceneInteractor); + + FakeKeyguardTransitionRepository keyguardTransitionRepository = + new FakeKeyguardTransitionRepository(); + + KeyguardTransitionInteractor keyguardTransitionInteractor = + new KeyguardTransitionInteractor( + mTestScope.getBackgroundScope(), + keyguardTransitionRepository, + () -> keyguardInteractor, + () -> mFromLockscreenTransitionInteractor, + () -> mFromPrimaryBouncerTransitionInteractor); + + mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mShadeRepository, + powerInteractor); + + mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mock(KeyguardSecurityModel.class), + powerInteractor); + + ResourcesSplitShadeStateController splitShadeStateController = + new ResourcesSplitShadeStateController(); + mShadeInteractor = new ShadeInteractor( mTestScope.getBackgroundScope(), + deviceProvisioningRepository, mDisableFlagsRepository, - new FakeSceneContainerFlags(), - () -> mUtils.sceneInteractor(), + mDozeParameters, + sceneContainerFlags, + () -> sceneInteractor, mKeyguardRepository, + keyguardTransitionInteractor, + powerInteractor, new FakeUserSetupRepository(), - mDeviceProvisionedController, mUserInteractor, new SharedNotificationContainerInteractor( - new FakeConfigurationRepository(), + configurationRepository, mContext, - new ResourcesSplitShadeStateController()), + splitShadeStateController), mShadeRepository ); @@ -262,7 +349,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mShadeInteractor, new JavaAdapter(mTestScope.getBackgroundScope()), mCastController, - new ResourcesSplitShadeStateController() + splitShadeStateController ); mQsController.init(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index b5841a9de150..215f8b1c462f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.shade import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper import android.view.Display import android.view.WindowManager import androidx.test.filters.SmallTest @@ -54,6 +55,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) @SmallTest class ShadeControllerImplTest : SysuiTestCase() { private val executor = FakeExecutor(FakeSystemClock()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt index 2be1c09843c1..bcb060ddb417 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt @@ -16,50 +16,53 @@ package com.android.systemui.shade.data.repository -import android.app.ActivityManager import android.app.StatusBarManager.DISABLE2_NONE import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS import android.content.pm.UserInfo import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.internal.logging.UiEventLogger -import com.android.keyguard.KeyguardUpdateMonitor +import com.android.SysUITestModule +import com.android.TestMocksModule import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeTransitionModel +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState -import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.res.R -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.telephony.data.repository.FakeTelephonyRepository -import com.android.systemui.telephony.domain.interactor.TelephonyInteractor +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.user.data.model.UserSwitcherSettingsModel import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.interactor.GuestUserInteractor -import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode -import com.android.systemui.user.domain.interactor.RefreshUsersScheduler -import com.android.systemui.user.domain.interactor.UserInteractor -import com.android.systemui.util.mockito.mock +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -70,50 +73,55 @@ import org.mockito.MockitoAnnotations @SmallTest @OptIn(ExperimentalCoroutinesApi::class) class ShadeInteractorTest : SysuiTestCase() { - private lateinit var underTest: ShadeInteractor - private val utils = SceneTestUtils(this) - private val testScope = utils.testScope - private val featureFlags = FakeFeatureFlags() - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - private val userSetupRepository = FakeUserSetupRepository() - private val userRepository = FakeUserRepository() - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val keyguardRepository = FakeKeyguardRepository() - private val shadeRepository = FakeShadeRepository() - private val configurationRepository = FakeConfigurationRepository() - private val sharedNotificationContainerInteractor = - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - - @Mock private lateinit var manager: UserManager - @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - @Mock private lateinit var activityStarter: ActivityStarter - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var activityManager: ActivityManager - @Mock private lateinit var uiEventLogger: UiEventLogger - @Mock private lateinit var guestInteractor: GuestUserInteractor - - private lateinit var userInteractor: UserInteractor + @Mock private lateinit var dozeParameters: DozeParameters + + private lateinit var testComponent: TestComponent + + private val configurationRepository + get() = testComponent.configurationRepository + private val deviceProvisioningRepository + get() = testComponent.deviceProvisioningRepository + private val disableFlagsRepository + get() = testComponent.disableFlagsRepository + private val keyguardRepository + get() = testComponent.keyguardRepository + private val keyguardTransitionRepository + get() = testComponent.keygaurdTransitionRepository + private val powerRepository + get() = testComponent.powerRepository + private val sceneInteractor + get() = testComponent.sceneInteractor + private val shadeRepository + get() = testComponent.shadeRepository + private val testScope + get() = testComponent.testScope + private val userRepository + get() = testComponent.userRepository + private val userSetupRepository + get() = testComponent.userSetupRepository + + private lateinit var underTest: ShadeInteractor @Before fun setUp() { MockitoAnnotations.initMocks(this) - featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) - featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) - - val refreshUsersScheduler = - RefreshUsersScheduler( - applicationScope = testScope.backgroundScope, - mainDispatcher = utils.testDispatcher, - repository = userRepository, - ) + testComponent = + DaggerShadeInteractorTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FACE_AUTH_REFACTOR, false) + set(Flags.FULL_SCREEN_USER_SWITCHER, true) + }, + mocks = + TestMocksModule( + dozeParameters = dozeParameters, + ), + ) + underTest = testComponent.underTest runBlocking { val userInfos = @@ -131,44 +139,6 @@ class ShadeInteractorTest : SysuiTestCase() { userRepository.setUserInfos(userInfos) userRepository.setSelectedUserInfo(userInfos[0]) } - userInteractor = - UserInteractor( - applicationContext = context, - repository = userRepository, - activityStarter = activityStarter, - keyguardInteractor = - KeyguardInteractorFactory.create(featureFlags = featureFlags) - .keyguardInteractor, - featureFlags = featureFlags, - manager = manager, - headlessSystemUserMode = headlessSystemUserMode, - applicationScope = testScope.backgroundScope, - telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), - broadcastDispatcher = fakeBroadcastDispatcher, - keyguardUpdateMonitor = keyguardUpdateMonitor, - backgroundDispatcher = utils.testDispatcher, - activityManager = activityManager, - refreshUsersScheduler = refreshUsersScheduler, - guestUserInteractor = guestInteractor, - uiEventLogger = uiEventLogger, - userRestrictionChecker = mock(), - ) - underTest = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository, - deviceProvisionedController, - userInteractor, - sharedNotificationContainerInteractor, - shadeRepository, - ) } @Test @@ -188,7 +158,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_deviceNotProvisioned_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) + deviceProvisioningRepository.setDeviceProvisioned(false) val actual by collectLastValue(underTest.isExpandToQsEnabled) @@ -198,7 +168,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_userNotSetupAndSimpleUserSwitcher_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(false) userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true)) @@ -211,7 +181,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_shadeNotEnabled_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(true) disableFlagsRepository.disableFlags.value = @@ -227,7 +197,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_quickSettingsNotEnabled_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(true) disableFlagsRepository.disableFlags.value = @@ -242,7 +212,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_dozing_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(true) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -259,7 +229,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_userSetup_true() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -276,7 +246,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_notSimpleUserSwitcher_true() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -293,7 +263,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToDozingUpdates() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -321,7 +291,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToDisableUpdates() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -353,7 +323,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToUserUpdates() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -625,7 +595,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -662,7 +633,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -698,7 +670,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -974,7 +947,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1011,7 +985,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = true, + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1048,7 +1023,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1085,7 +1061,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = true, + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1120,8 +1097,9 @@ class ShadeInteractorTest : SysuiTestCase() { ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = SceneKey.QuickSettings, - progress = progress, - isUserInputDriven = true, + progress = MutableStateFlow(0f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1129,4 +1107,168 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN interacting is false assertThat(interacting).isFalse() } + + @Test + fun isShadeTouchable_isFalse_whenFrpIsActive() = + testScope.runTest { + deviceProvisioningRepository.setFactoryResetProtectionActive(true) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isFalse() + } + + @Test + fun isShadeTouchable_isFalse_whenDeviceAsleepAndNotPulsing() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == false + // TODO: remove? + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_AOD, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isFalse() + } + + @Test + fun isShadeTouchable_isTrue_whenDeviceAsleepAndPulsing() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == false + // TODO: remove? + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_PULSING, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isTrue() + } + + @Test + fun isShadeTouchable_isFalse_whenStartingToSleepAndNotControlScreenOff() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == true + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParameters.shouldControlScreenOff()).thenReturn(false) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isFalse() + } + + @Test + fun isShadeTouchable_isTrue_whenStartingToSleepAndControlScreenOff() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == true + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParameters.shouldControlScreenOff()).thenReturn(true) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isTrue() + } + + @Test + fun isShadeTouchable_isTrue_whenNotAsleep() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isTrue() + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: ShadeInteractor + + val configurationRepository: FakeConfigurationRepository + val deviceProvisioningRepository: FakeDeviceProvisioningRepository + val disableFlagsRepository: FakeDisableFlagsRepository + val keyguardRepository: FakeKeyguardRepository + val keygaurdTransitionRepository: FakeKeyguardTransitionRepository + val powerRepository: FakePowerRepository + val sceneInteractor: SceneInteractor + val shadeRepository: FakeShadeRepository + val testScope: TestScope + val userRepository: FakeUserRepository + val userSetupRepository: FakeUserSetupRepository + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + mocks: TestMocksModule, + ): TestComponent + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt index 7463e65d1d6f..6eeafefd0ac1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.shade.data.repository 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.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionStateManager @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class ShadeRepositoryImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index 0925858d740d..bb20d94e7d39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.shade.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -16,15 +17,15 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class ShadeHeaderViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope @@ -84,7 +85,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) @@ -102,7 +104,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.QuickSettings, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) @@ -120,7 +123,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 602bd5fded3d..c4237827c3ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -37,11 +38,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class ShadeSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) 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 e71473681211..ae659f4d2676 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 @@ -131,11 +131,10 @@ class ClockRegistryTest : SysuiTestCase() { fun addClock( id: ClockId, - name: String, create: (ClockId) -> ClockController = ::failFactory, getThumbnail: (ClockId) -> Drawable? = ::failThumbnail ): FakeClockPlugin { - metadata.add(ClockMetadata(id, name)) + metadata.add(ClockMetadata(id)) createCallbacks[id] = create thumbnailCallbacks[id] = getThumbnail return this @@ -149,7 +148,7 @@ class ClockRegistryTest : SysuiTestCase() { scope = TestScope(dispatcher) fakeDefaultProvider = FakeClockPlugin() - .addClock(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME, { mockDefaultClock }, { mockThumbnail }) + .addClock(DEFAULT_CLOCK_ID, { mockDefaultClock }, { mockThumbnail }) whenever(mockContext.contentResolver).thenReturn(mockContentResolver) val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>() @@ -183,13 +182,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun pluginRegistration_CorrectState() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = FakeLifecycle("1", plugin1) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3") - .addClock("clock_4", "clock 4") + .addClock("clock_3") + .addClock("clock_4") val lifecycle2 = FakeLifecycle("2", plugin2) pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1) @@ -198,11 +197,11 @@ class ClockRegistryTest : SysuiTestCase() { assertEquals( list.toSet(), setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1"), - ClockMetadata("clock_2", "clock 2"), - ClockMetadata("clock_3", "clock 3"), - ClockMetadata("clock_4", "clock 4") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1"), + ClockMetadata("clock_2"), + ClockMetadata("clock_3"), + ClockMetadata("clock_4") ) ) } @@ -216,13 +215,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail }) - .addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail }) + .addClock("clock_1", { mockClock }, { mockThumbnail }) + .addClock("clock_2", { mockClock }, { mockThumbnail }) val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1) @@ -231,9 +230,9 @@ class ClockRegistryTest : SysuiTestCase() { assertEquals( list.toSet(), setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1"), - ClockMetadata("clock_2", "clock 2") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1"), + ClockMetadata("clock_2") ) ) @@ -248,13 +247,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun createCurrentClock_pluginConnected() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3", { mockClock }) - .addClock("clock_4", "clock 4") + .addClock("clock_3", { mockClock }) + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) registry.applySettings(ClockSettings("clock_3", null)) @@ -268,13 +267,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun activeClockId_changeAfterPluginConnected() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3", { mockClock }) - .addClock("clock_4", "clock 4") + .addClock("clock_3", { mockClock }) + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) registry.applySettings(ClockSettings("clock_3", null)) @@ -289,13 +288,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun createDefaultClock_pluginDisconnected() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3") - .addClock("clock_4", "clock 4") + .addClock("clock_3") + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) registry.applySettings(ClockSettings("clock_3", null)) @@ -310,13 +309,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun pluginRemoved_clockAndListChanged() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3", { mockClock }) - .addClock("clock_4", "clock 4") + .addClock("clock_3", { mockClock }) + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) var changeCallCount = 0 @@ -415,13 +414,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun pluginAddRemove_concurrentModification() { - val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1") + val plugin1 = FakeClockPlugin().addClock("clock_1") val lifecycle1 = FakeLifecycle("1", plugin1) - val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2") + val plugin2 = FakeClockPlugin().addClock("clock_2") val lifecycle2 = FakeLifecycle("2", plugin2) - val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3") + val plugin3 = FakeClockPlugin().addClock("clock_3") val lifecycle3 = FakeLifecycle("3", plugin3) - val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4") + val plugin4 = FakeClockPlugin().addClock("clock_4") val lifecycle4 = FakeLifecycle("4", plugin4) // Set the current clock to the final clock to load @@ -450,10 +449,10 @@ class ClockRegistryTest : SysuiTestCase() { // Verify all plugins were correctly loaded into the registry assertEquals(registry.getClocks().toSet(), setOf( - ClockMetadata("DEFAULT", "Default Clock"), - ClockMetadata("clock_2", "clock 2"), - ClockMetadata("clock_3", "clock 3"), - ClockMetadata("clock_4", "clock 4") + ClockMetadata("DEFAULT"), + ClockMetadata("clock_2"), + ClockMetadata("clock_3"), + ClockMetadata("clock_4") )) } @@ -527,8 +526,8 @@ class ClockRegistryTest : SysuiTestCase() { featureFlags.set(TRANSIT_CLOCK, flag) registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK) val plugin = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("DIGITAL_CLOCK_METRO", "metro clock") + .addClock("clock_1") + .addClock("DIGITAL_CLOCK_METRO") val lifecycle = FakeLifecycle("metro", plugin) pluginListener.onPluginLoaded(plugin, mockContext, lifecycle) @@ -536,17 +535,17 @@ class ClockRegistryTest : SysuiTestCase() { if (flag) { assertEquals( setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1"), - ClockMetadata("DIGITAL_CLOCK_METRO", "metro clock") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1"), + ClockMetadata("DIGITAL_CLOCK_METRO") ), list.toSet() ) } else { assertEquals( setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1") ), list.toSet() ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt index 7a2d12269885..b8fe2f911b50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.smartspace 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.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -33,7 +32,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class BcSmartspaceConfigProviderTest : SysuiTestCase() { @Mock private lateinit var featureFlags: FeatureFlags diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt index f1c181fb2235..e09385934991 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt @@ -26,7 +26,6 @@ import android.view.ViewGroup import android.widget.FrameLayout 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.dreams.smartspace.DreamSmartspaceController import com.android.systemui.plugins.BcSmartspaceConfigPlugin @@ -53,7 +52,6 @@ import org.mockito.MockitoAnnotations import org.mockito.Spy @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class DreamSmartspaceControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt index 7af29ba28df1..886c61ae29dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt @@ -25,7 +25,6 @@ import android.provider.Settings 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 import com.android.systemui.settings.UserTracker import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter @@ -52,7 +51,6 @@ import org.mockito.MockitoAnnotations @SmallTest @TestableLooper.RunWithLooper -@RoboPilotTest @RunWith(AndroidJUnit4::class) class LockscreenAndDreamTargetFilterTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt index a7c223dc4b8a..0b5aea7d8683 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.smartspace 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 import com.android.systemui.smartspace.preconditions.LockscreenPrecondition import com.android.systemui.statusbar.policy.DeviceProvisionedController @@ -36,7 +35,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class LockscreenPreconditionTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 14e58e5f570a..e8923a5baca8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -5,41 +5,32 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule import com.android.systemui.ExpandHelper -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase -import com.android.systemui.classifier.FalsingCollector -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.dump.DumpManager -import com.android.systemui.keyguard.WakefulnessLifecycle -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags import com.android.systemui.media.controls.ui.MediaHierarchyManager -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS -import com.android.systemui.power.domain.interactor.PowerInteractorFactory -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.res.R import com.android.systemui.shade.ShadeViewController -import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper -import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.ScrimController -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.policy.FakeConfigurationController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.util.mockito.mock +import com.android.systemui.user.domain.UserDomainLayerModule +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import org.junit.After import org.junit.Assert.assertFalse @@ -60,9 +51,8 @@ import org.mockito.Mockito import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +import org.mockito.junit.MockitoJUnit private fun <T> anyObject(): T { return Mockito.anyObject<T>() @@ -73,68 +63,38 @@ private fun <T> anyObject(): T { @RunWith(AndroidTestingRunner::class) @OptIn(ExperimentalCoroutinesApi::class) class LockscreenShadeTransitionControllerTest : SysuiTestCase() { - private val utils = SceneTestUtils(this) - private val testScope = utils.testScope - lateinit var transitionController: LockscreenShadeTransitionController + private lateinit var testComponent: TestComponent + + private val transitionController + get() = testComponent.transitionController + private val configurationController + get() = testComponent.configurationController + private val disableFlagsRepository + get() = testComponent.disableFlagsRepository + private val testScope + get() = testComponent.testScope + lateinit var row: ExpandableNotificationRow - @Mock lateinit var statusbarStateController: SysuiStatusBarStateController - @Mock lateinit var logger: LSShadeTransitionLogger - @Mock lateinit var dumpManager: DumpManager + + @Mock lateinit var centralSurfaces: CentralSurfaces + @Mock lateinit var depthController: NotificationShadeDepthController + @Mock lateinit var expandHelperCallback: ExpandHelper.Callback @Mock lateinit var keyguardBypassController: KeyguardBypassController @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager - @Mock lateinit var falsingCollector: FalsingCollector - @Mock lateinit var ambientState: AmbientState - @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager + @Mock lateinit var nsslController: NotificationStackScrollLayoutController + @Mock lateinit var qS: QS @Mock lateinit var scrimController: ScrimController - @Mock lateinit var falsingManager: FalsingManager @Mock lateinit var shadeViewController: ShadeViewController - @Mock lateinit var nsslController: NotificationStackScrollLayoutController - @Mock lateinit var depthController: NotificationShadeDepthController @Mock lateinit var stackscroller: NotificationStackScrollLayout - @Mock lateinit var expandHelperCallback: ExpandHelper.Callback - @Mock lateinit var mCentralSurfaces: CentralSurfaces - @Mock lateinit var qS: QS - @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller - @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller - @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController - @Mock lateinit var activityStarter: ActivityStarter + @Mock lateinit var statusbarStateController: SysuiStatusBarStateController @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val keyguardRepository = FakeKeyguardRepository() - private val configurationRepository = FakeConfigurationRepository() - private val sharedNotificationContainerInteractor = SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - private val shadeInteractor = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository = FakeUserSetupRepository(), - deviceProvisionedController = mock(), - userInteractor = mock(), - sharedNotificationContainerInteractor, - repository = FakeShadeRepository(), - ) - private val powerInteractor = PowerInteractorFactory.create().powerInteractor - @JvmField @Rule val mockito = MockitoJUnit.rule() - private val configurationController = FakeConfigurationController() + @JvmField @Rule val mockito = MockitoJUnit.rule() @Before fun setup() { - // By default, have the shade enabled - disableFlagsRepository.disableFlags.value = DisableFlagsModel() - testScope.runCurrent() - val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)) row = helper.createRow() context @@ -143,55 +103,9 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { context .getOrCreateTestableResources() .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100) - transitionController = - LockscreenShadeTransitionController( - statusBarStateController = statusbarStateController, - logger = logger, - keyguardBypassController = keyguardBypassController, - lockScreenUserManager = lockScreenUserManager, - falsingCollector = falsingCollector, - ambientState = ambientState, - mediaHierarchyManager = mediaHierarchyManager, - depthController = depthController, - wakefulnessLifecycle = wakefulnessLifecycle, - context = context, - configurationController = configurationController, - falsingManager = falsingManager, - dumpManager = dumpManager, - splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller }, - singleShadeOverScrollerFactory = { singleShadeOverScroller }, - scrimTransitionController = - LockscreenShadeScrimTransitionController( - scrimController, - context, - configurationController, - dumpManager, - ResourcesSplitShadeStateController() - ), - keyguardTransitionControllerFactory = { notificationPanelController -> - LockscreenShadeKeyguardTransitionController( - mediaHierarchyManager, - notificationPanelController, - context, - configurationController, - dumpManager, - ResourcesSplitShadeStateController() - ) - }, - qsTransitionControllerFactory = { qsTransitionController }, - activityStarter = activityStarter, - shadeRepository = FakeShadeRepository(), - shadeInteractor = shadeInteractor, - powerInteractor = powerInteractor, - splitShadeStateController = ResourcesSplitShadeStateController() - ) - transitionController.addCallback(transitionControllerCallback) + whenever(nsslController.view).thenReturn(stackscroller) whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback) - transitionController.shadeViewController = shadeViewController - transitionController.centralSurfaces = mCentralSurfaces - transitionController.qS = qS - transitionController.setStackScroller(nsslController) whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(nsslController.isInLockedDownShade).thenReturn(false) whenever(qS.isFullyCollapsed).thenReturn(true) @@ -199,9 +113,36 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { .thenReturn(true) whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true) whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true) - whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false) whenever(keyguardBypassController.bypassEnabled).thenReturn(false) - clearInvocations(mCentralSurfaces) + + testComponent = + DaggerLockscreenShadeTransitionControllerTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FULL_SCREEN_USER_SWITCHER, false) + }, + mocks = + TestMocksModule( + notificationShadeDepthController = depthController, + keyguardBypassController = keyguardBypassController, + mediaHierarchyManager = mediaHierarchyManager, + notificationLockscreenUserManager = lockScreenUserManager, + notificationStackScrollLayoutController = nsslController, + scrimController = scrimController, + statusBarStateController = statusbarStateController, + ) + ) + + transitionController.addCallback(transitionControllerCallback) + transitionController.shadeViewController = shadeViewController + transitionController.centralSurfaces = centralSurfaces + transitionController.qS = qS + transitionController.setStackScroller(nsslController) + clearInvocations(centralSurfaces) + + testScope.runCurrent() } @After @@ -282,7 +223,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.goToLockedShade(null) verify(statusbarStateController, never()).setState(anyInt()) verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true) - verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject()) + verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject()) } @Test @@ -318,7 +259,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(transitionControllerCallback, never()) .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong()) - verify(qsTransitionController, never()).dragDownAmount = anyFloat() + verify(qS, never()).setTransitionToFullShadeProgress(anyBoolean(), anyFloat(), anyFloat()) } @Test @@ -329,7 +270,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(transitionControllerCallback) .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong()) - verify(qsTransitionController).dragDownAmount = 10f + verify(qS).setTransitionToFullShadeProgress(eq(true), anyFloat(), anyFloat()) verify(depthController).transitionToFullShadeProgress = anyFloat() } @@ -532,8 +473,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.dragDownAmount = 10f - verify(singleShadeOverScroller).expansionDragDownAmount = 10f - verifyZeroInteractions(splitShadeOverScroller) + verify(nsslController).setOverScrollAmount(0) + verify(scrimController, never()).setNotificationsOverScrollAmount(anyInt()) } @Test @@ -542,8 +483,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.dragDownAmount = 10f - verify(splitShadeOverScroller).expansionDragDownAmount = 10f - verifyZeroInteractions(singleShadeOverScroller) + verify(nsslController).setOverScrollAmount(0) + verify(scrimController).setNotificationsOverScrollAmount(0) } @Test @@ -591,6 +532,32 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { progress: Float, lockScreenNotificationsProgress: Float ) { - scrimController.setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress) + setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress) + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val transitionController: LockscreenShadeTransitionController + + val configurationController: FakeConfigurationController + val disableFlagsRepository: FakeDisableFlagsRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + mocks: TestMocksModule, + ): TestComponent + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt index fbb8ebfb3e3b..20e5c43cba19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt @@ -29,9 +29,9 @@ import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.mockito.mock import org.junit.Before import org.junit.Test @@ -52,7 +52,7 @@ class PulseExpansionHandlerTest : SysuiTestCase() { private val collapsedHeight = 300 private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock() private val bypassController: KeyguardBypassController = mock() - private val headsUpManager: HeadsUpManagerPhone = mock() + private val headsUpManager: HeadsUpManager = mock() private val roundnessManager: NotificationRoundnessManager = mock() private val configurationController: ConfigurationController = mock() private val statusBarStateController: StatusBarStateController = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt index 5b919d9a6a82..3fef1d9832f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt @@ -72,7 +72,7 @@ class AccessPointControllerImplTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(wifiPickerTracker) + `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(wifiPickerTracker) `when`(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntryConnected) `when`(wifiPickerTracker.wifiEntries).thenReturn(ArrayList<WifiEntry>().apply { @@ -165,7 +165,7 @@ class AccessPointControllerImplTest : SysuiTestCase() { @Test fun testReturnEmptyListWhenNoWifiPickerTracker() { - `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(null) + `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(null) val otherController = AccessPointControllerImpl( userManager, userTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt index d86f8bbbeb15..235ac5c2e9cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt @@ -8,15 +8,15 @@ import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.res.R import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpUtil -import com.android.systemui.res.R import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import kotlinx.coroutines.test.TestScope @@ -37,7 +37,7 @@ import org.mockito.junit.MockitoJUnit @RunWithLooper class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { @Mock lateinit var notificationListContainer: NotificationListContainer - @Mock lateinit var headsUpManager: HeadsUpManagerPhone + @Mock lateinit var headsUpManager: HeadsUpManager @Mock lateinit var jankMonitor: InteractionJankMonitor @Mock lateinit var onFinishAnimationCallback: Runnable diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 257cc5b1b85c..4f1581cced91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -43,8 +43,8 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.NotificationGroupTestHelper -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -87,7 +87,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private val notifPipeline: NotifPipeline = mock() private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true) - private val headsUpManager: HeadsUpManager = mock() + private val headsUpManager: HeadsUpManagerPhone = mock() private val headsUpViewBinder: HeadsUpViewBinder = mock() private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock() private val remoteInputManager: NotificationRemoteInputManager = mock() @@ -435,7 +435,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private fun addHUN(entry: NotificationEntry) { huns.add(entry) - whenever(headsUpManager.topEntry).thenReturn(entry) + whenever(headsUpManager.getTopEntry()).thenReturn(entry) onHeadsUpChangedListener.onHeadsUpStateChanged(entry, true) notifLifetimeExtender.cancelLifetimeExtension(entry) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt index fbd61f4fe3bd..546abd4ec79a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt @@ -23,7 +23,6 @@ import android.provider.Settings import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.advanceTimeBy import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -40,10 +39,10 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.mockito.any @@ -247,7 +246,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { unseenFilter.onCleanup() // THEN: The SeenNotificationProvider has been updated to reflect the suppression - assertThat(seenNotificationsProvider.hasFilteredOutSeenNotifications).isTrue() + assertThat(notificationListInteractor.hasFilteredOutSeenNotifications.value).isTrue() } } @@ -598,7 +597,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { FakeSettings().apply { putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1) } - val seenNotificationsProvider = SeenNotificationsProviderImpl() + val notificationListInteractor = NotificationListInteractor(NotificationListRepository()) val keyguardCoordinator = KeyguardCoordinator( testDispatcher, @@ -611,7 +610,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { testScope.backgroundScope, sectionHeaderVisibilityProvider, fakeSettings, - seenNotificationsProvider, + notificationListInteractor, statusBarStateController, ) keyguardCoordinator.attach(notifPipeline) @@ -619,7 +618,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { KeyguardCoordinatorTestScope( keyguardCoordinator, testScope, - seenNotificationsProvider, + notificationListInteractor, fakeSettings, ) .testBlock() @@ -629,7 +628,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { private inner class KeyguardCoordinatorTestScope( private val keyguardCoordinator: KeyguardCoordinator, private val scope: TestScope, - val seenNotificationsProvider: SeenNotificationsProvider, + val notificationListInteractor: NotificationListInteractor, private val fakeSettings: FakeSettings, ) : CoroutineScope by scope { val testScheduler: TestCoroutineScheduler diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt new file mode 100644 index 000000000000..2f8f3bb14ee5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt @@ -0,0 +1,102 @@ +/* + * 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. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.data.repository + +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() { + + private val testComponent: TestComponent = + DaggerNotificationsKeyguardViewStateRepositoryTest_TestComponent.factory() + .create(test = this) + + @Test + fun areNotifsFullyHidden_reflectsWakeUpCoordinator() = + with(testComponent) { + testScope.runTest { + whenever(mockWakeUpCoordinator.notificationsFullyHidden).thenReturn(false) + val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden) + runCurrent() + + assertThat(notifsFullyHidden).isFalse() + + withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) } + .onFullyHiddenChanged(true) + runCurrent() + + assertThat(notifsFullyHidden).isTrue() + } + } + + @Test + fun isPulseExpanding_reflectsWakeUpCoordinator() = + with(testComponent) { + testScope.runTest { + whenever(mockWakeUpCoordinator.isPulseExpanding()).thenReturn(false) + val isPulseExpanding by collectLastValue(underTest.isPulseExpanding) + runCurrent() + + assertThat(isPulseExpanding).isFalse() + + withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) } + .onPulseExpansionChanged(true) + runCurrent() + + assertThat(isPulseExpanding).isTrue() + } + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationsKeyguardViewStateRepositoryImpl + + val mockWakeUpCoordinator: NotificationWakeUpCoordinator + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt new file mode 100644 index 000000000000..705a5a3f9792 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt @@ -0,0 +1,89 @@ +/* + * 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.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@SmallTest +class NotificationsKeyguardInteractorTest : SysuiTestCase() { + + private val testComponent: TestComponent = + DaggerNotificationsKeyguardInteractorTest_TestComponent.factory().create(test = this) + + @Test + fun areNotifsFullyHidden_reflectsRepository() = + with(testComponent) { + testScope.runTest { + repository.setNotificationsFullyHidden(false) + val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden) + runCurrent() + + assertThat(notifsFullyHidden).isFalse() + + repository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(notifsFullyHidden).isTrue() + } + } + + @Test + fun isPulseExpanding_reflectsRepository() = + with(testComponent) { + testScope.runTest { + repository.setPulseExpanding(false) + val isPulseExpanding by collectLastValue(underTest.isPulseExpanding) + runCurrent() + + assertThat(isPulseExpanding).isFalse() + + repository.setPulseExpanding(true) + runCurrent() + + assertThat(isPulseExpanding).isTrue() + } + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationsKeyguardInteractor + + val repository: FakeNotificationsKeyguardViewStateRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create(@BindsInstance test: SysuiTestCase): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java index b120c4747cb9..f72142ffc6d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java @@ -11,10 +11,10 @@ * 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. */ -package com.android.systemui.statusbar.notification.row; +package com.android.systemui.statusbar.notification.footer.ui.view; import static com.google.common.truth.Truth.assertThat; @@ -31,8 +31,8 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.res.R; import org.junit.Before; import org.junit.Test; 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 index 7caa5ccc5837..e57986ddfa18 100644 --- 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 @@ -26,9 +26,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.user.domain.UserDomainLayerModule -import com.android.systemui.util.mockito.whenever import dagger.BindsInstance import dagger.Component import org.junit.Assert.assertFalse @@ -37,7 +35,6 @@ 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 @@ -46,7 +43,6 @@ import org.mockito.MockitoAnnotations class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() { @Mock private lateinit var dozeParams: DozeParameters - @Mock private lateinit var aodIcons: NotificationIconContainer private lateinit var testComponent: TestComponent private val underTest @@ -85,15 +81,6 @@ class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() 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 - } - @SysUISingleton @Component( modules = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt new file mode 100644 index 000000000000..31efebbc5b60 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt @@ -0,0 +1,493 @@ +/* + * 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. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeTransitionModel +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository +import com.android.systemui.user.domain.UserDomainLayerModule +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.ui.AnimatedValue +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +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 +@RunWith(AndroidJUnit4::class) +class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { + + @Mock private lateinit var dozeParams: DozeParameters + @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController + + private lateinit var testComponent: TestComponent + private val underTest: NotificationIconContainerAlwaysOnDisplayViewModel + get() = testComponent.underTest + private val deviceEntryRepository: FakeDeviceEntryRepository + get() = testComponent.deviceEntryRepository + private val deviceProvisioningRepository: FakeDeviceProvisioningRepository + get() = testComponent.deviceProvisioningRepository + private val keyguardRepository: FakeKeyguardRepository + get() = testComponent.keyguardRepository + private val keyguardTransitionRepository: FakeKeyguardTransitionRepository + get() = testComponent.keyguardTransitionRepository + private val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository + get() = testComponent.notifsKeyguardRepository + private val powerRepository: FakePowerRepository + get() = testComponent.powerRepository + private val scope: TestScope + get() = testComponent.scope + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + testComponent = + DaggerNotificationIconContainerAlwaysOnDisplayViewModelTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + setDefault(Flags.FACE_AUTH_REFACTOR) + set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) + setDefault(Flags.NEW_AOD_TRANSITION) + }, + mocks = + TestMocksModule( + dozeParameters = dozeParams, + screenOffAnimationController = screenOffAnimController, + ), + ) + + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + deviceProvisioningRepository.setFactoryResetProtectionActive(false) + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + } + + @Test + fun animationsEnabled_isFalse_whenFrpIsActive() = + scope.runTest { + deviceProvisioningRepository.setFactoryResetProtectionActive(true) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_AOD, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_PULSING, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(true) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenNotAsleep() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenKeyguardIsShowing() = + scope.runTest { + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + runCurrent() + + assertThat(animationsEnabled).isTrue() + + keyguardRepository.setKeyguardOccluded(true) + runCurrent() + + assertThat(animationsEnabled).isFalse() + + keyguardRepository.setKeyguardShowing(false) + keyguardRepository.setKeyguardOccluded(true) + runCurrent() + + assertThat(animationsEnabled).isFalse() + } + + @Test + fun isDozing_startAodTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isDozing_startDozeTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.DOZING, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = false)) + } + + @Test + fun isDozing_startDozeToAodTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.DOZING, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isNotDozing_startAodToGoneTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(false, isAnimating = true)) + } + + @Test + fun isDozing_stopAnimation() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + + underTest.completeDozeAnimation() + runCurrent() + + assertThat(isDozing?.isAnimating).isEqualTo(false) + } + + @Test + fun isNotVisible_pulseExpanding() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(true) + runCurrent() + + assertThat(isVisible?.value).isFalse() + } + + @Test + fun isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + to = KeyguardState.GONE, + transitionState = TransitionState.FINISHED, + ) + ) + whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(false, isAnimating = false)) + } + + @Test + fun isVisible_bypassEnabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + deviceEntryRepository.setBypassEnabled(true) + runCurrent() + + assertThat(isVisible?.value).isTrue() + } + + @Test + fun isNotVisible_pulseExpanding_notBypassing() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(true) + deviceEntryRepository.setBypassEnabled(false) + runCurrent() + + assertThat(isVisible?.value).isEqualTo(false) + } + + @Test + fun isVisible_notifsFullyHidden_bypassEnabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + runCurrent() + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(true) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(false) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false)) + } + + @Test + fun isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(true) + whenever(dozeParams.displayNeedsBlanking).thenReturn(true) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false)) + } + + @Test + fun isVisible_notifsFullyHidden_bypassDisabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + runCurrent() + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(true) + whenever(dozeParams.displayNeedsBlanking).thenReturn(false) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isVisible_stopAnimation() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(true) + whenever(dozeParams.displayNeedsBlanking).thenReturn(false) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + underTest.completeVisibilityAnimation() + runCurrent() + + assertThat(isVisible?.isAnimating).isEqualTo(false) + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationIconContainerAlwaysOnDisplayViewModel + + val deviceEntryRepository: FakeDeviceEntryRepository + val deviceProvisioningRepository: FakeDeviceProvisioningRepository + val keyguardRepository: FakeKeyguardRepository + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository + val powerRepository: FakePowerRepository + val scope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + mocks: TestMocksModule, + featureFlags: FakeFeatureFlagsClassicModule, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt new file mode 100644 index 000000000000..e1e7f92265f0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -0,0 +1,276 @@ +/* + * 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. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeTransitionModel +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository +import com.android.systemui.user.domain.UserDomainLayerModule +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +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 +@RunWith(AndroidJUnit4::class) +class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { + + @Mock lateinit var dozeParams: DozeParameters + + private lateinit var testComponent: TestComponent + private val underTest: NotificationIconContainerStatusBarViewModel + get() = testComponent.underTest + private val deviceProvisioningRepository + get() = testComponent.deviceProvisioningRepository + private val keyguardTransitionRepository + get() = testComponent.keyguardTransitionRepository + private val keyguardRepository + get() = testComponent.keyguardRepository + private val powerRepository + get() = testComponent.powerRepository + private val scope + get() = testComponent.scope + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + testComponent = + DaggerNotificationIconContainerStatusBarViewModelTest_TestComponent.factory() + .create( + test = this, + // Configurable bindings + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FACE_AUTH_REFACTOR, value = false) + set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) + }, + mocks = + TestMocksModule( + dozeParameters = dozeParams, + ), + ) + + keyguardRepository.setKeyguardShowing(false) + deviceProvisioningRepository.setFactoryResetProtectionActive(false) + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + } + + @Test + fun animationsEnabled_isFalse_whenFrpIsActive() = + scope.runTest { + deviceProvisioningRepository.setFactoryResetProtectionActive(true) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_AOD, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_PULSING, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(true) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenNotAsleep() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenKeyguardIsNotShowing() = + scope.runTest { + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setKeyguardShowing(true) + runCurrent() + + assertThat(animationsEnabled).isFalse() + + keyguardRepository.setKeyguardShowing(false) + runCurrent() + + assertThat(animationsEnabled).isTrue() + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + // Real impls + BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationIconContainerStatusBarViewModel + + val deviceProvisioningRepository: FakeDeviceProvisioningRepository + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + val keyguardRepository: FakeKeyguardRepository + val powerRepository: FakePowerRepository + val scope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + mocks: TestMocksModule, + featureFlags: FakeFeatureFlagsClassicModule, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java index 8f39ee6a6e9f..83f2a5d4fbdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java @@ -62,11 +62,15 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.FakeGlobalSettings; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.utils.os.FakeHandler; +import dagger.BindsInstance; +import dagger.Component; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -77,9 +81,6 @@ import org.mockito.MockitoAnnotations; import java.util.Map; import java.util.function.Consumer; -import dagger.BindsInstance; -import dagger.Component; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -93,7 +94,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { @Mock private HighPriorityProvider mHighPriorityProvider; @Mock private SysuiStatusBarStateController mStatusBarStateController; @Mock private UserTracker mUserTracker; - private final FakeSettings mFakeSettings = new FakeSettings(); + private final FakeSettings mSecureSettings = new FakeSettings(); + private final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings(); private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; private NotificationEntry mEntry; @@ -113,8 +115,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { mHighPriorityProvider, mStatusBarStateController, mUserTracker, - mFakeSettings, - mFakeSettings); + mSecureSettings, + mGlobalSettings); mKeyguardNotificationVisibilityProvider = component.getProvider(); for (CoreStartable startable : component.getCoreStartables().values()) { startable.start(); @@ -223,7 +225,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { Consumer<String> listener = mock(Consumer.class); mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); verify(listener).accept(anyString()); } @@ -234,7 +236,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { Consumer<String> listener = mock(Consumer.class); mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, true); verify(listener).accept(anyString()); } @@ -242,8 +244,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { @Test public void hideSilentNotificationsPerUserSettingWithHighPriorityParent() { when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); GroupEntry parent = new GroupEntryBuilder() .setKey("parent") .addChild(mEntry) @@ -264,8 +266,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { @Test public void keyguardShowing_hideSilentNotifications_perUserSetting() { when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); mEntry = new NotificationEntryBuilder() .setUser(new UserHandle(NOTIF_USER_ID)) .setImportance(IMPORTANCE_LOW) @@ -277,8 +279,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { @Test public void keyguardShowing_hideSilentNotifications_perUserSetting_withHighPriorityParent() { when(mKeyguardStateController.isShowing()).thenReturn(true); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); GroupEntry parent = new GroupEntryBuilder() .setKey("parent") .addChild(mEntry) @@ -300,10 +302,10 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { public void hideSilentOnLockscreenSetting() { // GIVEN an 'unfiltered-keyguard-showing' state and notifications shown on lockscreen setupUnfilteredState(mEntry); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); // WHEN the show silent notifs on lockscreen setting is false - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); // WHEN the notification is not high priority and not ambient mEntry = new NotificationEntryBuilder() @@ -319,10 +321,10 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { public void showSilentOnLockscreenSetting() { // GIVEN an 'unfiltered-keyguard-showing' state and notifications shown on lockscreen setupUnfilteredState(mEntry); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); // WHEN the show silent notifs on lockscreen setting is true - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true); // WHEN the notification is not high priority and not ambient mEntry = new NotificationEntryBuilder() @@ -338,7 +340,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { public void defaultSilentOnLockscreenSettingIsHide() { // GIVEN an 'unfiltered-keyguard-showing' state and notifications shown on lockscreen setupUnfilteredState(mEntry); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); // WHEN the notification is not high priority and not ambient mEntry = new NotificationEntryBuilder() @@ -348,7 +350,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { when(mHighPriorityProvider.isExplicitlyHighPriority(any())).thenReturn(false); // WhHEN the show silent notifs on lockscreen setting is unset - assertNull(mFakeSettings.getString(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)); + assertNull( + mSecureSettings.getString(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)); assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @@ -359,7 +362,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { Consumer<String> listener = mock(Consumer.class); mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); - mFakeSettings.putBool(Settings.Global.ZEN_MODE, true); + mGlobalSettings.putBool(Settings.Global.ZEN_MODE, true); verify(listener).accept(anyString()); } @@ -370,7 +373,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { Consumer<String> listener = mock(Consumer.class); mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true); verify(listener).accept(anyString()); } @@ -470,8 +473,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { public void highPriorityCharacteristicsIgnored() { // GIVEN an 'unfiltered-keyguard-showing' state with silent notifications hidden setupUnfilteredState(mEntry); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); // WHEN the notification doesn't exceed the threshold to show on the lockscreen, but does // have the "high priority characteristics" that would promote it to high priority @@ -557,7 +560,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .build()); // WHEN its parent does exceed threshold tot show on the lockscreen - mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); + mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); when(mHighPriorityProvider.isExplicitlyHighPriority(parent)).thenReturn(true); // THEN filter out the entry regardless of parent diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java index c9b77c5372df..9c20e541e35f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java @@ -54,9 +54,9 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -132,7 +132,8 @@ public class FeedbackInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), - mMockNotificationRow, mAssistantFeedbackController); + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, + mNotificationGutsManager); final TextView textView = mFeedbackInfo.findViewById(R.id.pkg_name); assertTrue(textView.getText().toString().contains("App Name")); } @@ -143,7 +144,8 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), - mMockNotificationRow, mAssistantFeedbackController); + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, + mNotificationGutsManager); final ImageView iconView = mFeedbackInfo.findViewById(R.id.pkg_icon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -153,7 +155,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_SILENCED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically demoted to Silent by the system. " + "Let the developer know your feedback. Was this correct?", @@ -165,7 +167,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_PROMOTED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically ranked higher in your shade. " + "Let the developer know your feedback. Was this correct?", @@ -177,7 +179,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_ALERTED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically promoted to Default by the system. " + "Let the developer know your feedback. Was this correct?", @@ -189,7 +191,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_DEMOTED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically ranked lower in your shade. " + "Let the developer know your feedback. Was this correct?", @@ -199,7 +201,7 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testPositiveFeedback() { mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final View yes = mFeedbackInfo.findViewById(R.id.yes); yes.performClick(); @@ -216,7 +218,7 @@ public class FeedbackInfoTest extends SysuiTestCase { .thenReturn(true); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final View no = mFeedbackInfo.findViewById(R.id.no); no.performClick(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 9f2afdf1b168..8a730cfd7ddd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -33,7 +33,6 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -89,8 +88,8 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; @@ -152,7 +151,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private AssistantFeedbackController mAssistantFeedbackController; @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; @Mock private StatusBarStateController mStatusBarStateController; - @Mock private HeadsUpManagerPhone mHeadsUpManagerPhone; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private ActivityStarter mActivityStarter; @Mock private UserManager mUserManager; @@ -171,7 +170,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mTestScope.getBackgroundScope(), new WindowRootViewVisibilityRepository(mBarService, mExecutor), new FakeKeyguardRepository(), - mHeadsUpManagerPhone, + mHeadsUpManager, PowerInteractorFactory.create().getPowerInteractor()); mGutsManager = new NotificationGutsManager( @@ -196,9 +195,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mWindowRootViewVisibilityInteractor, mNotificationLockscreenUserManager, mStatusBarStateController, + mBarService, mDeviceProvisionedController, mMetricsLogger, - mHeadsUpManagerPhone, + mHeadsUpManager, mActivityStarter); mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer, mOnSettingsClickListener); @@ -239,7 +239,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { anyInt(), anyBoolean(), any(Runnable.class)); - verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), true); + verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), true); assertEquals(View.VISIBLE, guts.getVisibility()); mGutsManager.closeAndSaveGuts(false, false, true, 0, 0, false); @@ -247,7 +247,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean()); verify(row, times(1)).setGutsView(any()); mTestableLooper.processAllMessages(); - verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), false); + verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), false); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index ac680e6c902e..cb731082b89b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -61,6 +61,7 @@ import com.android.systemui.flags.Flags; import com.android.systemui.media.controls.util.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -81,14 +82,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; -import com.android.systemui.res.R; import com.android.systemui.wmshell.BubblesManager; import com.android.systemui.wmshell.BubblesTestActivity; @@ -123,7 +123,7 @@ public class NotificationTestHelper { private final GroupMembershipManager mGroupMembershipManager; private final GroupExpansionManager mGroupExpansionManager; private ExpandableNotificationRow mRow; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotifBindPipeline mBindPipeline; private final NotifCollectionListener mBindPipelineEntryListener; private final RowContentBindStage mBindStage; @@ -161,7 +161,7 @@ public class NotificationTestHelper { mKeyguardBypassController = mock(KeyguardBypassController.class); mGroupMembershipManager = mock(GroupMembershipManager.class); mGroupExpansionManager = mock(GroupExpansionManager.class); - mHeadsUpManager = mock(HeadsUpManagerPhone.class); + mHeadsUpManager = mock(HeadsUpManager.class); mIconManager = new IconManager( mock(CommonNotifCollection.class), mock(LauncherApps.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index ffe312be8fae..20197e3ed547 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -77,7 +77,6 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.NotifStats; @@ -88,13 +87,15 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent; import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback; +import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository; +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; @@ -124,7 +125,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private NotificationGutsManager mNotificationGutsManager; @Mock private NotificationsController mNotificationsController; @Mock private NotificationVisibilityProvider mVisibilityProvider; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private TunerService mTunerService; @Mock private DeviceProvisionedController mDeviceProvisionedController; @@ -170,8 +171,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; - private final SeenNotificationsProviderImpl mSeenNotificationsProvider = - new SeenNotificationsProviderImpl(); + private final NotificationListInteractor mNotificationListInteractor = + new NotificationListInteractor(new NotificationListRepository()); private NotificationStackScrollLayoutController mController; @@ -503,7 +504,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Test public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() { initController(/* viewIsAttached= */ true); - mSeenNotificationsProvider.setHasFilteredOutSeenNotifications(true); + mNotificationListInteractor.setHasFilteredOutSeenNotifications(true); mController.getNotifStackController().setNotifStats(NotifStats.getEmpty()); verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true); verify(mNotificationStackScrollLayout).updateFooter(); @@ -703,7 +704,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mUiEventLogger, mRemoteInputManager, mVisibilityLocationProviderDelegator, - mSeenNotificationsProvider, + mNotificationListInteractor, mShadeController, mJankMonitor, mStackLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 3a820e8087a8..a2be8b0e0be2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -65,12 +65,12 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.ExpandHelper; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.EmptyShadeView; @@ -81,9 +81,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -163,6 +163,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM); mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS); mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR); + mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION); // Inject dependencies before initializing the layout mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags); @@ -247,25 +248,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testUpdateStackHeight_withDozeAmount_whenDozeChanging() { - final float dozeAmount = 0.5f; - mAmbientState.setDozeAmount(dozeAmount); - - final float endHeight = 8f; - final float expansionFraction = 1f; - float expected = MathUtils.lerp( - endHeight * StackScrollAlgorithm.START_FRACTION, - endHeight, dozeAmount); - - mStackScroller.updateStackHeight(endHeight, expansionFraction); - assertThat(mAmbientState.getStackHeight()).isEqualTo(expected); - } - - @Test public void testUpdateStackHeight_withExpansionAmount_whenDozeNotChanging() { - final float dozeAmount = 1f; - mAmbientState.setDozeAmount(dozeAmount); - final float endHeight = 8f; final float expansionFraction = 0.5f; final float expected = MathUtils.lerp( 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 a52466d2fa41..49906dca0344 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 @@ -5,18 +5,18 @@ import android.content.pm.PackageManager import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation.getContentAlpha import com.android.systemui.dump.DumpManager +import com.android.systemui.res.R import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.EmptyShadeView import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView -import com.android.systemui.statusbar.notification.row.FooterView -import com.android.systemui.statusbar.notification.row.FooterView.FooterViewState import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.util.mockito.mock import com.google.common.truth.Expect @@ -241,6 +241,8 @@ class StackScrollAlgorithmTest : SysuiTestCase() { fun resetViewStates_isOnKeyguard_viewBecomesTransparent() { ambientState.setStatusBarState(StatusBarState.KEYGUARD) ambientState.hideAmount = 0.25f + whenever(notificationRow.isHeadsUpState).thenReturn(true) + stackScrollAlgorithm.initView(context) stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) @@ -283,17 +285,20 @@ class StackScrollAlgorithmTest : SysuiTestCase() { val row2 = mockExpandableNotificationRow() hostView.addView(row2) + whenever(row1.isHeadsUpState).thenReturn(true) + whenever(row2.isHeadsUpState).thenReturn(false) + ambientState.setStatusBarState(StatusBarState.KEYGUARD) ambientState.hideAmount = 0.25f + ambientState.dozeAmount = 0.33f notificationShelf.viewState.hidden = true ambientState.shelf = notificationShelf stackScrollAlgorithm.initView(context) stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) - val expected = 1f - ambientState.hideAmount - assertThat(row1.viewState.alpha).isEqualTo(expected) - assertThat(row2.viewState.alpha).isEqualTo(expected) + assertThat(row1.viewState.alpha).isEqualTo(1f - ambientState.hideAmount) + assertThat(row2.viewState.alpha).isEqualTo(1f - ambientState.dozeAmount) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index e254dd085b2e..ac11ff29b83a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -19,36 +19,35 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R +import com.android.SysUITestModule +import com.android.TestMocksModule import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.SharedNotificationContainerPosition import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.res.R import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.user.domain.interactor.UserInteractor +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -60,29 +59,27 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class SharedNotificationContainerViewModelTest : SysuiTestCase() { - private val utils = SceneTestUtils(this) - - private val testScope = utils.testScope - - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val userSetupRepository = FakeUserSetupRepository() - private val shadeRepository = FakeShadeRepository() - private val keyguardRepository = FakeKeyguardRepository() - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - - private lateinit var configurationRepository: FakeConfigurationRepository - private lateinit var sharedNotificationContainerInteractor: - SharedNotificationContainerInteractor - private lateinit var underTest: SharedNotificationContainerViewModel - private lateinit var keyguardInteractor: KeyguardInteractor - private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor - private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository - private lateinit var shadeInteractor: ShadeInteractor + + private lateinit var testComponent: TestComponent + + private val shadeRepository + get() = testComponent.shadeRepository + private val keyguardRepository + get() = testComponent.keyguardRepository + private val configurationRepository + get() = testComponent.configurationRepository + private val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor + get() = testComponent.sharedNotificationContainerInteractor + private val underTest: SharedNotificationContainerViewModel + get() = testComponent.underTest + private val keyguardInteractor: KeyguardInteractor + get() = testComponent.keyguardInteractor + private val keyguardTransitionRepository + get() = testComponent.keyguardTransitionRepository + private val testScope + get() = testComponent.testScope @Mock private lateinit var notificationStackSizeCalculator: NotificationStackSizeCalculator - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - @Mock private lateinit var userInteractor: UserInteractor @Mock private lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController @@ -94,43 +91,21 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { whenever(notificationStackScrollLayoutController.getView()).thenReturn(mock()) whenever(notificationStackScrollLayoutController.getShelfHeight()).thenReturn(0) - configurationRepository = FakeConfigurationRepository() - KeyguardTransitionInteractorFactory.create( - scope = testScope.backgroundScope, - ) - .also { - keyguardInteractor = it.keyguardInteractor - keyguardTransitionInteractor = it.keyguardTransitionInteractor - keyguardTransitionRepository = it.repository - } - sharedNotificationContainerInteractor = - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - shadeInteractor = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository, - deviceProvisionedController, - userInteractor, - sharedNotificationContainerInteractor, - shadeRepository, - ) - underTest = - SharedNotificationContainerViewModel( - sharedNotificationContainerInteractor, - keyguardInteractor, - keyguardTransitionInteractor, - notificationStackSizeCalculator, - notificationStackScrollLayoutController, - shadeInteractor - ) + testComponent = + DaggerSharedNotificationContainerViewModelTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FULL_SCREEN_USER_SWITCHER, true) + }, + mocks = + TestMocksModule( + notificationStackSizeCalculator = notificationStackSizeCalculator, + notificationStackScrollLayoutController = + notificationStackScrollLayoutController, + ) + ) } @Test @@ -404,4 +379,34 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { ) ) } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: SharedNotificationContainerViewModel + + val configurationRepository: FakeConfigurationRepository + val keyguardRepository: FakeKeyguardRepository + val keyguardInteractor: KeyguardInteractor + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + val shadeRepository: FakeShadeRepository + val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + mocks: TestMocksModule, + ): TestComponent + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java index 416694b29f69..1d8a3461e546 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java @@ -50,15 +50,15 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dagger.NightDisplayListenerModule; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.ReduceBrightColorsController; -import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.external.CustomTile; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.DataSaverController; @@ -288,7 +288,7 @@ public class AutoTileManagerTest extends SysuiTestCase { inOrderSafety.verify(mSafetyController).removeCallback(any()); inOrderSafety.verify(mSafetyController).addCallback(any()); - SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); + UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); assertEquals(USER + 1, setting.getCurrentUser()); assertTrue(setting.isListening()); } @@ -342,7 +342,7 @@ public class AutoTileManagerTest extends SysuiTestCase { inOrderSafety.verify(mSafetyController).removeCallback(any()); inOrderSafety.verify(mSafetyController).addCallback(any()); - SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); + UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); assertEquals(USER + 1, setting.getCurrentUser()); assertFalse(setting.isListening()); } 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 a5d348404240..e7dad6a2908f 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 @@ -57,6 +57,7 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; @@ -85,7 +86,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { private final MetricsLogger mMetricsLogger = new FakeMetricsLogger(); @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private KeyguardStateController mKeyguardStateController; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private WakefulnessLifecycle mWakefulnessLifecycle; @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index e33fa22bfc0c..f18af61dd314 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -85,7 +85,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.TestScopeProvider; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -117,6 +116,7 @@ import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.brightness.BrightnessSliderController; @@ -219,7 +219,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private KeyguardIndicationController mKeyguardIndicationController; @Mock private NotificationStackScrollLayout mStackScroller; @Mock private NotificationStackScrollLayoutController mStackScrollerController; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private NotificationPanelViewController mNotificationPanelViewController; @Mock private ShadeLogger mShadeLogger; @Mock private NotificationPanelView mNotificationPanelView; @@ -336,6 +336,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false); // Set default value to avoid IllegalStateException. mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false); + mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR); // For the Shade to respond to Back gesture, we must enable the event routing mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true); // For the Shade to animate during the Back gesture, we must enable the animation flag. @@ -343,6 +344,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true); // Turn AOD on and toggle feature flag for jank fixes mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false); when(mDozeParameters.getAlwaysOn()).thenReturn(true); IThermalService thermalService = mock(IThermalService.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index ff6f40d539fc..593c587c0f51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import org.junit.Before; import org.junit.Test; @@ -72,7 +73,7 @@ public class DozeServiceHostTest extends SysuiTestCase { private DozeServiceHost mDozeServiceHost; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private ScrimController mScrimController; @Mock private DozeScrimController mDozeScrimController; @Mock private StatusBarStateControllerImpl mStatusBarStateController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index ec6286b66ed2..d84bb728a856 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -72,7 +73,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow; private NotificationEntry mEntry; private HeadsUpStatusBarView mHeadsUpStatusBarView; - private HeadsUpManagerPhone mHeadsUpManager; + private HeadsUpManager mHeadsUpManager; private View mOperatorNameView; private StatusBarStateController mStatusbarStateController; private PhoneStatusBarTransitions mPhoneStatusBarTransitions; @@ -93,7 +94,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mEntry = mRow.getEntry(); mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class), mock(TextView.class)); - mHeadsUpManager = mock(HeadsUpManagerPhone.class); + mHeadsUpManager = mock(HeadsUpManager.class); mOperatorNameView = new View(mContext); mStatusbarStateController = mock(StatusBarStateController.class); mPhoneStatusBarTransitions = mock(PhoneStatusBarTransitions.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 1bc522d72213..cda2a74609bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -32,8 +32,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; -import com.android.systemui.res.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.collection.provider.VisualSta import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import org.junit.After; @@ -71,7 +72,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper; @Mock private ShadeExpansionStateManager mShadeExpansionStateManager; @Mock private UiEventLogger mUiEventLogger; - private boolean mLivesPastNormalTime; private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone { TestableHeadsUpManagerPhone( @@ -149,7 +149,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testSnooze() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry entry = createEntry(/* id = */ 0); hmp.showNotification(entry); @@ -160,7 +160,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testSwipedOutNotification() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry entry = createEntry(/* id = */ 0); hmp.showNotification(entry); @@ -176,7 +176,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testCanRemoveImmediately_swipedOut() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry entry = createEntry(/* id = */ 0); hmp.showNotification(entry); @@ -189,7 +189,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Ignore("b/141538055") @Test public void testCanRemoveImmediately_notTopEntry() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry earlierEntry = createEntry(/* id = */ 0); final NotificationEntry laterEntry = createEntry(/* id = */ 1); laterEntry.setRow(mRow); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index bac857910329..45e9224aa253 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -33,6 +34,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; + import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher; import android.service.trust.TrustAgentService; @@ -173,8 +175,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true); mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false); - mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, true); mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false); + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false); when(mNotificationShadeWindowController.getWindowRootView()) .thenReturn(mNotificationShadeWindowView); @@ -761,6 +763,30 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() { + mStatusBarKeyguardViewManager.addCallback(mCallback); + + // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + + // THEN the touch is not acted upon + verify(mCallback, never()).onTouch(any()); + } + + @Test + public void onInterceptTouch_alternateBouncerViewFlagEnabled() { + // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + + // THEN the touch is not intercepted + assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( + MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + )); + } + + @Test public void handleDispatchTouchEvent_alternateBouncerNotVisible() { mStatusBarKeyguardViewManager.addCallback(mCallback); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 8013e5eef44b..beac995c893b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -92,6 +92,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -218,7 +219,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mScreenOffAnimationController, mStatusBarStateController).getPowerInteractor(); - HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class); + HeadsUpManager headsUpManager = mock(HeadsUpManager.class); NotificationLaunchAnimatorControllerProvider notificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( new NotificationExpansionRepository(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 233f407813b2..ee4f2089c05c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -15,6 +15,7 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -59,6 +60,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -106,7 +108,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mContext, shadeViewController, mock(QuickSettingsController.class), - mock(HeadsUpManagerPhone.class), + mock(HeadsUpManager.class), notificationShadeWindowView, mock(ActivityStarter.class), stackScrollLayoutController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt index d35ce76d7a9a..8ecf6f82806a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt @@ -18,12 +18,11 @@ package com.android.systemui.statusbar.pipeline.airplane.data.repository import android.os.Handler import android.os.Looper -import android.os.UserHandle import android.provider.Settings.Global import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.table.TableLogBuffer -import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.FakeGlobalSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -48,15 +47,14 @@ class AirplaneModeRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var logger: TableLogBuffer private lateinit var bgHandler: Handler private lateinit var scope: CoroutineScope - private lateinit var settings: FakeSettings + private lateinit var settings: FakeGlobalSettings @Before fun setUp() { MockitoAnnotations.initMocks(this) bgHandler = Handler(Looper.getMainLooper()) scope = CoroutineScope(IMMEDIATE) - settings = FakeSettings() - settings.userId = UserHandle.USER_ALL + settings = FakeGlobalSettings() underTest = AirplaneModeRepositoryImpl( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt index dbaa29bb3688..d06a6e26b4ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt @@ -20,7 +20,6 @@ import android.net.ConnectivityManager import android.net.wifi.WifiManager 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.demomode.DemoMode import com.android.systemui.demomode.DemoModeController @@ -53,7 +52,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiRepositorySwitcherTest : SysuiTestCase() { private lateinit var underTest: WifiRepositorySwitcher diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt index 206ac1d37074..ce00250467f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod 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.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel @@ -28,7 +27,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DisabledWifiRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index c2e75aa85fcb..cf20ba87e8c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -35,7 +35,6 @@ import android.net.wifi.WifiManager.UNKNOWN_SSID import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID 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.coroutines.collectLastValue import com.android.systemui.log.table.TableLogBuffer @@ -73,7 +72,6 @@ import org.mockito.MockitoAnnotations @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiRepositoryImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt index afab6230df5b..c2f56654e00a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt @@ -105,7 +105,7 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() { fun setUp() { featureFlags.set(Flags.INSTANT_TETHER, false) featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, false) - whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor))) + whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor), any())) .thenReturn(wifiPickerTracker) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt index 1db80651bf9b..7fbbfc77300e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor import android.net.wifi.WifiManager 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.coroutines.collectLastValue import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot @@ -43,7 +42,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiInteractorImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt index 49a2648a7cac..2d1a27f1b133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription import com.android.systemui.coroutines.collectLastValue @@ -53,7 +52,6 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt index 6094135c6364..361fa5b169d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.settings.FakeGlobalSettings import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.wrapper.BuildInfo @@ -67,19 +68,22 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { private lateinit var mainExecutor: FakeExecutor private lateinit var testableLooper: TestableLooper - private lateinit var settings: FakeSettings + private lateinit var secureSettings: FakeSettings + private lateinit var globalSettings: FakeGlobalSettings + @Before fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) mainExecutor = FakeExecutor(FakeSystemClock()) - settings = FakeSettings() + secureSettings = FakeSettings() + globalSettings = FakeGlobalSettings() `when`(userTracker.userId).thenReturn(START_USER) whenever(buildInfo.isDebuggable).thenReturn(false) controller = DeviceProvisionedControllerImpl( - settings, - settings, + secureSettings, + globalSettings, userTracker, dumpManager, buildInfo, @@ -108,7 +112,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { @Test fun testProvisionedWhenCreated() { - settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) + globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) init() assertThat(controller.isDeviceProvisioned).isTrue() @@ -116,7 +120,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { @Test fun testFrpActiveWhenCreated() { - settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1) + globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1) init() assertThat(controller.isFrpActive).isTrue() @@ -124,7 +128,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { @Test fun testUserSetupWhenCreated() { - settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) + secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) init() assertThat(controller.isUserSetup(START_USER)) @@ -134,7 +138,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { fun testDeviceProvisionedChange() { init() - settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) + globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) testableLooper.processAllMessages() // background observer assertThat(controller.isDeviceProvisioned).isTrue() @@ -144,7 +148,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { fun testFrpActiveChange() { init() - settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1) + globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1) testableLooper.processAllMessages() // background observer assertThat(controller.isFrpActive).isTrue() @@ -154,7 +158,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { fun testUserSetupChange() { init() - settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) + secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) testableLooper.processAllMessages() // background observer assertThat(controller.isUserSetup(START_USER)).isTrue() @@ -165,7 +169,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { init() val otherUser = 10 - settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser) + secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser) testableLooper.processAllMessages() // background observer assertThat(controller.isUserSetup(START_USER)).isFalse() @@ -175,7 +179,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { @Test fun testCurrentUserSetup() { val otherUser = 10 - settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser) + secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser) init() assertThat(controller.isCurrentUserSetup).isFalse() @@ -219,7 +223,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { init() controller.addCallback(listener) - settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) + secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) testableLooper.processAllMessages() mainExecutor.runAllReady() @@ -234,7 +238,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { init() controller.addCallback(listener) - settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) + globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) testableLooper.processAllMessages() mainExecutor.runAllReady() @@ -249,7 +253,7 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { init() controller.addCallback(listener) - settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1) + globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1) testableLooper.processAllMessages() mainExecutor.runAllReady() @@ -266,9 +270,9 @@ class DeviceProvisionedControllerImplTest : SysuiTestCase() { controller.removeCallback(listener) switchUser(10) - settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) - settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) - settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1) + secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER) + globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1) + globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1) testableLooper.processAllMessages() mainExecutor.runAllReady() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index 64ebcd9e3abf..4f3f56423eb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -41,10 +41,13 @@ import android.app.PendingIntent; import android.app.Person; import android.content.Context; import android.content.Intent; +import android.graphics.Region; import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; @@ -73,7 +76,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer())); @Mock private AccessibilityManagerWrapper mAccessibilityMgr; - private final class TestableHeadsUpManager extends HeadsUpManager { + private final class TestableHeadsUpManager extends BaseHeadsUpManager { TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger, Handler handler, @@ -85,9 +88,78 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME; } + + // The following are only implemented by HeadsUpManagerPhone. If you need them, use that. + @Override + public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void addSwipedOutNotification(@NonNull String key) { + throw new UnsupportedOperationException(); + } + + @Override + public void extendHeadsUp() { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public Region getTouchableRegion() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isHeadsUpGoingAway() { + throw new UnsupportedOperationException(); + } + + @Override + public void onExpandingFinished() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeNotification(@NonNull String key, boolean releaseImmediately, + boolean animate) { + throw new UnsupportedOperationException(); + } + + @Override + public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) { + throw new UnsupportedOperationException(); + } + + @Override + public void setHeadsUpGoingAway(boolean headsUpGoingAway) { + throw new UnsupportedOperationException(); + } + + @Override + public void setRemoteInputActive(@NonNull NotificationEntry entry, + boolean remoteInputActive) { + throw new UnsupportedOperationException(); + } + + @Override + public void setTrackingHeadsUp(boolean tracking) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean shouldSwallowClick(@NonNull String key) { + throw new UnsupportedOperationException(); + } } - private HeadsUpManager createHeadsUpManager() { + private BaseHeadsUpManager createHeadsUpManager() { return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mAccessibilityMgr, mUiEventLoggerFake); } @@ -165,9 +237,10 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testHunRemovedLogging() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); - final HeadsUpManager.HeadsUpEntry headsUpEntry = mock(HeadsUpManager.HeadsUpEntry.class); + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = mock( + BaseHeadsUpManager.HeadsUpEntry.class); headsUpEntry.mEntry = notifEntry; hum.onAlertEntryRemoved(headsUpEntry); @@ -177,35 +250,37 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0); // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); - headsUpEntry.wasUnpinned = false; + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); + headsUpEntry.mWasUnpinned = false; assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry)); } @Test public void testShouldHeadsUpBecomePinned_wasUnpinned_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0); // Add notifEntry to ANM mAlertEntries map and make it unpinned hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); - headsUpEntry.wasUnpinned = true; + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); + headsUpEntry.mWasUnpinned = true; assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry)); } @Test public void testShouldHeadsUpBecomePinned_noFSI_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); assertFalse(hum.shouldHeadsUpBecomePinned(entry)); @@ -214,7 +289,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -228,7 +303,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_autoDismissesWithDefaultTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -242,7 +317,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -256,7 +331,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_sticky_neverAutoDismisses() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -278,7 +353,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_autoDismissesWithAccessibilityTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(true); @@ -292,7 +367,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(true); @@ -306,7 +381,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testRemoveNotification_beforeMinimumDisplayTime() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -329,7 +404,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testRemoveNotification_afterMinimumDisplayTime() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -366,7 +441,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testRemoveNotification_releaseImmediately() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); hum.showNotification(entry); @@ -382,14 +457,15 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_rowPinnedAndExpanded_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); when(mRow.isPinned()).thenReturn(true); notifEntry.setRow(mRow); hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); headsUpEntry.setExpanded(true); assertTrue(hum.isSticky(notifEntry.getKey())); @@ -397,20 +473,21 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_remoteInputActive_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); - headsUpEntry.remoteInputActive = true; + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); + headsUpEntry.mRemoteInputActive = true; assertTrue(hum.isSticky(notifEntry.getKey())); } @Test public void testIsSticky_hasFullScreenIntent_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0); hum.showNotification(notifEntry); @@ -421,7 +498,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_stickyForSomeTime_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); hum.showNotification(entry); @@ -432,21 +509,22 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); headsUpEntry.setExpanded(false); - headsUpEntry.remoteInputActive = false; + headsUpEntry.mRemoteInputActive = false; assertFalse(hum.isSticky(notifEntry.getKey())); } @Test public void testCompareTo_withNullEntries() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); hum.showNotification(alertEntry); @@ -458,7 +536,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testCompareTo_withNonAlertEntries() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag( "nae1").build(); @@ -474,9 +552,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); - final HeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry(); ongoingCall.setEntry(new NotificationEntryBuilder() .setSbn(createSbn(/* id = */ 0, new Notification.Builder(mContext, "") @@ -484,9 +562,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { .setOngoing(true))) .build()); - final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); activeRemoteInput.setEntry(createEntry(/* id = */ 1)); - activeRemoteInput.remoteInputActive = true; + activeRemoteInput.mRemoteInputActive = true; assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0); assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0); @@ -494,9 +572,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); - final HeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry(); final Person person = new Person.Builder().setName("person").build(); final PendingIntent intent = mock(PendingIntent.class); incomingCall.setEntry(new NotificationEntryBuilder() @@ -506,9 +584,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { .forIncomingCall(person, intent, intent)))) .build()); - final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); activeRemoteInput.setEntry(createEntry(/* id = */ 1)); - activeRemoteInput.remoteInputActive = true; + activeRemoteInput.mRemoteInputActive = true; assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0); assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0); @@ -516,10 +594,10 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testPinEntry_logsPeek() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); // Needs full screen intent in order to be pinned - final HeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(); entryToPin.setEntry(createFullScreenIntentEntry(/* id = */ 0)); // Note: the standard way to show a notification would be calling showNotification rather @@ -530,13 +608,13 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { hum.onAlertEntryAdded(entryToPin); assertEquals(1, mUiEventLoggerFake.numLogs()); - assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), + assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(0)); } @Test public void testSetUserActionMayIndirectlyRemove() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); hum.showNotification(notifEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java index 66c5aaa3ed07..6825f650c421 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java @@ -38,7 +38,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.ZenModeController.Callback; -import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.FakeGlobalSettings; import org.junit.Before; import org.junit.Test; @@ -67,7 +67,7 @@ public class ZenModeControllerImplTest extends SysuiTestCase { UserTracker mUserTracker; private ZenModeControllerImpl mController; - private final FakeSettings mGlobalSettings = new FakeSettings(); + private final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings(); @Before public void setUp() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt new file mode 100644 index 000000000000..12694ae998c1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt @@ -0,0 +1,108 @@ +/* + * 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. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.policy.data.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +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.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { + + @Mock lateinit var deviceProvisionedController: DeviceProvisionedController + + lateinit var underTest: DeviceProvisioningRepositoryImpl + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = + DeviceProvisioningRepositoryImpl( + deviceProvisionedController, + ) + } + + @Test + fun isDeviceProvisioned_reflectsCurrentControllerState() = runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned) + assertThat(deviceProvisioned).isTrue() + } + + @Test + fun isDeviceProvisioned_updatesWhenControllerStateChanges_toTrue() = runTest { + val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned) + runCurrent() + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onDeviceProvisionedChanged() + assertThat(deviceProvisioned).isTrue() + } + + @Test + fun isDeviceProvisioned_updatesWhenControllerStateChanges_toFalse() = runTest { + val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned) + runCurrent() + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onDeviceProvisionedChanged() + assertThat(deviceProvisioned).isFalse() + } + + @Test + fun isFrpActive_reflectsCurrentControllerState() = runTest { + whenever(deviceProvisionedController.isFrpActive).thenReturn(true) + val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive) + assertThat(frpActive).isTrue() + } + + @Test + fun isFrpActive_updatesWhenControllerStateChanges_toTrue() = runTest { + val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive) + runCurrent() + whenever(deviceProvisionedController.isFrpActive).thenReturn(true) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onFrpActiveChanged() + assertThat(frpActive).isTrue() + } + + @Test + fun isFrpActive_updatesWhenControllerStateChanges_toFalse() = runTest { + val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive) + runCurrent() + whenever(deviceProvisionedController.isFrpActive).thenReturn(false) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onFrpActiveChanged() + assertThat(frpActive).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt index e249cece5a1e..0d78ae9c7b11 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt @@ -29,7 +29,7 @@ import com.android.systemui.settings.FakeUserTracker import com.android.systemui.user.data.model.SelectedUserModel import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.model.UserSwitcherSettingsModel -import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.FakeGlobalSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -56,14 +56,14 @@ class UserRepositoryImplTest : SysuiTestCase() { private lateinit var underTest: UserRepositoryImpl - private lateinit var globalSettings: FakeSettings + private lateinit var globalSettings: FakeGlobalSettings private lateinit var tracker: FakeUserTracker @Before fun setUp() { MockitoAnnotations.initMocks(this) - globalSettings = FakeSettings() + globalSettings = FakeGlobalSettings() tracker = FakeUserTracker() } @@ -282,20 +282,17 @@ class UserRepositoryImplTest : SysuiTestCase() { com.android.internal.R.bool.config_expandLockScreenUserSwitcher, true, ) - globalSettings.putIntForUser( + globalSettings.putInt( UserRepositoryImpl.SETTING_SIMPLE_USER_SWITCHER, if (isSimpleUserSwitcher) 1 else 0, - UserHandle.USER_SYSTEM, ) - globalSettings.putIntForUser( + globalSettings.putInt( Settings.Global.ADD_USERS_WHEN_LOCKED, if (isAddUsersFromLockscreen) 1 else 0, - UserHandle.USER_SYSTEM, ) - globalSettings.putIntForUser( + globalSettings.putInt( Settings.Global.USER_SWITCHER_ENABLED, if (isUserSwitcherEnabled) 1 else 0, - UserHandle.USER_SYSTEM, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt index af941d03f191..c56266dde752 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt @@ -155,6 +155,9 @@ class UserInteractorTest : SysuiTestCase() { @Test fun createUserInteractor_nonProcessUser_startsSecondaryService() { + val userId = Process.myUserHandle().identifier + 1 + whenever(manager.aliveUsers).thenReturn(listOf(createUserInfo(userId, "abc"))) + createUserInteractor(false /* startAsProcessUser */) verify(spyContext).startServiceAsUser(any(), any()) } @@ -655,9 +658,10 @@ class UserInteractorTest : SysuiTestCase() { @Test fun userSwitchedBroadcast() { - createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) + whenever(manager.aliveUsers).thenReturn(userInfos) + createUserInteractor() userRepository.setUserInfos(userInfos) userRepository.setSelectedUserInfo(userInfos[0]) userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) @@ -728,6 +732,26 @@ class UserInteractorTest : SysuiTestCase() { } @Test + fun localeChanged_refreshUsers() { + createUserInteractor() + testScope.runTest { + val userInfos = createUserInfos(count = 2, includeGuest = false) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + runCurrent() + val refreshUsersCallCount = userRepository.refreshUsersCallCount + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + spyContext, + Intent(Intent.ACTION_LOCALE_CHANGED) + ) + runCurrent() + + assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1) + } + } + + @Test fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() { createUserInteractor() testScope.runTest { @@ -985,6 +1009,13 @@ class UserInteractorTest : SysuiTestCase() { } } + @Test + fun initWithNoAliveUsers() { + whenever(manager.aliveUsers).thenReturn(listOf()) + createUserInteractor() + verify(spyContext, never()).startServiceAsUser(any(), any()) + } + private fun assertUsers( models: List<UserModel>?, count: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index 6932f5ed4b30..c236b12d723f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Text +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.FakeKeyguardRepository @@ -347,6 +348,24 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test + fun isFinishRequested_finishesWhenUserButtonIsClicked() = + testScope.runTest { + setUsers(count = 2) + val isFinishRequested = mutableListOf<Boolean>() + val job = + launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } + + val userViewModels = collectLastValue(underTest.users) + assertThat(isFinishRequested.last()).isFalse() + + userViewModels.invoke()?.firstOrNull()?.onClicked?.invoke() + + assertThat(isFinishRequested.last()).isTrue() + + job.cancel() + } + + @Test fun guestSelected_nameIsExitGuest() = testScope.runTest { val userInfos = diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt new file mode 100644 index 000000000000..aaf8d0761dce --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt @@ -0,0 +1,98 @@ +/* + * 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.util.ui + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class AnimatedValueTest : SysuiTestCase() { + + @Test + fun animatableEvent_updatesValue() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = false)) + + assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false)) + } + + @Test + fun animatableEvent_startAnimation() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = true)) + + assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = true)) + } + + @Test + fun animatableEvent_startAnimation_alreadyAnimating() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = true)) + events.emit(AnimatableEvent(value = 2, startAnimating = true)) + + assertThat(value).isEqualTo(AnimatedValue(value = 2, isAnimating = true)) + } + + @Test + fun animatedValue_stopAnimating() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val stopEvent = MutableSharedFlow<Unit>() + val values = events.toAnimatedValueFlow(completionEvents = stopEvent) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = true)) + stopEvent.emit(Unit) + + assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false)) + } + + @Test + fun animatedValue_stopAnimating_notAnimating() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val stopEvent = MutableSharedFlow<Unit>() + val values = events.toAnimatedValueFlow(completionEvents = stopEvent) + values.launchIn(backgroundScope) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = false)) + + assertThat(stopEvent.subscriptionCount.value).isEqualTo(0) + } +} 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 28fc5db150b7..b8f747b8e961 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -42,6 +42,7 @@ import android.app.KeyguardManager; import android.content.res.Configuration; import android.media.AudioManager; import android.os.SystemClock; +import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Log; @@ -70,6 +71,10 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.FakeConfigurationController; +import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.SecureSettings; + +import dagger.Lazy; import org.junit.After; import org.junit.Before; @@ -122,6 +127,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Mock CsdWarningDialog mCsdWarningDialog; @Mock DevicePostureController mPostureController; + @Mock + private Lazy<SecureSettings> mLazySecureSettings; private final CsdWarningDialog.Factory mCsdWarningDialogFactory = new CsdWarningDialog.Factory() { @@ -133,6 +140,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { private FakeFeatureFlags mFeatureFlags; private int mLongestHideShowAnimationDuration = 250; + private FakeSettings mSecureSettings; @Rule public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); @@ -162,6 +170,10 @@ public class VolumeDialogImplTest extends SysuiTestCase { mFeatureFlags = new FakeFeatureFlags(); + mSecureSettings = new FakeSettings(); + + when(mLazySecureSettings.get()).thenReturn(mSecureSettings); + mDialog = new VolumeDialogImpl( getContext(), mVolumeDialogController, @@ -177,7 +189,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { mPostureController, mTestableLooper.getLooper(), mDumpManager, - mFeatureFlags); + mFeatureFlags, + mLazySecureSettings); mDialog.init(0, null); State state = createShellState(); mDialog.onStateChangedH(state); @@ -242,6 +255,17 @@ public class VolumeDialogImplTest extends SysuiTestCase { } @Test + public void testSetTimeoutValue_ComputeTimeout() { + mSecureSettings.putInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, 7000); + Mockito.reset(mAccessibilityMgr); + mDialog.init(0, null); + mDialog.rescheduleTimeoutH(); + verify(mAccessibilityMgr).getRecommendedTimeoutMillis( + 7000, + AccessibilityManager.FLAG_CONTENT_CONTROLS); + } + + @Test public void testComputeTimeout_tooltip() { Mockito.reset(mAccessibilityMgr); mDialog.showCaptionsTooltip(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 409ba4801d0d..c8327029026d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -86,20 +86,36 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; +import com.android.keyguard.KeyguardSecurityModel; import com.android.launcher3.icons.BubbleIconFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardViewMediator; +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.domain.interactor.FromLockscreenTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.scene.FakeWindowRootViewComponent; import com.android.systemui.scene.SceneTestUtils; +import com.android.systemui.scene.data.repository.SceneContainerRepository; +import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -139,6 +155,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.user.domain.interactor.UserInteractor; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; @@ -329,6 +346,8 @@ public class BubblesTest extends SysuiTestCase { private UserHandle mUser0; private FakeBubbleProperties mBubbleProperties; + private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor; + private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor; @Before public void setUp() throws Exception { @@ -350,21 +369,94 @@ public class BubblesTest extends SysuiTestCase { when(mNotificationShadeWindowView.getViewTreeObserver()) .thenReturn(mock(ViewTreeObserver.class)); - mShadeInteractor = new ShadeInteractor( + + FakeDeviceProvisioningRepository deviceProvisioningRepository = + new FakeDeviceProvisioningRepository(); + deviceProvisioningRepository.setDeviceProvisioned(true); + FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository(); + FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic(); + FakeShadeRepository shadeRepository = new FakeShadeRepository(); + FakePowerRepository powerRepository = new FakePowerRepository(); + FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); + + PowerInteractor powerInteractor = new PowerInteractor( + powerRepository, + new FalsingCollectorFake(), + mock(ScreenOffAnimationController.class), + mStatusBarStateController); + + SceneInteractor sceneInteractor = new SceneInteractor( mTestScope.getBackgroundScope(), - new FakeDisableFlagsRepository(), - new FakeSceneContainerFlags(), - mUtils::sceneInteractor, - new FakeKeyguardRepository(), - new FakeUserSetupRepository(), - mock(DeviceProvisionedController.class), - mock(UserInteractor.class), - new SharedNotificationContainerInteractor( - new FakeConfigurationRepository(), - mContext, - new ResourcesSplitShadeStateController()), - new FakeShadeRepository() - ); + new SceneContainerRepository( + mTestScope.getBackgroundScope(), + mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())), + powerRepository, + mock(SceneLogger.class)); + + FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); + KeyguardInteractor keyguardInteractor = new KeyguardInteractor( + keyguardRepository, + new FakeCommandQueue(), + powerInteractor, + featureFlags, + sceneContainerFlags, + new FakeDeviceEntryRepository(), + new FakeKeyguardBouncerRepository(), + configurationRepository, + shadeRepository, + () -> sceneInteractor); + + FakeKeyguardTransitionRepository keyguardTransitionRepository = + new FakeKeyguardTransitionRepository(); + + KeyguardTransitionInteractor keyguardTransitionInteractor = + new KeyguardTransitionInteractor( + mTestScope.getBackgroundScope(), + keyguardTransitionRepository, + () -> keyguardInteractor, + () -> mFromLockscreenTransitionInteractor, + () -> mFromPrimaryBouncerTransitionInteractor); + + mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + shadeRepository, + powerInteractor); + + mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mock(KeyguardSecurityModel.class), + powerInteractor); + + ResourcesSplitShadeStateController splitShadeStateController = + new ResourcesSplitShadeStateController(); + + mShadeInteractor = + new ShadeInteractor( + mTestScope.getBackgroundScope(), + deviceProvisioningRepository, + new FakeDisableFlagsRepository(), + mDozeParameters, + sceneContainerFlags, + () -> sceneInteractor, + keyguardRepository, + keyguardTransitionInteractor, + powerInteractor, + new FakeUserSetupRepository(), + mock(UserInteractor.class), + new SharedNotificationContainerInteractor( + configurationRepository, + mContext, + splitShadeStateController), + new FakeShadeRepository() + ); mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl( mContext, diff --git a/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt index 43a26f34ef2e..ca5e1d075856 100644 --- a/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt +++ b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt @@ -41,7 +41,7 @@ class PlatformAnimatorIsolationRule : TestRule { private fun onError() = exceptionDeferrer.fail( "Test's animations are not isolated! " + - "Did you forget to add an AnimatorTestRule to your test class?" + "Did you forget to add an AnimatorTestRule as a @Rule?" ) fun throwDeferred() = exceptionDeferrer.throwDeferred() diff --git a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt index 7a97029ca6b0..95335a639ea2 100644 --- a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt +++ b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt @@ -37,7 +37,7 @@ class AndroidXAnimatorIsolationRule : TestRule { private fun onError() = exceptionDeferrer.fail( "Test's animations are not isolated! " + - "Did you forget to add an AnimatorTestRule to your test class?" + "Did you forget to add an AnimatorTestRule as a @Rule?" ) fun throwDeferred() = exceptionDeferrer.throwDeferred() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt index 0e594964bb30..dc5fd953239d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt @@ -15,21 +15,29 @@ */ package com.android.systemui +import com.android.systemui.classifier.FakeClassifierModule import com.android.systemui.data.FakeSystemUiDataLayerModule import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.log.FakeUiEventLoggerModule import com.android.systemui.scene.FakeSceneModule import com.android.systemui.settings.FakeSettingsModule +import com.android.systemui.statusbar.policy.FakeConfigurationControllerModule +import com.android.systemui.statusbar.policy.FakeSplitShadeStateControllerModule import com.android.systemui.util.concurrency.FakeExecutorModule +import com.android.systemui.util.time.FakeSystemClockModule import dagger.Module @Module( includes = [ + FakeClassifierModule::class, + FakeConfigurationControllerModule::class, FakeExecutorModule::class, FakeFeatureFlagsClassicModule::class, - FakeSettingsModule::class, FakeSceneModule::class, + FakeSettingsModule::class, + FakeSplitShadeStateControllerModule::class, + FakeSystemClockModule::class, FakeSystemUiDataLayerModule::class, FakeUiEventLoggerModule::class, ] diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index d6632a3c5ea7..f7e0120d3843 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -15,8 +15,6 @@ */ package com.android.systemui; -import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -37,19 +35,15 @@ import androidx.core.animation.AndroidXAnimatorIsolationRule; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.FakeBroadcastDispatcher; import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger; import com.android.systemui.dump.DumpManager; import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.phone.SystemUIDialogManager; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; -import org.junit.ClassRule; import org.junit.Rule; import org.mockito.Mockito; @@ -69,8 +63,8 @@ public abstract class SysuiTestCase { private Handler mHandler; // set the lowest order so it's the outermost rule - @ClassRule(order = Integer.MIN_VALUE) - public static AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule = + @Rule(order = Integer.MIN_VALUE) + public AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule = new AndroidXAnimatorIsolationRule(); @Rule @@ -94,10 +88,7 @@ public abstract class SysuiTestCase { if (isRobolectricTest()) { mContext = mContext.createDefaultDisplayContext(); } - SystemUIInitializer initializer = new SystemUIInitializerImpl(mContext); - initializer.init(true); - mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency()); - Dependency.setInstance(mDependency); + mDependency = SysuiTestDependencyKt.installSysuiTestDependency(mContext); mFakeBroadcastDispatcher = new FakeBroadcastDispatcher( mContext, mContext.getMainExecutor(), @@ -124,13 +115,6 @@ public abstract class SysuiTestCase { // reference and are never sent to the Context. This will also prevent a real // BroadcastDispatcher from actually registering receivers. mDependency.injectTestDependency(BroadcastDispatcher.class, mFakeBroadcastDispatcher); - mDependency.injectMockDependency(KeyguardUpdateMonitor.class); - - // Make sure that all tests on any SystemUIDialog does not crash because this dependency - // is missing (constructing the actual one would throw). - // TODO(b/219008720): Remove this. - mDependency.injectMockDependency(SystemUIDialogManager.class); - mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator()); } protected boolean shouldFailOnLeakedReceiver() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt new file mode 100644 index 000000000000..c791f4f70920 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt @@ -0,0 +1,26 @@ +package com.android.systemui + +import android.annotation.SuppressLint +import android.content.Context +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.animation.fakeDialogLaunchAnimator +import com.android.systemui.statusbar.phone.SystemUIDialogManager + +@SuppressLint("VisibleForTests") +fun installSysuiTestDependency(context: Context): TestableDependency { + val initializer: SystemUIInitializer = SystemUIInitializerImpl(context) + initializer.init(true) + + val dependency = TestableDependency(initializer.sysUIComponent.createDependency()) + Dependency.setInstance(dependency) + + dependency.injectMockDependency(KeyguardUpdateMonitor::class.java) + + // Make sure that all tests on any SystemUIDialog does not crash because this dependency + // is missing (constructing the actual one would throw). + // TODO(b/219008720): Remove this. + dependency.injectMockDependency(SystemUIDialogManager::class.java) + dependency.injectTestDependency(DialogLaunchAnimator::class.java, fakeDialogLaunchAnimator()) + return dependency +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt new file mode 100644 index 000000000000..8fa6695cab99 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.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.authentication.data + +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepositoryModule +import dagger.Module + +@Module(includes = [FakeAuthenticationRepositoryModule::class]) +object FakeAuthenticationDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index 4fc3e3f66e6d..ddfe79aedce6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -24,10 +24,17 @@ import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import dagger.Binds +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.currentTime class FakeAuthenticationRepository( private val deviceEntryRepository: FakeDeviceEntryRepository, @@ -201,3 +208,19 @@ class FakeAuthenticationRepository( } } } + +@OptIn(ExperimentalCoroutinesApi::class) +@Module(includes = [FakeAuthenticationRepositoryModule.Bindings::class]) +object FakeAuthenticationRepositoryModule { + @Provides + @SysUISingleton + fun provideFake( + deviceEntryRepository: FakeDeviceEntryRepository, + scope: TestScope, + ) = FakeAuthenticationRepository(deviceEntryRepository, currentTime = { scope.currentTime }) + + @Module + interface Bindings { + @Binds fun bindFake(fake: FakeAuthenticationRepository): AuthenticationRepository + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt new file mode 100644 index 000000000000..23bad39a262e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt @@ -0,0 +1,21 @@ +/* + * 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.classifier + +import dagger.Module + +@Module(includes = [FakeFalsingCollectorModule::class, FakeFalsingManagerModule::class]) +object FakeClassifierModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt new file mode 100644 index 000000000000..92acc9465bb0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt @@ -0,0 +1,25 @@ +/* + * 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.classifier + +import dagger.Binds +import dagger.Module + +@Module +interface FakeFalsingCollectorModule { + @Binds @FalsingCollectorActual fun bindFake(fake: FalsingCollectorFake): FalsingCollector + @Binds fun bindFakeLegacy(fake: FalsingCollectorFake): FalsingCollector +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt new file mode 100644 index 000000000000..554fc75ec1db --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt @@ -0,0 +1,25 @@ +/* + * 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.classifier + +import com.android.systemui.plugins.FalsingManager +import dagger.Binds +import dagger.Module + +@Module +interface FakeFalsingManagerModule { + @Binds fun bindFake(fake: FalsingManagerFake): FalsingManager +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java index d47e88fc9385..5038285aef00 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -21,15 +21,19 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.net.Uri; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.FalsingManager; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + /** * Simple Fake for testing where {@link FalsingManager} is required. */ +@SysUISingleton public class FalsingManagerFake implements FalsingManager { private boolean mIsFalseTouch; private boolean mIsSimpleTap; @@ -46,6 +50,10 @@ public class FalsingManagerFake implements FalsingManager { private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); private final List<FalsingTapListener> mTapListeners = new ArrayList<>(); + @Inject + public FalsingManagerFake() { + } + @Override public void onSuccessfulUnlock() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalTutorialRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalTutorialRepository.kt new file mode 100644 index 000000000000..902e8521acd1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalTutorialRepository.kt @@ -0,0 +1,19 @@ +package com.android.systemui.communal.data.repository + +import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED +import android.provider.Settings.Secure.HubModeTutorialState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +/** Fake implementation of [CommunalTutorialRepository] */ +class FakeCommunalTutorialRepository() : CommunalTutorialRepository { + private val _tutorialSettingState = MutableStateFlow(HUB_MODE_TUTORIAL_NOT_STARTED) + override val tutorialSettingState: StateFlow<Int> = _tutorialSettingState + override suspend fun setTutorialState(@HubModeTutorialState state: Int) { + setTutorialSettingState(state) + } + + fun setTutorialSettingState(@HubModeTutorialState state: Int) { + _tutorialSettingState.value = state + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt index f866932309bb..cffbf0271c29 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt @@ -15,8 +15,10 @@ */ package com.android.systemui.data +import com.android.systemui.authentication.data.FakeAuthenticationDataLayerModule import com.android.systemui.bouncer.data.repository.FakeBouncerDataLayerModule import com.android.systemui.common.ui.data.FakeCommonDataLayerModule +import com.android.systemui.deviceentry.data.FakeDeviceEntryDataLayerModule import com.android.systemui.keyguard.data.FakeKeyguardDataLayerModule import com.android.systemui.power.data.FakePowerDataLayerModule import com.android.systemui.shade.data.repository.FakeShadeDataLayerModule @@ -28,8 +30,10 @@ import dagger.Module @Module( includes = [ - FakeCommonDataLayerModule::class, + FakeAuthenticationDataLayerModule::class, FakeBouncerDataLayerModule::class, + FakeCommonDataLayerModule::class, + FakeDeviceEntryDataLayerModule::class, FakeKeyguardDataLayerModule::class, FakePowerDataLayerModule::class, FakeShadeDataLayerModule::class, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt new file mode 100644 index 000000000000..ef02bdd9c35c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * 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.deviceentry.data + +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule +import dagger.Module + +@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt new file mode 100644 index 000000000000..f4feee19df65 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt @@ -0,0 +1,20 @@ +/* + * 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.deviceentry.data.repository + +import dagger.Module + +@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt index 5e60a09e006b..f0293489cb87 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt @@ -1,22 +1,40 @@ +/* + * 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.deviceentry.data.repository +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** Fake implementation of [DeviceEntryRepository] */ -class FakeDeviceEntryRepository : DeviceEntryRepository { +@SysUISingleton +class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { private var isInsecureLockscreenEnabled = true - private var isBypassEnabled = false + + private val _isBypassEnabled = MutableStateFlow(false) + override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled private val _isUnlocked = MutableStateFlow(false) override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() - override fun isBypassEnabled(): Boolean { - return isBypassEnabled - } - override suspend fun isInsecureLockscreenEnabled(): Boolean { return isInsecureLockscreenEnabled } @@ -30,6 +48,11 @@ class FakeDeviceEntryRepository : DeviceEntryRepository { } fun setBypassEnabled(isBypassEnabled: Boolean) { - this.isBypassEnabled = isBypassEnabled + _isBypassEnabled.value = isBypassEnabled } } + +@Module +interface FakeDeviceEntryRepositoryModule { + @Binds fun bindFake(fake: FakeDeviceEntryRepository): DeviceEntryRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt new file mode 100644 index 000000000000..95b5316fbab5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt @@ -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. + */ + +package com.android.systemui.keyevent.data.repository + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeKeyEventRepository() : KeyEventRepository { + private val _isPowerButtonDown = MutableStateFlow(false) + override val isPowerButtonDown: Flow<Boolean> = _isPowerButtonDown.asStateFlow() + + fun setPowerButtonDown(isDown: Boolean) { + _isPowerButtonDown.value = isDown + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt index bf77b1a050cd..911eafae5c27 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt @@ -53,7 +53,7 @@ import com.android.systemui.user.data.repository.UserSwitcherRepository import com.android.systemui.user.data.repository.UserSwitcherRepositoryImpl import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.util.mockito.mock -import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.FakeGlobalSettings import com.android.systemui.util.settings.GlobalSettings import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.test.StandardTestDispatcher @@ -69,8 +69,8 @@ class FooterActionsTestUtils( private val scheduler: TestCoroutineScheduler, ) { /** Enable or disable the user switcher in the settings. */ - fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) { - settings.putBoolForUser(Settings.Global.USER_SWITCHER_ENABLED, enabled, userId) + fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean) { + settings.putBool(Settings.Global.USER_SWITCHER_ENABLED, enabled) // The settings listener is processing messages on the bgHandler (usually backed by a // testableLooper in tests), so let's make sure we process the callback before continuing. @@ -152,7 +152,7 @@ class FooterActionsTestUtils( userTracker: UserTracker = FakeUserTracker(), userSwitcherController: UserSwitcherController = mock(), userInfoController: UserInfoController = FakeUserInfoController(), - settings: GlobalSettings = FakeSettings(), + settings: GlobalSettings = FakeGlobalSettings(), ): UserSwitcherRepository { return UserSwitcherRepositoryImpl( context, 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 766f748bece8..a4881bc6efec 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 @@ -80,7 +80,11 @@ class SceneTestUtils( ) { val testDispatcher = StandardTestDispatcher() val testScope = TestScope(testDispatcher) - val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, false) } + val featureFlags = + FakeFeatureFlagsClassic().apply { + set(Flags.FACE_AUTH_REFACTOR, false) + set(Flags.FULL_SCREEN_USER_SWITCHER, false) + } val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true } val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() } val authenticationRepository: FakeAuthenticationRepository by lazy { @@ -205,7 +209,7 @@ class SceneTestUtils( return BouncerInteractor( applicationScope = applicationScope(), applicationContext = context, - repository = BouncerRepository(), + repository = BouncerRepository(featureFlags), deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt index 1bec82b0875b..e59f642071fb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt @@ -16,14 +16,18 @@ package com.android.systemui.statusbar.data import com.android.systemui.statusbar.disableflags.data.FakeStatusBarDisableFlagsDataLayerModule +import com.android.systemui.statusbar.notification.data.FakeStatusBarNotificationsDataLayerModule import com.android.systemui.statusbar.pipeline.data.FakeStatusBarPipelineDataLayerModule +import com.android.systemui.statusbar.policy.data.FakeStatusBarPolicyDataLayerModule import dagger.Module @Module( includes = [ FakeStatusBarDisableFlagsDataLayerModule::class, + FakeStatusBarNotificationsDataLayerModule::class, FakeStatusBarPipelineDataLayerModule::class, + FakeStatusBarPolicyDataLayerModule::class, ] ) object FakeStatusBarDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt new file mode 100644 index 000000000000..788e3aa9c41a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.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.data + +import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardStateRepositoryModule +import dagger.Module + +@Module(includes = [FakeNotificationsKeyguardStateRepositoryModule::class]) +object FakeStatusBarNotificationsDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt new file mode 100644 index 000000000000..5d3cb4db9c7e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.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.statusbar.notification.data.repository + +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +@SysUISingleton +class FakeNotificationsKeyguardViewStateRepository @Inject constructor() : + NotificationsKeyguardViewStateRepository { + private val _notificationsFullyHidden = MutableStateFlow(false) + override val areNotificationsFullyHidden: Flow<Boolean> = _notificationsFullyHidden + + private val _isPulseExpanding = MutableStateFlow(false) + override val isPulseExpanding: Flow<Boolean> = _isPulseExpanding + + fun setNotificationsFullyHidden(fullyHidden: Boolean) { + _notificationsFullyHidden.value = fullyHidden + } + + fun setPulseExpanding(expanding: Boolean) { + _isPulseExpanding.value = expanding + } +} + +@Module +interface FakeNotificationsKeyguardStateRepositoryModule { + @Binds + fun bindFake( + fake: FakeNotificationsKeyguardViewStateRepository + ): NotificationsKeyguardViewStateRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt index 16a326869562..23477d86807c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt @@ -1,9 +1,14 @@ package com.android.systemui.statusbar.policy import android.content.res.Configuration +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject /** Fake implementation of [ConfigurationController] for tests. */ -class FakeConfigurationController : ConfigurationController { +@SysUISingleton +class FakeConfigurationController @Inject constructor() : ConfigurationController { private var listeners = mutableListOf<ConfigurationController.ConfigurationListener>() @@ -33,3 +38,8 @@ class FakeConfigurationController : ConfigurationController { override fun isLayoutRtl(): Boolean = false } + +@Module +interface FakeConfigurationControllerModule { + @Binds fun bindFake(fake: FakeConfigurationController): ConfigurationController +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt new file mode 100644 index 000000000000..14bab84e62b2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt @@ -0,0 +1,24 @@ +/* + * 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.policy + +import dagger.Binds +import dagger.Module + +@Module +interface FakeSplitShadeStateControllerModule { + @Binds fun bindFake(fake: ResourcesSplitShadeStateController): SplitShadeStateController +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt new file mode 100644 index 000000000000..5aece1bbbd31 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.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.policy.data + +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepositoryModule +import dagger.Module + +@Module(includes = [FakeDeviceProvisioningRepositoryModule::class]) +object FakeStatusBarPolicyDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt new file mode 100644 index 000000000000..300229954044 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.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.statusbar.policy.data.repository + +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +@SysUISingleton +class FakeDeviceProvisioningRepository @Inject constructor() : DeviceProvisioningRepository { + private val _isDeviceProvisioned = MutableStateFlow(false) + override val isDeviceProvisioned: Flow<Boolean> = _isDeviceProvisioned + private val _isFactoryResetProtectionActive = MutableStateFlow(false) + override val isFactoryResetProtectionActive: Flow<Boolean> = _isFactoryResetProtectionActive + fun setDeviceProvisioned(isProvisioned: Boolean) { + _isDeviceProvisioned.value = isProvisioned + } + fun setFactoryResetProtectionActive(isActive: Boolean) { + _isFactoryResetProtectionActive.value = isActive + } +} + +@Module +interface FakeDeviceProvisioningRepositoryModule { + @Binds fun bindFake(fake: FakeDeviceProvisioningRepository): DeviceProvisioningRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt index 5de05c27ba2e..1f48d940f91c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.util.concurrency +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.util.time.FakeSystemClock import dagger.Binds @@ -22,14 +23,12 @@ import dagger.Module import dagger.Provides import java.util.concurrent.Executor -@Module(includes = [FakeExecutorModule.Bindings::class]) -class FakeExecutorModule( - @get:Provides val clock: FakeSystemClock = FakeSystemClock(), -) { - @get:Provides val executor = FakeExecutor(clock) +@Module +interface FakeExecutorModule { + @Binds @Main @SysUISingleton fun bindMainExecutor(executor: FakeExecutor): Executor - @Module - interface Bindings { - @Binds @Main fun bindMainExecutor(executor: FakeExecutor): Executor + companion object { + @Provides + fun provideFake(clock: FakeSystemClock) = FakeExecutor(clock) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java new file mode 100644 index 000000000000..db5eaffee76b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java @@ -0,0 +1,89 @@ +/* + * 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.systemui.util.settings; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.net.Uri; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FakeGlobalSettings implements GlobalSettings { + private final Map<String, String> mValues = new HashMap<>(); + private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>(); + + public static final Uri CONTENT_URI = Uri.parse("content://settings/fake_global"); + + public FakeGlobalSettings() { + } + + @Override + public ContentResolver getContentResolver() { + return null; + } + + @Override + public void registerContentObserver(Uri uri, boolean notifyDescendants, + ContentObserver settingsObserver) { + List<ContentObserver> observers; + mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>()); + observers = mContentObserversAllUsers.get(uri.toString()); + observers.add(settingsObserver); + } + + @Override + public void unregisterContentObserver(ContentObserver settingsObserver) { + for (Map.Entry<String, List<ContentObserver>> entry : + mContentObserversAllUsers.entrySet()) { + entry.getValue().remove(settingsObserver); + } + } + + @Override + public Uri getUriFor(String name) { + return Uri.withAppendedPath(CONTENT_URI, name); + } + + @Override + public String getString(String name) { + return mValues.get(getUriFor(name).toString()); + } + + @Override + public boolean putString(String name, String value) { + return putString(name, value, null, false); + } + + @Override + public boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag, + boolean makeDefault) { + String key = getUriFor(name).toString(); + mValues.put(key, value); + + Uri uri = getUriFor(name); + for (ContentObserver observer : + mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) { + observer.dispatchChange(false, List.of(uri), 0); + } + return true; + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java index 4b973162e42f..a49188687100 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java @@ -23,6 +23,8 @@ import android.net.Uri; import android.os.UserHandle; import android.util.Pair; +import androidx.annotation.NonNull; + import com.android.systemui.settings.UserTracker; import java.util.ArrayList; @@ -30,7 +32,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class FakeSettings implements SecureSettings, GlobalSettings, SystemSettings { +public class FakeSettings implements SecureSettings, SystemSettings { private final Map<SettingsKey, String> mValues = new HashMap<>(); private final Map<SettingsKey, List<ContentObserver>> mContentObservers = new HashMap<>(); @@ -64,7 +66,7 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti } @Override - public void registerContentObserverForUser(Uri uri, boolean notifyDescendents, + public void registerContentObserverForUser(Uri uri, boolean notifyDescendants, ContentObserver settingsObserver, int userHandle) { List<ContentObserver> observers; if (userHandle == UserHandle.USER_ALL) { @@ -147,7 +149,7 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti } @Override - public boolean putString(String name, String value, String tag, boolean makeDefault) { + public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) { return putString(name, value); } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt new file mode 100644 index 000000000000..3e3d7cbb40b5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.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.util.time + +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import dagger.Provides + +@Module +interface FakeSystemClockModule { + @Binds fun bindFake(fake: FakeSystemClock): SystemClock + + companion object { + @Provides @SysUISingleton fun providesFake() = FakeSystemClock() + } +} diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp index 155dc1a68295..18f783146c72 100644 --- a/packages/WallpaperBackup/Android.bp +++ b/packages/WallpaperBackup/Android.bp @@ -49,7 +49,7 @@ android_test { "androidx.test.core", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], resource_dirs: ["test/res"], certificate: "platform", diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp index b781602399a3..0244c0fe0533 100644 --- a/packages/overlays/tests/Android.bp +++ b/packages/overlays/tests/Android.bp @@ -34,7 +34,7 @@ android_test { "androidx.test.rules", "androidx.test.espresso.core", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], dxflags: ["--multi-dex"], } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index ced97cc0b39e..7ac7859f3c83 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -90,8 +90,14 @@ message MetricsEvent { // Make sound through the speaker. ALERT_BEEP = 2; - // Flash a notificaiton light. + // Flash a notification light. ALERT_BLINK = 4; + + // Alert was attenuated by polite notif. feature. + ALERT_POLITE = 8; + + // Alert was muted by polite notif. feature. + ALERT_MUTED = 16; } // Reasons that a notification might be dismissed. diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp new file mode 100644 index 000000000000..91acc3d0deb4 --- /dev/null +++ b/ravenwood/Android.bp @@ -0,0 +1,33 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "ravenwood-annotations", + srcs: [ + "annotations-src/**/*.java", + ], + visibility: ["//visibility:public"], +} + +// File that contains the standard command line arguments to hoststubgen. +filegroup { + name: "ravenwood-standard-options", + srcs: [ + "ravenwood-standard-options.txt", + ], + visibility: ["//visibility:public"], +} + +java_library { + name: "ravenwood-annotations-lib", + srcs: [":ravenwood-annotations"], + sdk_version: "core_current", + host_supported: true, + visibility: ["//visibility:public"], +} diff --git a/ravenwood/OWNERS b/ravenwood/OWNERS new file mode 100644 index 000000000000..c06b3b9c2d11 --- /dev/null +++ b/ravenwood/OWNERS @@ -0,0 +1,3 @@ +jsharkey@google.com +omakoto@google.com +jaggies@google.com diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java new file mode 100644 index 000000000000..be7b923244bf --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.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 android.ravenwood.annotations; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"}) + * of a callback to get a callback at the class load time. + * + * The method must be {@code public static} with a single argument that takes + * {@link Class}. + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodClassLoadHook { + String value(); +} diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java new file mode 100644 index 000000000000..1644ffc57dca --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java @@ -0,0 +1,37 @@ +/* + * 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.ravenwood.annotations; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * TODO: Javadoc + * + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodKeep { +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java index 3fff136db03a..eb883e228a40 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java +++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java @@ -13,19 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.ravenwood.annotations; -package com.android.systemui; +import static java.lang.annotation.ElementType.TYPE; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Mark as tests for Robolectric pilot projects. The filter can better help grouping test results - * that runs on CI + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * TODO: Javadoc */ -@Target({ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface RoboPilotTest { +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodNativeSubstitutionClass { + String value(); } diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java new file mode 100644 index 000000000000..ffa1fa50fa4e --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.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 android.ravenwood.annotations; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * TODO: Javadoc + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodRemove { +} diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java new file mode 100644 index 000000000000..6d747da10207 --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.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 android.ravenwood.annotations; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * TODO: Javadoc + */ +@Target({METHOD}) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodSubstitute { + // TODO We should add "_host" as default. We're not doing it yet, because extractign the default + // value with ASM doesn't seem trivial. (? not sure.) + String suffix(); +} diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java new file mode 100644 index 000000000000..a329d841abbe --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java @@ -0,0 +1,35 @@ +/* + * 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.ravenwood.annotations; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * TODO: Javadoc + * TODO: Create "whole-class-throw"? + */ +@Target({METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodThrow { +} diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java new file mode 100644 index 000000000000..ae6f42dbeaa6 --- /dev/null +++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java @@ -0,0 +1,37 @@ +/* + * 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.ravenwood.annotations; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * TODO: Javadoc + * TODO: Create "whole-class-throw"? + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface RavenwoodWholeClassKeep { +} diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt new file mode 100644 index 000000000000..6e1384f368b8 --- /dev/null +++ b/ravenwood/ravenwood-standard-options.txt @@ -0,0 +1,37 @@ +# File containing standard options to HostStubGen for Ravenwood + +--debug + +# Keep all classes / methods / fields, but make the methods throw. +--default-throw + +# Uncomment below lines to enable each feature. +# --enable-non-stub-method-check + +#--default-method-call-hook +# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall +#--default-class-load-hook +# com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + +# Standard annotations. +# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`. +--keep-annotation + android.ravenwood.annotations.RavenwoodKeep + +--keep-class-annotation + android.ravenwood.annotations.RavenwoodWholeClassKeep + +--throw-annotation + android.ravenwood.annotations.RavenwoodThrow + +--remove-annotation + android.ravenwood.annotations.RavenwoodRemove + +--substitute-annotation + android.ravenwood.annotations.RavenwoodSubstitute + +--native-substitute-annotation + android.ravenwood.annotations.RavenwoodNativeSubstitutionClass + +--class-load-hook-annotation + android.ravenwood.annotations.RavenwoodClassLoadHook diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 11189cf9e39c..6fa9c0809f75 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -34,3 +34,10 @@ flag { description: "Calls WMS.addWindowToken without holding A11yManagerService#mLock" bug: "297972548" } + +flag { + name: "pinch_zoom_zero_min_span" + namespace: "accessibility" + description: "Whether to set min span of ScaleGestureDetector to zero." + bug: "295327792" +}
\ No newline at end of file diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java index c5495d98226e..94556282d0d3 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java @@ -28,8 +28,10 @@ import android.util.TypedValue; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import android.view.ViewConfiguration; import com.android.internal.R; +import com.android.server.accessibility.Flags; /** * Handles the behavior while receiving scaling and panning gestures if it's enabled. @@ -70,7 +72,13 @@ class PanningScalingHandler extends mMaxScale = maxScale; mMinScale = minScale; mBlockScroll = blockScroll; - mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain()); + if (Flags.pinchZoomZeroMinSpan()) { + mScaleGestureDetector = new ScaleGestureDetector(context, + ViewConfiguration.get(context).getScaledTouchSlop() * 2, + /* minSpan= */ 0, Handler.getMain(), this); + } else { + mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain()); + } mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain()); mScaleGestureDetector.setQuickScaleEnabled(false); mMagnificationDelegate = magnificationDelegate; diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 7e09b5ea9fa5..258820a5a03c 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -24,6 +24,7 @@ import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -1660,8 +1661,21 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku synchronized (mLock) { ensureGroupStateLoadedLocked(userId); + final String pkg = componentName.getPackageName(); + final ProviderId id; + if (!mPackageManagerInternal.isSameApp(pkg, callingUid, userId)) { + // If the calling process is requesting to pin appwidgets from another process, + // check if the calling process has the necessary permission. + if (!injectHasAccessWidgetsPermission(Binder.getCallingPid(), callingUid)) { + return false; + } + id = new ProviderId(mPackageManagerInternal.getPackageUid( + pkg, 0 /* flags */, userId), componentName); + } else { + id = new ProviderId(callingUid, componentName); + } // Look for the widget associated with the caller. - Provider provider = lookupProviderLocked(new ProviderId(callingUid, componentName)); + Provider provider = lookupProviderLocked(id); if (provider == null || provider.zombie) { return false; } @@ -1675,6 +1689,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku .requestPinAppWidget(callingPackage, info, extras, resultSender, userId); } + /** + * Returns true if the caller has the proper permission to access app widgets. + */ + private boolean injectHasAccessWidgetsPermission(int callingPid, int callingUid) { + return mContext.checkPermission(Manifest.permission.CLEAR_APP_USER_DATA, + callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; + } + @Override public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter, int profileId, String packageName) { @@ -4131,7 +4153,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku return false; } - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public AppWidgetProviderInfo getInfoLocked(Context context) { if (!mInfoParsed) { // parse @@ -4159,18 +4181,18 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku * be completely parsed and only contain placeHolder information like * {@link AppWidgetProviderInfo#providerInfo} */ - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public AppWidgetProviderInfo getPartialInfoLocked() { return info; } - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public void setPartialInfoLocked(AppWidgetProviderInfo info) { this.info = info; mInfoParsed = false; } - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public void setInfoLocked(AppWidgetProviderInfo info) { this.info = info; mInfoParsed = true; diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp index d43a219e6205..eb23f2f9b435 100644 --- a/services/autofill/Android.bp +++ b/services/autofill/Android.bp @@ -19,19 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.autofill-sources"], libs: ["services.core"], - static_libs: ["autofill_flags_java_lib"], -} - -aconfig_declarations { - name: "autofill_flags", - package: "android.service.autofill", - srcs: [ - "bugfixes.aconfig", - "features.aconfig", - ], -} - -java_aconfig_library { - name: "autofill_flags_java_lib", - aconfig_declarations: "autofill_flags", } diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ef237546378e..b37bbd6ea27f 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -5,4 +5,18 @@ flag { namespace: "autofill" description: "Test flag " bug: "297380045" -}
\ No newline at end of file +} + +flag { + name: "fill_fields_from_current_session_only" + namespace: "autofill" + description: "Only fill autofill fields that are part of the current session." + bug: "270722825" +} + +flag { + name: "relayout" + namespace: "autofill" + description: "Mitigation for relayout issue" + bug: "294330426" +} diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index c7b53c55d89b..72242d265e79 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -58,6 +58,7 @@ import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; import android.service.autofill.FillEventHistory; +import android.service.autofill.Flags; import android.service.autofill.UserData; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; @@ -226,6 +227,12 @@ public final class AutofillManagerService @GuardedBy("mFlagLock") private int mMaxInputLengthForAutofill; + @GuardedBy("mFlagLock") + private boolean mAutofillCredmanIntegrationEnabled; + + @GuardedBy("mFlagLock") + private boolean mIsFillFieldsFromCurrentSessionOnly; + // Default flag values for Autofill PCC private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = ""; @@ -701,12 +708,16 @@ public final class AutofillManagerService DeviceConfig.NAMESPACE_AUTOFILL, AutofillFeatureFlags.DEVICE_CONFIG_MAX_INPUT_LENGTH_FOR_AUTOFILL, AutofillFeatureFlags.DEFAULT_MAX_INPUT_LENGTH_FOR_AUTOFILL); + mAutofillCredmanIntegrationEnabled = Flags.autofillCredmanIntegration(); + mIsFillFieldsFromCurrentSessionOnly = Flags.fillFieldsFromCurrentSessionOnly(); if (verbose) { Slog.v(mTag, "setDeviceConfigProperties() for PCC: " + "mPccClassificationEnabled=" + mPccClassificationEnabled + ", mPccPreferProviderOverPcc=" + mPccPreferProviderOverPcc + ", mPccUseFallbackDetection=" + mPccUseFallbackDetection - + ", mPccProviderHints=" + mPccProviderHints); + + ", mPccProviderHints=" + mPccProviderHints + + ", mAutofillCredmanIntegrationEnabled=" + + mAutofillCredmanIntegrationEnabled); } } } @@ -965,6 +976,15 @@ public final class AutofillManagerService } /** + * Whether the Autofill-Credman integration feature flag is enabled. + */ + public boolean isAutofillCredmanIntegrationEnabled() { + synchronized (mFlagLock) { + return mAutofillCredmanIntegrationEnabled; + } + } + + /** * Whether the Autofill Provider shouldbe preferred over PCC results for selecting datasets. */ public boolean preferProviderOverPcc() { @@ -1004,6 +1024,15 @@ public final class AutofillManagerService } } + /** + * Return if autofill should only fill in fields from current session. + */ + public boolean getIsFillFieldsFromCurrentSessionOnly() { + synchronized (mFlagLock) { + return mIsFillFieldsFromCurrentSessionOnly; + } + } + @Nullable @VisibleForTesting static Map<String, String[]> getAllowedCompatModePackages(String setting) { @@ -2096,6 +2125,9 @@ public final class AutofillManagerService pw.print(";"); pw.print("mPccProviderHints="); pw.println(mPccProviderHints); + pw.print(";"); + pw.print("mAutofillCredmanIntegrationEnabled="); + pw.println(mAutofillCredmanIntegrationEnabled); } // Dump per-user services dumpLocked("", pw); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 5b8bdd57ccbb..518b81f19467 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -19,6 +19,7 @@ package com.android.server.autofill; import static android.service.autofill.FillEventHistory.Event.NO_SAVE_UI_REASON_NONE; import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.autofill.FillRequest.FLAG_SCREEN_HAS_CREDMAN_FIELD; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED; import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY; @@ -103,6 +104,10 @@ final class AutofillManagerServiceImpl extends AbstractPerUserSystemService<AutofillManagerServiceImpl, AutofillManagerService> { private static final String TAG = "AutofillManagerServiceImpl"; + + private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME = + new ComponentName("com.android.credentialmanager", + "com.android.credentialmanager.autofill.CredentialAutofillService"); private static final int MAX_SESSION_ID_CREATE_TRIES = 2048; /** Minimum interval to prune abandoned sessions */ @@ -532,9 +537,16 @@ final class AutofillManagerServiceImpl assertCallerLocked(clientActivity, compatMode); - // It's null when the session is just for augmented autofill - final ComponentName serviceComponentName = mInfo == null ? null + ComponentName serviceComponentName = mInfo == null ? null : mInfo.getServiceInfo().getComponentName(); + + if (isAutofillCredmanIntegrationEnabled() + && ((flags & FLAG_SCREEN_HAS_CREDMAN_FIELD) != 0)) { + // Hardcode to credential manager proxy service + Slog.i(TAG, "Routing to CredentialAutofillService"); + serviceComponentName = CREDMAN_SERVICE_COMPONENT_NAME; + } + final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock, sessionId, taskId, clientUid, clientActivityToken, clientCallback, hasCallback, mUiLatencyHistory, mWtfHistory, serviceComponentName, @@ -1747,6 +1759,10 @@ final class AutofillManagerServiceImpl } } + public boolean isAutofillCredmanIntegrationEnabled() { + return mMaster.isAutofillCredmanIntegrationEnabled(); + } + /** * Called when the {@link AutofillManagerService#mFieldClassificationResolver} * changed (among other places). diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 0220deca18c1..ae1487775b74 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2437,7 +2437,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } - fillInIntent = createAuthFillInIntentLocked(requestId, extras); + fillInIntent = createAuthFillInIntentLocked(requestId, extras, /* authExtras= */ null); if (fillInIntent == null) { forceRemoveFromServiceLocked(); return; @@ -5558,7 +5558,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetAuthenticationType( AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false); - final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState); + final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState, + dataset.getAuthenticationExtras()); if (fillInIntent == null) { forceRemoveFromServiceLocked(); return; @@ -5574,7 +5575,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO: this should never be null, but we got at least one occurrence, probably due to a race. @GuardedBy("mLock") @Nullable - private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) { + private Intent createAuthFillInIntentLocked(int requestId, Bundle extras, + @Nullable Bundle authExtras) { final Intent fillInIntent = new Intent(); final FillContext context = getFillContextByRequestIdLocked(requestId); @@ -5591,6 +5593,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure()); fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras); + if (authExtras != null) { + fillInIntent.putExtra(AutofillManager.EXTRA_AUTH_STATE, authExtras); + } return fillInIntent; } @@ -6137,9 +6142,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState continue; } final AutofillId viewId = dataset.getFieldIds().get(i); + final ViewState viewState = mViewStates.get(viewId); + if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly() + && viewState != null && viewState.id.getSessionId() != id) { + if (sVerbose) { + Slog.v(TAG, "Skipping filling view: " + + viewId + " as it isn't part of the current session: " + id); + } + continue; + } ids.add(viewId); values.add(dataset.getFieldValues().get(i)); - final ViewState viewState = mViewStates.get(viewId); if (viewState != null && (viewState.getState() & ViewState.STATE_WAITING_DATASET_AUTH) != 0) { if (sVerbose) { 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 cfe56e910b8a..5cb100a1bfa5 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -38,6 +38,7 @@ import android.companion.virtual.VirtualDeviceManager; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.flags.Flags; import android.companion.virtual.sensor.VirtualSensor; +import android.companion.virtualnative.IVirtualDeviceManagerNative; import android.content.AttributionSource; import android.content.Context; import android.content.Intent; @@ -88,8 +89,11 @@ public class VirtualDeviceManagerService extends SystemService { private static final String TAG = "VirtualDeviceManagerService"; + private static final String VIRTUAL_DEVICE_NATIVE_SERVICE = "virtualdevice_native"; + private final Object mVirtualDeviceManagerLock = new Object(); private final VirtualDeviceManagerImpl mImpl; + private final VirtualDeviceManagerNativeImpl mNativeImpl; private final VirtualDeviceManagerInternal mLocalService; private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext()); private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -125,6 +129,7 @@ public class VirtualDeviceManagerService extends SystemService { public VirtualDeviceManagerService(Context context) { super(context); mImpl = new VirtualDeviceManagerImpl(); + mNativeImpl = Flags.enableNativeVdm() ? new VirtualDeviceManagerNativeImpl() : null; mLocalService = new LocalService(); } @@ -155,6 +160,9 @@ public class VirtualDeviceManagerService extends SystemService { @Override public void onStart() { publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl); + if (Flags.enableNativeVdm()) { + publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl); + } publishLocalService(VirtualDeviceManagerInternal.class, mLocalService); ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService( ActivityTaskManagerInternal.class); @@ -590,6 +598,19 @@ public class VirtualDeviceManagerService extends SystemService { } } + final class VirtualDeviceManagerNativeImpl extends IVirtualDeviceManagerNative.Stub { + @Override // Binder call + public int[] getDeviceIdsForUid(int uid) { + return mLocalService + .getDeviceIdsForUid(uid).stream().mapToInt(Integer::intValue).toArray(); + } + + @Override // Binder call + public int getDevicePolicy(int deviceId, int policyType) { + return mImpl.getDevicePolicy(deviceId, policyType); + } + } + private final class LocalService extends VirtualDeviceManagerInternal { @GuardedBy("mVirtualDeviceManagerLock") private final ArrayList<VirtualDisplayListener> diff --git a/services/core/Android.bp b/services/core/Android.bp index 6521fabe5b7c..e5225f65f22a 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -183,6 +183,7 @@ java_library_static { "cbor-java", "display_flags_lib", "icu4j_calendar_astronomer", + "android.security.aaid_aidl-java", "netd-client", "overlayable_policy_aidl-java", "SurfaceFlingerProperties", diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index cd879083927f..8df54569cccd 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1428,6 +1428,12 @@ public abstract class PackageManagerInternal { @UserIdInt int userId); /** + * Sends the PACKAGE_RESTARTED broadcast on the package manager handler thread. + */ + public abstract void sendPackageRestartedBroadcast(@NonNull String packageName, + int uid, @Intent.Flags int flags); + + /** * Return a list of all historical install sessions for the given user. */ public abstract ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions( diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index d47a3991a966..db89cac2a943 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -87,7 +87,7 @@ option java_package com.android.server # replaces 27510 with a row per notification 27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1) # a notification emited noise, vibration, or light -27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1) +27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1),(politeness|1) # a notification was added to a autogroup 27533 notification_autogrouped (key|3) # notification was removed from an autogroup diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 55130e4cbbe6..987507fe7f03 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -16,9 +16,6 @@ per-file UserspaceRebootLogger.java = ioffe@google.com, dvander@google.com # ServiceWatcher per-file ServiceWatcher.java = sooniln@google.com -# Health -per-file BatteryService.java = file:platform/hardware/interfaces:/health/aidl/OWNERS - per-file *Accessibility* = file:/services/accessibility/OWNERS per-file *Alarm* = file:/apex/jobscheduler/OWNERS per-file *AppOp* = file:/core/java/android/permission/OWNERS @@ -39,7 +36,7 @@ per-file MmsServiceBroker.java = file:/telephony/OWNERS per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS per-file PinnerService.java = file:/apct-tests/perftests/OWNERS -per-file RescueParty.java = fdunlap@google.com, shuc@google.com, ancr@google.com, harshitmahajan@google.com +per-file RescueParty.java = shuc@google.com, ancr@google.com, harshitmahajan@google.com per-file SystemClockTime.java = file:/services/core/java/com/android/server/timedetector/OWNERS per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timezonedetector/OWNERS per-file TelephonyRegistry.java = file:/telephony/OWNERS diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 9bab261bb0f4..57ed5a298d9f 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -41,6 +41,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -153,6 +154,9 @@ public final class PinnerService extends SystemService { @GuardedBy("this") private ArraySet<Integer> mPinKeys; + private static final String DEVICE_CONFIG_KEY_ANON_SIZE = "pin_shared_anon_size"; + private static final long DEFAULT_ANON_SIZE = + SystemProperties.getLong("pinner.pin_shared_anon_size", 0); private static final long MAX_ANON_SIZE = 2L * (1L << 30); // 2GB private long mPinAnonSize; private long mPinAnonAddress; @@ -180,6 +184,17 @@ public final class PinnerService extends SystemService { } }; + private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + if (DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT.equals(properties.getNamespace()) + && properties.getKeyset().contains(DEVICE_CONFIG_KEY_ANON_SIZE)) { + refreshPinAnonConfig(); + } + } + }; + public PinnerService(Context context) { super(context); @@ -206,6 +221,11 @@ public final class PinnerService extends SystemService { registerUidListener(); registerUserSetupCompleteListener(); + + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + new HandlerExecutor(mPinnerHandler), + mDeviceConfigListener); } @Override @@ -344,6 +364,8 @@ public final class PinnerService extends SystemService { } } } + + refreshPinAnonConfig(); } /** @@ -359,8 +381,10 @@ public final class PinnerService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri) { if (userSetupCompleteUri.equals(uri)) { - sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(), - true /* force */); + if (mConfiguredToPinHome) { + sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(), + true /* force */); + } } } }, UserHandle.USER_ALL); @@ -558,11 +582,6 @@ public final class PinnerService extends SystemService { pinKeys.add(KEY_ASSISTANT); } - mPinAnonSize = DeviceConfig.getLong(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, - "pin_anon_size", - SystemProperties.getLong("pinner.pin_anon_size", 0)); - mPinAnonSize = Math.max(0, Math.min(mPinAnonSize, MAX_ANON_SIZE)); - return pinKeys; } @@ -602,7 +621,6 @@ public final class PinnerService extends SystemService { int key = currentPinKeys.valueAt(i); pinApp(key, userHandle, true /* force */); } - pinAnonRegion(); } /** @@ -687,10 +705,28 @@ public final class PinnerService extends SystemService { } /** + * Handle any changes in the anon region pinner config. + */ + private void refreshPinAnonConfig() { + long newPinAnonSize = + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + DEVICE_CONFIG_KEY_ANON_SIZE, + DEFAULT_ANON_SIZE); + newPinAnonSize = Math.max(0, Math.min(newPinAnonSize, MAX_ANON_SIZE)); + if (newPinAnonSize != mPinAnonSize) { + mPinAnonSize = newPinAnonSize; + pinAnonRegion(); + } + } + + /** * Pin an empty anonymous region. This should only be used for ablation experiments. */ private void pinAnonRegion() { if (mPinAnonSize == 0) { + Slog.d(TAG, "pinAnonRegion: releasing pinned region"); + unpinAnonRegion(); return; } long alignedPinSize = mPinAnonSize; @@ -698,15 +734,21 @@ public final class PinnerService extends SystemService { alignedPinSize -= alignedPinSize % PAGE_SIZE; Slog.e(TAG, "pinAnonRegion: aligning size to " + alignedPinSize); } - if (mPinAnonAddress != 0 - && mCurrentlyPinnedAnonSize != alignedPinSize) { + if (mPinAnonAddress != 0) { + if (mCurrentlyPinnedAnonSize == alignedPinSize) { + Slog.d(TAG, "pinAnonRegion: already pinned region of size " + alignedPinSize); + return; + } + Slog.d(TAG, "pinAnonRegion: resetting pinned region for new size " + alignedPinSize); unpinAnonRegion(); } long address = 0; try { + // Map as SHARED to avoid changing rss.anon for system_server (per /proc/*/status). + // The mapping is visible in other rss metrics, and as private dirty in smaps/meminfo. address = Os.mmap(0, alignedPinSize, OsConstants.PROT_READ | OsConstants.PROT_WRITE, - OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS, + OsConstants.MAP_SHARED | OsConstants.MAP_ANONYMOUS, new FileDescriptor(), /*offset=*/0); Unsafe tempUnsafe = null; @@ -727,7 +769,7 @@ public final class PinnerService extends SystemService { mCurrentlyPinnedAnonSize = alignedPinSize; mPinAnonAddress = address; address = -1; - Slog.e(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize); + Slog.w(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize); } catch (Exception ex) { Slog.e(TAG, "Could not pin anon region of size " + alignedPinSize, ex); return; @@ -742,6 +784,8 @@ public final class PinnerService extends SystemService { if (mPinAnonAddress != 0) { safeMunmap(mPinAnonAddress, mCurrentlyPinnedAnonSize); } + mPinAnonAddress = 0; + mCurrentlyPinnedAnonSize = 0; } /** diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8cc2665b3562..962f38f10b5d 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -214,6 +214,9 @@ class StorageManagerService extends IStorageManager.Stub // external storage service. public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10; + /** Extended timeout for the system server watchdog. */ + private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 60 * 1000; + @GuardedBy("mLock") private final Set<Integer> mFuseMountedUser = new ArraySet<>(); @@ -1230,6 +1233,8 @@ class StorageManagerService extends IStorageManager.Stub private void onUserStopped(int userId) { Slog.d(TAG, "onUserStopped " + userId); + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow"); try { mVold.onUserStopped(userId); mStoraged.onUserStopped(userId); @@ -1312,6 +1317,8 @@ class StorageManagerService extends IStorageManager.Stub unlockedUsers.add(userId); } } + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow"); for (Integer userId : unlockedUsers) { try { mVold.onUserStopped(userId); @@ -3600,6 +3607,8 @@ class StorageManagerService extends IStorageManager.Stub @Override public ParcelFileDescriptor open() throws AppFuseMountException { + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow"); try { final FileDescriptor fd = mVold.mountAppFuse(uid, mountId); mMounted = true; @@ -3612,6 +3621,8 @@ class StorageManagerService extends IStorageManager.Stub @Override public ParcelFileDescriptor openFile(int mountId, int fileId, int flags) throws AppFuseMountException { + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow"); try { return new ParcelFileDescriptor( mVold.openAppFuseFile(uid, mountId, fileId, flags)); @@ -3622,6 +3633,8 @@ class StorageManagerService extends IStorageManager.Stub @Override public void close() throws Exception { + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow"); if (mMounted) { mVold.unmountAppFuse(uid, mountId); mMounted = false; diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index b05b397a45de..55aa7164a67b 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -644,6 +644,16 @@ public class Watchdog implements Dumpable { } /** + * Sets a one-off timeout for the next run of the watchdog for the monitor thread. + * + * <p>Simiar to {@link setOneOffTimeoutForCurrentThread} but used for monitors added through + * {@link #addMonitor} + */ + public void setOneOffTimeoutForMonitors(int oneOffTimeoutMillis, String reason) { + mMonitorChecker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason); + } + + /** * Pauses Watchdog action for the currently running thread. Useful before executing long running * operations that could falsely trigger the watchdog. Each call to this will require a matching * call to {@link #resumeWatchingCurrentThread}. diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 330742a2d748..5fb889a23fc5 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -5023,7 +5023,7 @@ public class AccountManagerService p.setDataPosition(0); Bundle simulateBundle = p.readBundle(); p.recycle(); - Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class); if (intent != null && intent.getClass() != Intent.class) { return false; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d3ce47c56e49..19879db1d22e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4160,26 +4160,34 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") private void finishForceStopPackageLocked(final String packageName, int uid) { - Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, - Uri.fromParts("package", packageName, null)); + int flags = 0; if (!mProcessesReady) { - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - } - final int userId = UserHandle.getUserId(uid); - final int[] broadcastAllowList = - getPackageManagerInternal().getVisibilityAllowList(packageName, userId); - intent.putExtra(Intent.EXTRA_UID, uid); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - broadcastIntentLocked(null /* callerApp */, null /* callerPackage */, - null /* callerFeatureId */, intent, null /* resolvedType */, - null /* resultToApp */, null /* resultTo */, - 0 /* resultCode */, null /* resultData */, null /* resultExtras */, - null /* requiredPermissions */, null /* excludedPermissions */, - null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */, - false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(), - Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE, - broadcastAllowList, null /* filterExtrasForReceiver */); + flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND; + } + if (android.content.pm.Flags.stayStopped()) { + // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED + mPackageManagerInt.sendPackageRestartedBroadcast(packageName, + uid, flags); + } else { + Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, + Uri.fromParts("package", packageName, null)); + intent.addFlags(flags); + final int userId = UserHandle.getUserId(uid); + final int[] broadcastAllowList = + getPackageManagerInternal().getVisibilityAllowList(packageName, userId); + intent.putExtra(Intent.EXTRA_UID, uid); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + broadcastIntentLocked(null /* callerApp */, null /* callerPackage */, + null /* callerFeatureId */, intent, null /* resolvedType */, + null /* resultToApp */, null /* resultTo */, + 0 /* resultCode */, null /* resultData */, null /* resultExtras */, + null /* requiredPermissions */, null /* excludedPermissions */, + null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */, + false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE, + broadcastAllowList, null /* filterExtrasForReceiver */); + } } private void cleanupDisabledPackageComponentsLocked( diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java index 378a38602211..9ba49ce35dad 100644 --- a/services/core/java/com/android/server/am/AnrTimer.java +++ b/services/core/java/com/android/server/am/AnrTimer.java @@ -108,6 +108,14 @@ class AnrTimer<V> { private static final boolean ENABLE_TRACING = false; /** + * Return true if the feature is enabled. By default, the value is take from the Flags class + * but it can be changed for local testing. + */ + private static boolean anrTimerServiceEnabled() { + return Flags.anrTimerServiceEnabled(); + } + + /** * The status of an ANR timer. TIMER_INVALID status is returned when an error is detected. */ private static final int TIMER_INVALID = 0; @@ -327,18 +335,33 @@ class AnrTimer<V> { */ @VisibleForTesting static class Injector { + private final Handler mReferenceHandler; + + Injector(@NonNull Handler handler) { + mReferenceHandler = handler; + } + /** - * Return a handler for the given Callback. + * Return a handler for the given Callback, based on the reference handler. The handler + * might be mocked, in which case it does not have a valid Looper. In this case, use the + * main Looper. */ + @NonNull Handler getHandler(@NonNull Handler.Callback callback) { - return null; + Looper looper = mReferenceHandler.getLooper(); + if (looper == null) looper = Looper.getMainLooper(); + return new Handler(looper, callback); }; - /** - * Return a CpuTracker. - */ + /** Return a CpuTracker. */ + @NonNull CpuTracker getTracker() { - return null; + return new CpuTracker(); + } + + /** Return true if the feature is enabled. */ + boolean getFeatureEnabled() { + return anrTimerServiceEnabled(); } } @@ -375,12 +398,6 @@ class AnrTimer<V> { /** The interface to fetch process statistics that might extend an ANR timeout. */ private final CpuTracker mCpu; - /** Create a HandlerTimerService based on the input handler. */ - HandlerTimerService(@NonNull Handler handler) { - mHandler = new Handler(handler.getLooper(), this::expires); - mCpu = new CpuTracker(); - } - /** Create a HandlerTimerService that directly uses the supplied handler and tracker. */ @VisibleForTesting HandlerTimerService(@NonNull Injector injector) { @@ -491,38 +508,56 @@ class AnrTimer<V> { private final boolean mLenientCancel = true; /** + * The top-level switch for the feature enabled or disabled. + */ + private final FeatureSwitch mFeature; + + /** * The common constructor. A null injector results in a normal, production timer. */ @VisibleForTesting AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend, - @Nullable Injector injector) { + @NonNull Injector injector) { mHandler = handler; mWhat = what; mLabel = label; mExtend = extend; - if (injector == null) { - mTimerService = new HandlerTimerService(handler); + boolean enabled = injector.getFeatureEnabled(); + if (!enabled) { + mFeature = new FeatureDisabled(); + mTimerService = null; } else { + mFeature = new FeatureEnabled(); mTimerService = new HandlerTimerService(injector); + + synchronized (sAnrTimerList) { + sAnrTimerList.add(new WeakReference(this)); + } } - synchronized (sAnrTimerList) { - sAnrTimerList.add(new WeakReference(this)); - } - Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService.toString(), label)); + Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService, label)); } /** * Create one timer instance for production. The client can ask for extensible timeouts. */ AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) { - this(handler, what, label, extend, null); + this(handler, what, label, extend, new Injector(handler)); } /** * Create one timer instance for production. There are no extensible timeouts. */ AnrTimer(@NonNull Handler handler, int what, @NonNull String label) { - this(handler, what, label, false, null); + this(handler, what, label, false); + } + + /** + * Return true if the service is enabled on this instance. Clients should use this method to + * decide if the feature is enabled, and not read the flags directly. This method should be + * deleted if and when the feature is enabled permanently. + */ + boolean serviceEnabled() { + return mFeature.enabled(); } /** @@ -613,93 +648,186 @@ class AnrTimer<V> { Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg)); } - /** - * Start a timer. + /** + * The FeatureSwitch class provides a quick switch between feature-enabled behavior and + * feature-disabled behavior. */ - boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { - final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, this); - synchronized (mLock) { - Timer old = mTimerMap.get(arg); - if (old != null) { - // There is an existing timer. This is a protocol error in the client. Record - // the error and then clean up by canceling running timers and discarding expired - // timers. - restartedLocked(old.status, arg); - if (old.status == TIMER_EXPIRED) { - discard(arg); + private abstract class FeatureSwitch { + abstract boolean start(@NonNull V arg, int pid, int uid, long timeoutMs); + abstract boolean cancel(@NonNull V arg); + abstract boolean accept(@NonNull V arg); + abstract boolean discard(@NonNull V arg); + abstract boolean enabled(); + } + + /** + * The FeatureDisabled class bypasses almost all AnrTimer logic. It is used when the AnrTimer + * service is disabled via Flags.anrTimerServiceEnabled. + */ + private class FeatureDisabled extends FeatureSwitch { + /** Start a timer by sending a message to the client's handler. */ + boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + final Message msg = mHandler.obtainMessage(mWhat, arg); + mHandler.sendMessageDelayed(msg, timeoutMs); + return true; + } + + /** Cancel a timer by removing the message from the client's handler. */ + boolean cancel(@NonNull V arg) { + mHandler.removeMessages(mWhat, arg); + return true; + } + + /** accept() is a no-op when the feature is disabled. */ + boolean accept(@NonNull V arg) { + return true; + } + + /** discard() is a no-op when the feature is disabled. */ + boolean discard(@NonNull V arg) { + return true; + } + + /** The feature is not enabled. */ + boolean enabled() { + return false; + } + } + + /** + * The FeatureEnabled class enables the AnrTimer logic. It is used when the AnrTimer service + * is enabled via Flags.anrTimerServiceEnabled. + */ + private class FeatureEnabled extends FeatureSwitch { + + /** + * Start a timer. + */ + boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, AnrTimer.this); + synchronized (mLock) { + Timer old = mTimerMap.get(arg); + if (old != null) { + // There is an existing timer. This is a protocol error in the client. + // Record the error and then clean up by canceling running timers and + // discarding expired timers. + restartedLocked(old.status, arg); + if (old.status == TIMER_EXPIRED) { + discard(arg); + } else { + cancel(arg); + } + } + if (mTimerService.start(timer)) { + timer.status = TIMER_RUNNING; + mTimerMap.put(arg, timer); + mTotalStarted++; + mMaxStarted = Math.max(mMaxStarted, mTimerMap.size()); + if (DEBUG) report(timer, "start"); + return true; } else { - cancel(arg); + Log.e(TAG, "AnrTimer.start failed"); + return false; } } - if (mTimerService.start(timer)) { - timer.status = TIMER_RUNNING; - mTimerMap.put(arg, timer); - mTotalStarted++; - mMaxStarted = Math.max(mMaxStarted, mTimerMap.size()); - if (DEBUG) report(timer, "start"); + } + + /** + * Cancel a timer. Return false if the timer was not found. + */ + boolean cancel(@NonNull V arg) { + synchronized (mLock) { + Timer timer = removeLocked(arg); + if (timer == null) { + if (!mLenientCancel) notFoundLocked("cancel", arg); + return false; + } + mTimerService.cancel(timer); + // There may be an expiration message in flight. Cancel it. + mHandler.removeMessages(mWhat, arg); + if (DEBUG) report(timer, "cancel"); + timer.release(); return true; - } else { - Log.e(TAG, "AnrTimer.start failed"); - return false; } } + + /** + * Accept a timer in the framework-level handler. The timeout has been accepted and the + * timeout handler is executing. Return false if the timer was not found. + */ + boolean accept(@NonNull V arg) { + synchronized (mLock) { + Timer timer = removeLocked(arg); + if (timer == null) { + notFoundLocked("accept", arg); + return false; + } + mTimerService.accept(timer); + traceEnd(timer); + if (DEBUG) report(timer, "accept"); + timer.release(); + return true; + } + } + + /** + * Discard a timer in the framework-level handler. For whatever reason, the timer is no + * longer interesting. No statistics are collected. Return false if the time was not + * found. + */ + boolean discard(@NonNull V arg) { + synchronized (mLock) { + Timer timer = removeLocked(arg); + if (timer == null) { + notFoundLocked("discard", arg); + return false; + } + mTimerService.discard(timer); + traceEnd(timer); + if (DEBUG) report(timer, "discard"); + timer.release(); + return true; + } + } + + /** The feature is enabled. */ + boolean enabled() { + return true; + } } /** - * Cancel a timer. Return false if the timer was not found. + * Start a timer associated with arg. If a timer already exists with the same arg, then that + * timer is canceled and a new timer is created. This returns false if the timer cannot be + * created. + */ + boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + return mFeature.start(arg, pid, uid, timeoutMs); + } + + /** + * Cancel a running timer and remove it from any list. This returns true if the timer was + * found and false otherwise. It is not an error to cancel a non-existent timer. It is also + * not an error to cancel an expired timer. */ boolean cancel(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - if (!mLenientCancel) notFoundLocked("cancel", arg); - return false; - } - mTimerService.cancel(timer); - // There may be an expiration message in flight. Cancel it. - mHandler.removeMessages(mWhat, arg); - if (DEBUG) report(timer, "cancel"); - timer.release(); - return true; - } + return mFeature.cancel(arg); } /** - * Accept a timer in the framework-level handler. The timeout has been accepted and the - * timeout handler is executing. Return false if the timer was not found. + * Accept an expired timer. This returns false if the timer was not found or if the timer was + * not expired. */ boolean accept(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - notFoundLocked("accept", arg); - return false; - } - mTimerService.accept(timer); - traceEnd(timer); - if (DEBUG) report(timer, "accept"); - timer.release(); - return true; - } + return mFeature.accept(arg); } /** - * Discard a timer in the framework-level handler. For whatever reason, the timer is no - * longer interesting. No statistics are collected. Return false if the time was not found. + * Discard an expired timer. This returns false if the timer was not found or if the timer was + * not expired. */ boolean discard(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - notFoundLocked("discard", arg); - return false; - } - mTimerService.discard(timer); - traceEnd(timer); - if (DEBUG) report(timer, "discard"); - timer.release(); - return true; - } + return mFeature.discard(arg); } /** diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 907069de8c97..147f8d1a1e32 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -580,7 +580,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> batteryStatsInternal); curDuration += curStart - lastUidBatteryUsageStartTs; try { - statsCommit.close(); + if (statsCommit != null) { + statsCommit.close(); + } else { + Slog.w(TAG, "Stat was null"); + } } catch (IOException e) { Slog.w(TAG, "Failed to close a stat"); } @@ -660,7 +664,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> } } try { - stats.close(); + if (stats != null) { + stats.close(); + } else { + Slog.w(TAG, "Stat was null"); + } } catch (IOException e) { Slog.w(TAG, "Failed to close a stat"); } @@ -684,7 +692,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> final BatteryUsageStats stats = statsList.get(0); for (int i = 1; i < statsList.size(); i++) { try { - statsList.get(i).close(); + if (statsList.get(i) != null) { + statsList.get(i).close(); + } else { + Slog.w(TAG, "Stat was null"); + } } catch (IOException e) { Slog.w(TAG, "Failed to close a stat in BatteryUsageStats List"); } diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index 928b5d8f3ca7..3cf4332349d7 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1205,6 +1205,8 @@ public class AppProfiler { trackerMemFactor = mService.mProcessStats.getMemFactorLocked(); } + mLastMemoryLevel = memFactor; + mLastNumProcesses = mService.mProcessList.getLruSizeLOSP(); if (mService.mConstants.USE_MODERN_TRIM) { // Modern trim is not sent based on lowmem state // Dispatch UI_HIDDEN to processes that need it @@ -1235,8 +1237,6 @@ public class AppProfiler { return false; } - mLastMemoryLevel = memFactor; - mLastNumProcesses = mService.mProcessList.getLruSizeLOSP(); if (memFactor != ADJ_MEM_FACTOR_NORMAL) { if (mLowRamStartTime == 0) { mLowRamStartTime = now; diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index 5d31d1545b8d..e07c2bcaaaed 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -106,6 +106,14 @@ class BroadcastProcessQueue { private boolean mTimeoutScheduled; /** + * Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically + * used when deciding if we should extend the soft ANR timeout. + * + * Required when Flags.anrTimerServiceEnabled is false. + */ + long lastCpuDelayTime; + + /** * Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before * dispatching the current broadcast to the receiver in this process. */ diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index eb219a8819c6..a42890707368 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -258,6 +258,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6; private static final int MSG_UID_STATE_CHANGED = 7; + // Required when Flags.anrTimerServiceEnabled is false. + private static final int MSG_DELIVERY_TIMEOUT_SOFT = 8; + private void enqueueUpdateRunningList() { mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST); mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST); @@ -271,6 +274,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { updateRunningList(); return true; } + // Required when Flags.anrTimerServiceEnabled is false. + case MSG_DELIVERY_TIMEOUT_SOFT: { + synchronized (mService) { + deliveryTimeoutSoftLocked((BroadcastProcessQueue) msg.obj, msg.arg1); + return true; + } + } case MSG_DELIVERY_TIMEOUT: { deliveryTimeout((BroadcastProcessQueue) msg.obj); return true; @@ -1030,7 +1040,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { queue.setTimeoutScheduled(true); final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT : mBgConstants.TIMEOUT); - mAnrTimer.start(queue, softTimeoutMillis); + startDeliveryTimeoutLocked(queue, softTimeoutMillis); } else { queue.setTimeoutScheduled(false); } @@ -1110,7 +1120,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // If we were trying to deliver a manifest broadcast, throw the error as we need // to try redelivering the broadcast to this receiver. if (receiver instanceof ResolveInfo) { - mAnrTimer.cancel(queue); + cancelDeliveryTimeoutLocked(queue); throw new BroadcastDeliveryFailedException(e); } finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, @@ -1159,6 +1169,41 @@ class BroadcastQueueModernImpl extends BroadcastQueue { r.resultTo = null; } + // Required when Flags.anrTimerServiceEnabled is false. + private void startDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue, + int softTimeoutMillis) { + if (mAnrTimer.serviceEnabled()) { + mAnrTimer.start(queue, softTimeoutMillis); + } else { + queue.lastCpuDelayTime = queue.app.getCpuDelayTime(); + mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler, + MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis); + } + } + + // Required when Flags.anrTimerServiceEnabled is false. + private void cancelDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue) { + mAnrTimer.cancel(queue); + if (!mAnrTimer.serviceEnabled()) { + mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue); + } + } + + // Required when Flags.anrTimerServiceEnabled is false. + private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue, + int softTimeoutMillis) { + if (queue.app != null) { + // Instead of immediately triggering an ANR, extend the timeout by + // the amount of time the process was runnable-but-waiting; we're + // only willing to do this once before triggering an hard ANR + final long cpuDelayTime = queue.app.getCpuDelayTime() - queue.lastCpuDelayTime; + final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis); + mAnrTimer.start(queue, hardTimeoutMillis); + } else { + deliveryTimeoutLocked(queue); + } + } + private void deliveryTimeout(@NonNull BroadcastProcessQueue queue) { synchronized (mService) { deliveryTimeoutLocked(queue); @@ -1292,7 +1337,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mAnrTimer.discard(queue); } } else if (queue.timeoutScheduled()) { - mAnrTimer.cancel(queue); + cancelDeliveryTimeoutLocked(queue); } // Given that a receiver just finished, check if the "waitingFor" conditions are met. diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 4b622f589adb..903cb7bcfaed 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -175,13 +175,15 @@ final class CoreSettingsObserver extends ContentObserver { TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT)); // Register all text aconfig flags. - for (String flag : TextFlags.TEXT_ACONFIGS_FLAGS) { + for (int i = 0; i < TextFlags.TEXT_ACONFIGS_FLAGS.length; i++) { + final String flag = TextFlags.TEXT_ACONFIGS_FLAGS[i]; + final boolean defaultValue = TextFlags.TEXT_ACONFIG_DEFAULT_VALUE[i]; sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( TextFlags.NAMESPACE, flag, TextFlags.getKeyForFlag(flag), boolean.class, - false)); // All aconfig flags are false by default. + defaultValue)); } // add other device configs here... } diff --git a/services/core/java/com/android/server/am/OomConnection.java b/services/core/java/com/android/server/am/OomConnection.java new file mode 100644 index 000000000000..17a4ce5b921c --- /dev/null +++ b/services/core/java/com/android/server/am/OomConnection.java @@ -0,0 +1,63 @@ +/* + * 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.am; + +import android.os.OomKillRecord; +import android.util.Slog; + +/** Connection to the out-of-memory (OOM) events' file */ +public final class OomConnection { + private static final String TAG = "OomConnection"; + + /** Connection listener interface */ + public interface OomConnectionListener { + + /** + * Callback function to handle the newest OOM kills. + * + * @param oomKills List of oom kills received from `waitOom()` + */ + void handleOomEvent(OomKillRecord[] oomKills); + } + + private final OomConnectionListener mOomListener; + + private final OomConnectionThread mOomConnectionThread; + + private static native OomKillRecord[] waitOom(); + + public OomConnection(OomConnectionListener listener) { + mOomListener = listener; + mOomConnectionThread = new OomConnectionThread(); + mOomConnectionThread.start(); + } + + private final class OomConnectionThread extends Thread { + public void run() { + while (true) { + OomKillRecord[] oom_kills = null; + try { + oom_kills = waitOom(); + mOomListener.handleOomEvent(oom_kills); + } catch (RuntimeException e) { + Slog.e(TAG, "failed waiting for OOM events: " + e); + break; + } + } + } + } +} diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index a97675f1d776..4572766371ec 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -97,6 +97,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.OomKillRecord; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -412,6 +413,8 @@ public final class ProcessList { private static LmkdConnection sLmkdConnection = null; + private static OomConnection sOomConnection = null; + private boolean mOomLevelsSet = false; private boolean mAppDataIsolationEnabled = false; @@ -855,6 +858,21 @@ public final class ProcessList { THREAD_PRIORITY_BACKGROUND, true /* allowIo */); sKillThread.start(); sKillHandler = new KillHandler(sKillThread.getLooper()); + sOomConnection = new OomConnection(new OomConnection.OomConnectionListener() { + @Override + public void handleOomEvent(OomKillRecord[] oomKills) { + for (OomKillRecord oomKill: oomKills) { + synchronized (mProcLock) { + noteAppKill( + oomKill.getPid(), + oomKill.getUid(), + ApplicationExitInfo.REASON_LOW_MEMORY, + ApplicationExitInfo.SUBREASON_OOM_KILL, + "oom"); + } + } + } + }); sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(), new LmkdConnection.LmkdConnectionListener() { @Override diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index d8a269598bdc..3771c05a294e 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1255,11 +1255,10 @@ class ProcessRecord implements WindowProcessListener { killProcessGroup = true; } if (killProcessGroup) { - if (async) { - ProcessList.killProcessGroup(uid, mPid); - } else { + if (!async) { Process.sendSignalToProcessGroup(uid, mPid, OsConstants.SIGKILL); } + ProcessList.killProcessGroup(uid, mPid); } } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 4a0bc4b9ca6c..1ba1f55af71b 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -131,13 +131,17 @@ public class SettingsToPropertiesMapper { "car_telemetry", "codec_fwk", "companion", + "content_protection", "context_hub", "core_experiments_team_internal", "core_graphics", "haptics", "hardware_backed_security_mainline", + "input", "machine_learning", + "mainline_sdk", "media_audio", + "media_drm", "media_solutions", "nfc", "pixel_audio_android", diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index b03cc6295b8d..26d99d843c7e 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -6,4 +6,12 @@ flag { description: "Utilize new OomAdjuster implementation" bug: "298055811" is_fixed_read_only: true -}
\ No newline at end of file +} + +flag { + name: "anr_timer_service_enabled" + namespace: "system_performance" + is_fixed_read_only: true + description: "Feature flag for the ANR timer service" + bug: "282428924" +} diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java index 241abafe6179..85acf707677a 100644 --- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java +++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java @@ -47,6 +47,12 @@ class AudioManagerShellCommand extends ShellCommand { return setEncodedSurroundMode(); case "get-encoded-surround-mode": return getEncodedSurroundMode(); + case "set-sound-dose-value": + return setSoundDoseValue(); + case "get-sound-dose-value": + return getSoundDoseValue(); + case "reset-sound-dose-timeout": + return resetSoundDoseTimeout(); } return 0; } @@ -66,6 +72,12 @@ class AudioManagerShellCommand extends ShellCommand { pw.println(" Sets the encoded surround sound mode to SURROUND_SOUND_MODE"); pw.println(" get-encoded-surround-mode"); pw.println(" Returns the encoded surround sound mode"); + pw.println(" set-sound-dose-value"); + pw.println(" Sets the current sound dose value"); + pw.println(" get-sound-dose-value"); + pw.println(" Returns the current sound dose value"); + pw.println(" reset-sound-dose-timeout"); + pw.println(" Resets the sound dose timeout used for momentary exposure"); } private int setSurroundFormatEnabled() { @@ -162,4 +174,46 @@ class AudioManagerShellCommand extends ShellCommand { getOutPrintWriter().println("Encoded surround mode: " + am.getEncodedSurroundMode()); return 0; } + + private int setSoundDoseValue() { + String soundDoseValueText = getNextArg(); + + if (soundDoseValueText == null) { + getErrPrintWriter().println("Error: no sound dose value specified"); + return 1; + } + + float soundDoseValue = 0.f; + try { + soundDoseValue = Float.parseFloat(soundDoseValueText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: wrong format specified for sound dose"); + return 1; + } + + if (soundDoseValue < 0) { + getErrPrintWriter().println("Error: invalid value of sound dose"); + return 1; + } + + final Context context = mService.mContext; + final AudioManager am = context.getSystemService(AudioManager.class); + am.setCsd(soundDoseValue); + return 0; + } + + private int getSoundDoseValue() { + final Context context = mService.mContext; + final AudioManager am = context.getSystemService(AudioManager.class); + getOutPrintWriter().println("Sound dose value: " + am.getCsd()); + return 0; + } + + private int resetSoundDoseTimeout() { + final Context context = mService.mContext; + final AudioManager am = context.getSystemService(AudioManager.class); + am.setCsd(-1.f); + getOutPrintWriter().println("Reset sound dose momentary exposure timeout"); + return 0; + } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0aa9cc11c432..3243385c3b18 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -36,7 +36,6 @@ import static android.os.Process.INVALID_UID; import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; - import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE; import static com.android.server.utils.EventLogger.Event.ALOGE; import static com.android.server.utils.EventLogger.Event.ALOGI; @@ -73,7 +72,6 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -267,6 +265,8 @@ public class AudioService extends IAudioService.Stub private final SettingsAdapter mSettings; private final AudioPolicyFacade mAudioPolicy; + private final MusicFxHelper mMusicFxHelper; + /** Debug audio mode */ protected static final boolean DEBUG_MODE = false; @@ -407,9 +407,15 @@ public class AudioService extends IAudioService.Stub private static final int MSG_CONFIGURATION_CHANGED = 54; private static final int MSG_BROADCAST_MASTER_MUTE = 55; - /** Messages handled by the {@link SoundDoseHelper}. */ + /** + * Messages handled by the {@link SoundDoseHelper}, do not exceed + * {@link MUSICFX_HELPER_MSG_START}. + */ /*package*/ static final int SAFE_MEDIA_VOLUME_MSG_START = 1000; + /** Messages handled by the {@link MusicFxHelper}. */ + /*package*/ static final int MUSICFX_HELPER_MSG_START = 1100; + // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -1304,6 +1310,8 @@ public class AudioService extends IAudioService.Stub 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */); mDisplayManager = context.getSystemService(DisplayManager.class); + + mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler); } private void initVolumeStreamStates() { @@ -9456,11 +9464,21 @@ public class AudioService extends IAudioService.Stub onConfigurationChanged(); break; + case MusicFxHelper.MSG_EFFECT_CLIENT_GONE: + mMusicFxHelper.handleMessage(msg); + break; + + case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA: + case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA_FORCED: + case SoundDoseHelper.MSG_PERSIST_SAFE_VOLUME_STATE: + case SoundDoseHelper.MSG_PERSIST_MUSIC_ACTIVE_MS: + case SoundDoseHelper.MSG_PERSIST_CSD_VALUES: + case SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION: + mSoundDoseHelper.handleMessage(msg); + break; + default: - if (msg.what >= SAFE_MEDIA_VOLUME_MSG_START) { - // msg could be for the SoundDoseHelper - mSoundDoseHelper.handleMessage(msg); - } + Log.e(TAG, "Unsupported msgId " + msg.what); } } } @@ -9695,7 +9713,7 @@ public class AudioService extends IAudioService.Stub } } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) || action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) { - handleAudioEffectBroadcast(context, intent); + mMusicFxHelper.handleAudioEffectBroadcast(context, intent); } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) { final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); final String[] suspendedPackages = @@ -9750,27 +9768,6 @@ public class AudioService extends IAudioService.Stub } } // end class AudioServiceUserRestrictionsListener - private void handleAudioEffectBroadcast(Context context, Intent intent) { - String target = intent.getPackage(); - if (target != null) { - Log.w(TAG, "effect broadcast already targeted to " + target); - return; - } - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - // TODO this should target a user-selected panel - List<ResolveInfo> ril = context.getPackageManager().queryBroadcastReceivers( - intent, 0 /* flags */); - if (ril != null && ril.size() != 0) { - ResolveInfo ri = ril.get(0); - if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) { - intent.setPackage(ri.activityInfo.packageName); - context.sendBroadcastAsUser(intent, UserHandle.ALL); - return; - } - } - Log.w(TAG, "couldn't find receiver package for effect intent"); - } - private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) { PackageManager pm = mContext.getPackageManager(); // Find the home activity of the user. It should not be killed to avoid expensive restart, diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java new file mode 100644 index 000000000000..6c0fef5f628d --- /dev/null +++ b/services/core/java/com/android/server/audio/MusicFxHelper.java @@ -0,0 +1,295 @@ +/* + * 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.audio; + +import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static com.android.server.audio.AudioService.MUSICFX_HELPER_MSG_START; + +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.IUidObserver; +import android.app.UidObserver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; +import android.media.AudioManager; +import android.media.audiofx.AudioEffect; +import android.os.Binder; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.audio.AudioService.AudioHandler; + +import java.util.ArrayList; +import java.util.List; + +/** + * MusicFx management. + * . + */ +public class MusicFxHelper { + private static final String TAG = "AS.MusicFxHelper"; + + @NonNull private final Context mContext; + + @NonNull private final AudioHandler mAudioHandler; + + // Synchronization UidSessionMap access between UidObserver and AudioServiceBroadcastReceiver. + private final Object mClientUidMapLock = new Object(); + + // The binder token identifying the UidObserver registration. + private IBinder mUidObserverToken = null; + + // Hashmap of UID and list of open sessions for this UID. + @GuardedBy("mClientUidMapLock") + private SparseArray<List<Integer>> mClientUidSessionMap = new SparseArray<>(); + + /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1; + + // UID observer for effect MusicFx clients + private final IUidObserver mEffectUidObserver = new UidObserver() { + @Override public void onUidGone(int uid, boolean disabled) { + Log.w(TAG, " send MSG_EFFECT_CLIENT_GONE"); + mAudioHandler.sendMessageAtTime( + mAudioHandler.obtainMessage(MSG_EFFECT_CLIENT_GONE, + uid /* arg1 */, 0 /* arg2 */, + null /* obj */), 0 /* delay */); + } + }; + + // BindService connection implementation, we don't need any implementation now + private ServiceConnection mMusicFxBindConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + Log.d(TAG, " service connected to " + name); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Log.d(TAG, " service disconnected from " + name); + } + }; + + MusicFxHelper(@NonNull Context context, @NonNull AudioHandler audioHandler) { + mContext = context; + mAudioHandler = audioHandler; + } + + /** + * Handle the broadcast {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and + * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. + * + * If the intent is {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION}: + * - If the MusicFx process is not running, call bindService with AUTO_CREATE to create. + * - If this is the first audio session in MusicFx, call set foreground service delegate. + * - If this is the first audio session for a given UID, add the UID into observer. + * + * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION}: + * - MusicFx will not be foreground delegated anymore. + * - The KeepAliveService of MusicFx will be unbound. + * - The UidObserver will be removed. + */ + public void handleAudioEffectBroadcast(Context context, Intent intent) { + String target = intent.getPackage(); + if (target != null) { + Log.w(TAG, "effect broadcast already targeted to " + target); + return; + } + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + final PackageManager pm = context.getPackageManager(); + // TODO this should target a user-selected panel + List<ResolveInfo> ril = pm.queryBroadcastReceivers(intent, 0 /* flags */); + if (ril != null && ril.size() != 0) { + ResolveInfo ri = ril.get(0); + final String senderPackageName = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME); + try { + final int senderUid = pm.getPackageUidAsUser(senderPackageName, + PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId()); + if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) { + intent.setPackage(ri.activityInfo.packageName); + synchronized (mClientUidMapLock) { + setMusicFxServiceWithObserver(context, intent, senderUid); + } + context.sendBroadcastAsUser(intent, UserHandle.ALL); + return; + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Not able to find UID from package: " + senderPackageName + " error: " + + e); + } + } + Log.w(TAG, "couldn't find receiver package for effect intent"); + } + + /** + * Handle the UidObserver onUidGone callback of MusicFx clients. + * All open audio sessions of this UID will be closed. + * If this is the last UID for MusicFx: + * - MusicFx will not be foreground delegated anymore. + * - The KeepAliveService of MusicFx will be unbound. + * - The UidObserver will be removed. + */ + public void handleEffectClientUidGone(int uid) { + synchronized (mClientUidMapLock) { + Log.w(TAG, " inside handle MSG_EFFECT_CLIENT_GONE"); + // Once the uid is no longer running, close all remain audio session(s) for this UID + if (mClientUidSessionMap.get(Integer.valueOf(uid)) != null) { + final List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(uid)); + Log.i(TAG, "UID " + uid + " gone, closing " + sessions.size() + " sessions"); + for (Integer session : sessions) { + Intent intent = new Intent( + AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, session); + setMusicFxServiceWithObserver(mContext, intent, uid); + Log.i(TAG, "Close session " + session + " of UID " + uid); + } + mClientUidSessionMap.remove(Integer.valueOf(uid)); + } + } + } + + @GuardedBy("mClientUidMapLock") + private void setMusicFxServiceWithObserver(Context context, Intent intent, int senderUid) { + PackageManager pm = context.getPackageManager(); + try { + final int audioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, + AudioManager.AUDIO_SESSION_ID_GENERATE); + if (AudioManager.AUDIO_SESSION_ID_GENERATE == audioSession) { + Log.e(TAG, "Intent missing audio session: " + audioSession); + return; + } + + // only apply to com.android.musicfx and KeepAliveService for now + final String musicFxPackageName = "com.android.musicfx"; + final String musicFxKeepAliveService = "com.android.musicfx.KeepAliveService"; + final int musicFxUid = pm.getPackageUidAsUser(musicFxPackageName, + PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId()); + + if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) { + List<Integer> sessions = new ArrayList<>(); + Log.d(TAG, "UID " + senderUid + ", open MusicFx session " + audioSession); + // start foreground service delegate and register UID observer with the first + // session of first UID open + if (0 == mClientUidSessionMap.size()) { + final int procState = ActivityManager.getService().getPackageProcessState( + musicFxPackageName, this.getClass().getPackage().getName()); + // if musicfx process not in binding state, call bindService with AUTO_CREATE + if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + Intent bindIntent = new Intent().setClassName(musicFxPackageName, + musicFxKeepAliveService); + context.bindServiceAsUser( + bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE, + UserHandle.of(getCurrentUserId())); + Log.i(TAG, "bindService to " + musicFxPackageName); + } + + Log.i(TAG, "Package " + musicFxPackageName + " uid " + musicFxUid + + " procState " + procState); + } else if (mClientUidSessionMap.get(Integer.valueOf(senderUid)) != null) { + sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid)); + if (sessions.contains(audioSession)) { + Log.e(TAG, "Audio session " + audioSession + " already exist for UID " + + senderUid + ", abort"); + return; + } + } + // first session of this UID + if (sessions.size() == 0) { + // call registerUidObserverForUids with the first UID and first session + if (mClientUidSessionMap.size() == 0 || mUidObserverToken == null) { + mUidObserverToken = ActivityManager.getService().registerUidObserverForUids( + mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE, + ActivityManager.PROCESS_STATE_UNKNOWN, null, new int[]{senderUid}); + Log.i(TAG, "UID " + senderUid + " registered to observer"); + } else { + // add UID to observer for each new UID + ActivityManager.getService().addUidToObserver(mUidObserverToken, TAG, + senderUid); + Log.i(TAG, "UID " + senderUid + " addeded to observer"); + } + } + + sessions.add(Integer.valueOf(audioSession)); + mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions); + } else { + if (mClientUidSessionMap.get(senderUid) != null) { + Log.d(TAG, "UID " + senderUid + ", close MusicFx session " + audioSession); + List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid)); + sessions.remove(Integer.valueOf(audioSession)); + if (0 == sessions.size()) { + mClientUidSessionMap.remove(Integer.valueOf(senderUid)); + } else { + mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions); + } + + // stop foreground service delegate and unregister UID observer with the + // last session of last UID close + if (0 == mClientUidSessionMap.size()) { + ActivityManager.getService().unregisterUidObserver(mEffectUidObserver); + mClientUidSessionMap.clear(); + context.unbindService(mMusicFxBindConnection); + Log.i(TAG, " remove all sessions, unregister UID observer, and unbind " + + musicFxPackageName); + } + } else { + // if the audio session already closed, print an error + Log.e(TAG, "UID " + senderUid + " close audio session " + audioSession + + " which does not exist"); + } + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Not able to find UID from package: " + e); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException " + e + " with handling intent"); + } + } + + private int getCurrentUserId() { + final long ident = Binder.clearCallingIdentity(); + try { + UserInfo currentUser = ActivityManager.getService().getCurrentUser(); + return currentUser.id; + } catch (RemoteException e) { + // Activity manager not running, nothing we can do assume user 0. + } finally { + Binder.restoreCallingIdentity(ident); + } + return UserHandle.USER_SYSTEM; + } + + /*package*/ void handleMessage(Message msg) { + switch (msg.what) { + case MSG_EFFECT_CLIENT_GONE: + Log.w(TAG, " handle MSG_EFFECT_CLIENT_GONE"); + handleEffectClientUidGone(msg.arg1 /* uid */); + break; + default: + Log.e(TAG, "Unexpected msg to handle in MusicFxHelper: " + msg.what); + break; + } + } + +} diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 81365bfbf2a6..6c5f3e74b0d2 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -108,11 +108,11 @@ public class SoundDoseHelper { private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2; // confirmed private static final int SAFE_MEDIA_VOLUME_ACTIVE = 3; // unconfirmed - private static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1; - private static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2; - private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3; - private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4; - private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5; + /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1; + /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2; + /*package*/ static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3; + /*package*/ static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4; + /*package*/ static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5; /*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 6; private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours 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 f74b45cbdb0e..e42b66472cbe 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 @@ -875,6 +875,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void simulateVhalFingerDown(int userId, int sensorId) { Slog.d(getTag(), "Simulate virtual HAL finger down event"); final AidlSession session = mFingerprintSensors.get(sensorId).getSessionForUser(userId); + if (session == null) { + Slog.e(getTag(), "no existing hal session found - aborting"); + return; + } final PointerContext pc = new PointerContext(); try { session.getSession().onPointerDownWithContext(pc); diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 9805fd3d1316..333f62a81e94 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -1845,7 +1845,7 @@ public class SyncStorageEngine { private void parseListenForTickles(TypedXmlPullParser parser) { int userId = 0; try { - parser.getAttributeInt(null, XML_ATTR_USER); + userId = parser.getAttributeInt(null, XML_ATTR_USER); } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the user for listen-for-tickles", e); } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 9e92c8d7342d..cfbe0c69b320 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -60,6 +60,9 @@ import com.android.server.display.config.LuxThrottling; import com.android.server.display.config.NitsMap; import com.android.server.display.config.NonNegativeFloatToFloatPoint; import com.android.server.display.config.Point; +import com.android.server.display.config.PowerThrottlingConfig; +import com.android.server.display.config.PowerThrottlingMap; +import com.android.server.display.config.PowerThrottlingPoint; import com.android.server.display.config.PredefinedBrightnessLimitNames; import com.android.server.display.config.RefreshRateConfigs; import com.android.server.display.config.RefreshRateRange; @@ -139,6 +142,30 @@ import javax.xml.datatype.DatatypeConfigurationException; * </screenBrightnessMap> * * <screenBrightnessDefault>0.65</screenBrightnessDefault> + * <powerThrottlingConfig> + * <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed> + * <pollingWindowMillis>15</pollingWindowMillis> + * <powerThrottlingMap> + * <powerThrottlingPoint> + * <thermalStatus>severe</thermalStatus> + * <powerQuotaMilliWatts>200.6</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * <powerThrottlingPoint> + * <thermalStatus>critical</thermalStatus> + * <powerQuotaMilliWatts>300</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * </powerThrottlingMap> + * <powerThrottlingMap id="id_2"> // optional attribute, leave blank for default + * <powerThrottlingPoint> + * <thermalStatus>moderate</thermalStatus> + * <powerQuotaMilliWatts>400</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * <powerThrottlingPoint> + * <thermalStatus>severe</thermalStatus> + * <powerQuotaMilliWatts>250</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * </powerThrottlingMap> + * </powerThrottlingConfig> * * <thermalThrottling> * <brightnessThrottlingMap> @@ -669,6 +696,8 @@ public class DisplayDeviceConfig { private List<String> mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; + @Nullable + private PowerThrottlingConfigData mPowerThrottlingConfigData; private DensityMapping mDensityMapping; private String mLoadedFrom = null; private Spline mSdrToHdrRatioSpline; @@ -781,6 +810,9 @@ public class DisplayDeviceConfig { private final HashMap<String, ThermalBrightnessThrottlingData> mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>(); + private final HashMap<String, PowerThrottlingData> + mPowerThrottlingDataMapByThrottlingId = new HashMap<>(); + private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>> mRefreshRateThrottlingMap = new HashMap<>(); @@ -1458,6 +1490,14 @@ public class DisplayDeviceConfig { return hbmData; } + /** + * @return Power throttling configuration data for the display. + */ + @Nullable + public PowerThrottlingConfigData getPowerThrottlingConfigData() { + return mPowerThrottlingConfigData; + } + @NonNull public Map<BrightnessLimitMapType, Map<Float, Float>> getLuxThrottlingData() { return mLuxThrottlingData; @@ -1491,6 +1531,14 @@ public class DisplayDeviceConfig { } /** + * @return power throttling configuration data for this display, for each throttling id. + **/ + public HashMap<String, PowerThrottlingData> + getPowerThrottlingDataMapByThrottlingId() { + return mPowerThrottlingDataMapByThrottlingId; + } + + /** * @return Auto brightness darkening light debounce */ public long getAutoBrightnessDarkeningLightDebounce() { @@ -1702,6 +1750,9 @@ public class DisplayDeviceConfig { + ", mThermalBrightnessThrottlingDataMapByThrottlingId=" + mThermalBrightnessThrottlingDataMapByThrottlingId + "\n" + + ", mPowerThrottlingDataMapByThrottlingId=" + + mPowerThrottlingDataMapByThrottlingId + + "\n" + "mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease @@ -1853,6 +1904,7 @@ public class DisplayDeviceConfig { loadBrightnessConstraintsFromConfigXml(); loadBrightnessMap(config); loadThermalThrottlingConfig(config); + loadPowerThrottlingConfigData(config); loadHighBrightnessModeData(config); loadLuxThrottling(config); loadQuirks(config); @@ -2171,6 +2223,59 @@ public class DisplayDeviceConfig { } } + private boolean loadPowerThrottlingMaps(PowerThrottlingConfig throttlingConfig) { + final List<PowerThrottlingMap> maps = throttlingConfig.getPowerThrottlingMap(); + if (maps == null || maps.isEmpty()) { + Slog.i(TAG, "No power throttling map found"); + return false; + } + + for (PowerThrottlingMap map : maps) { + final List<PowerThrottlingPoint> points = map.getPowerThrottlingPoint(); + // At least 1 point is guaranteed by the display device config schema + List<PowerThrottlingData.ThrottlingLevel> throttlingLevels = + new ArrayList<>(points.size()); + + boolean badConfig = false; + for (PowerThrottlingPoint point : points) { + ThermalStatus status = point.getThermalStatus(); + if (!thermalStatusIsValid(status)) { + badConfig = true; + break; + } + + throttlingLevels.add(new PowerThrottlingData.ThrottlingLevel( + convertThermalStatus(status), + point.getPowerQuotaMilliWatts().floatValue())); + } + + if (!badConfig) { + String id = map.getId() == null ? DEFAULT_ID : map.getId(); + if (mPowerThrottlingDataMapByThrottlingId.containsKey(id)) { + throw new RuntimeException("Power throttling data with ID " + id + + " already exists"); + } + mPowerThrottlingDataMapByThrottlingId.put(id, + PowerThrottlingData.create(throttlingLevels)); + } + } + return true; + } + + private void loadPowerThrottlingConfigData(DisplayConfiguration config) { + final PowerThrottlingConfig powerThrottlingCfg = config.getPowerThrottlingConfig(); + if (powerThrottlingCfg == null) { + return; + } + if (!loadPowerThrottlingMaps(powerThrottlingCfg)) { + return; + } + float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue(); + int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue(); + mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap, + pollingWindowMillis); + } + private void loadRefreshRateSetting(DisplayConfiguration config) { final RefreshRateConfigs refreshRateConfigs = (config == null) ? null : config.getRefreshRate(); @@ -3379,6 +3484,148 @@ public class DisplayDeviceConfig { } /** + * Container for Power throttling configuration data. + * TODO(b/302814899): extract to separate class. + */ + public static class PowerThrottlingConfigData { + /** Lowest brightness cap allowed for this device. */ + public final float brightnessLowestCapAllowed; + /** Time window for polling power in seconds. */ + public final int pollingWindowMillis; + public PowerThrottlingConfigData(float brightnessLowestCapAllowed, + int pollingWindowMillis) { + this.brightnessLowestCapAllowed = brightnessLowestCapAllowed; + this.pollingWindowMillis = pollingWindowMillis; + } + + @Override + public String toString() { + return "PowerThrottlingConfigData{" + + "brightnessLowestCapAllowed: " + + brightnessLowestCapAllowed + + ", pollingWindowMillis: " + pollingWindowMillis + + "} "; + } + } + + /** + * Container for power throttling data. + * TODO(b/302814899): extract to separate class and unify with ThermalBrightnessThrottlingData. + */ + public static class PowerThrottlingData { + public List<ThrottlingLevel> throttlingLevels; + + /** + * thermal status to power quota mapping. + */ + public static class ThrottlingLevel { + public @PowerManager.ThermalStatus int thermalStatus; + public float powerQuotaMilliWatts; + + public ThrottlingLevel( + @PowerManager.ThermalStatus int thermalStatus, float powerQuotaMilliWatts) { + this.thermalStatus = thermalStatus; + this.powerQuotaMilliWatts = powerQuotaMilliWatts; + } + + @Override + public String toString() { + return "[" + thermalStatus + "," + powerQuotaMilliWatts + "]"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ThrottlingLevel)) { + return false; + } + ThrottlingLevel otherThrottlingLevel = (ThrottlingLevel) obj; + + return otherThrottlingLevel.thermalStatus == this.thermalStatus + && otherThrottlingLevel.powerQuotaMilliWatts == this.powerQuotaMilliWatts; + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + thermalStatus; + result = 31 * result + Float.hashCode(powerQuotaMilliWatts); + return result; + } + } + + + /** + * Creates multiple temperature based throttling levels of power quota. + */ + public static PowerThrottlingData create( + List<ThrottlingLevel> throttlingLevels) { + if (throttlingLevels == null || throttlingLevels.size() == 0) { + Slog.e(TAG, "PowerThrottlingData received null or empty throttling levels"); + return null; + } + + ThrottlingLevel prevLevel = throttlingLevels.get(0); + final int numLevels = throttlingLevels.size(); + for (int i = 1; i < numLevels; i++) { + ThrottlingLevel thisLevel = throttlingLevels.get(i); + + if (thisLevel.thermalStatus <= prevLevel.thermalStatus) { + Slog.e(TAG, "powerThrottlingMap must be strictly increasing, ignoring " + + "configuration. ThermalStatus " + thisLevel.thermalStatus + " <= " + + prevLevel.thermalStatus); + return null; + } + + if (thisLevel.powerQuotaMilliWatts >= prevLevel.powerQuotaMilliWatts) { + Slog.e(TAG, "powerThrottlingMap must be strictly decreasing, ignoring " + + "configuration. powerQuotaMilliWatts " + + thisLevel.powerQuotaMilliWatts + " >= " + + prevLevel.powerQuotaMilliWatts); + return null; + } + + prevLevel = thisLevel; + } + return new PowerThrottlingData(throttlingLevels); + } + + @Override + public String toString() { + return "PowerThrottlingData{" + + "throttlingLevels:" + throttlingLevels + + "} "; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof PowerThrottlingData)) { + return false; + } + + PowerThrottlingData otherData = (PowerThrottlingData) obj; + return throttlingLevels.equals(otherData.throttlingLevels); + } + + @Override + public int hashCode() { + return throttlingLevels.hashCode(); + } + + @VisibleForTesting + PowerThrottlingData(List<ThrottlingLevel> inLevels) { + throttlingLevels = new ArrayList<>(inLevels.size()); + for (ThrottlingLevel level : inLevels) { + throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, + level.powerQuotaMilliWatts)); + } + } + } + + /** * Container for brightness throttling data. */ public static class ThermalBrightnessThrottlingData { diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java index 652e6cfd67be..39f0b13f716a 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java +++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java @@ -105,16 +105,21 @@ public class HdrClamper { public void resetHdrConfig(HdrBrightnessData data, int width, int height, float minimumHdrPercentOfScreen, IBinder displayToken) { mHdrBrightnessData = data; - mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen; + mHdrListener.mHdrMinPixels = minimumHdrPercentOfScreen <= 0 ? -1 + : (float) (width * height) * minimumHdrPercentOfScreen; if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe mHdrListener.unregister(mRegisteredDisplayToken); mHdrVisible = false; + mRegisteredDisplayToken = null; } - if (displayToken != null) { // new token not null, subscribe + // new token not null and hdr min % of the screen is set, subscribe. + // e.g. for virtual display, HBM data will be missing and HdrListener + // should not be registered + if (displayToken != null && mHdrListener.mHdrMinPixels > 0) { mHdrListener.register(displayToken); + mRegisteredDisplayToken = displayToken; } - mRegisteredDisplayToken = displayToken; } recalculateBrightnessCap(data, mAmbientLux, mHdrVisible); } diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index d8831fac5c79..03b0cfc3d844 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -78,6 +78,7 @@ import com.android.internal.util.DumpUtils; import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; @@ -148,10 +149,12 @@ public final class ColorDisplayService extends SystemService { 1f, 1f, 1f, 1f }; + private final DisplayManagerFlags mDisplayManagerFlags = new DisplayManagerFlags(); + @VisibleForTesting final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController( - LocalServices.getService(DisplayManagerInternal.class)); + LocalServices.getService(DisplayManagerInternal.class), mDisplayManagerFlags); private final NightDisplayTintController mNightDisplayTintController = new NightDisplayTintController(); private final TintController mGlobalSaturationTintController = @@ -716,15 +719,16 @@ public final class ColorDisplayService extends SystemService { tintController.computeMatrixForCct(to)); tintController.setAppliedCct(to); } else { + final long duration = tintController.getTransitionDurationMilliseconds(to > from); Slog.d(TAG, tintController.getClass().getSimpleName() + " animation started: toCct=" - + to + " fromCct=" + from); + + to + " fromCct=" + from + " with duration=" + duration); ValueAnimator valueAnimator = ValueAnimator.ofInt(from, to); tintController.setAnimator(valueAnimator); final CctEvaluator evaluator = tintController.getEvaluator(); if (evaluator != null) { valueAnimator.setEvaluator(evaluator); } - valueAnimator.setDuration(tintController.getTransitionDurationMilliseconds()); + valueAnimator.setDuration(duration); valueAnimator.setInterpolator(AnimationUtils.loadInterpolator( getContext(), android.R.interpolator.linear)); valueAnimator.addUpdateListener((ValueAnimator animator) -> { diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java index bf0139f347f8..b9fd1d070cbf 100644 --- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java +++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java @@ -34,6 +34,7 @@ import android.view.SurfaceControl.DisplayPrimaries; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.feature.DisplayManagerFlags; import java.io.PrintWriter; @@ -65,6 +66,8 @@ final class DisplayWhiteBalanceTintController extends ColorTemperatureTintContro boolean mSetUp = false; private final float[] mMatrixDisplayWhiteBalance = new float[16]; private long mTransitionDuration; + private long mTransitionDurationIncrease; + private long mTransitionDurationDecrease; private Boolean mIsAvailable; // This feature becomes disallowed if the device is in an unsupported strong/light state. private boolean mIsAllowed = true; @@ -74,8 +77,12 @@ final class DisplayWhiteBalanceTintController extends ColorTemperatureTintContro private final DisplayManagerInternal mDisplayManagerInternal; - DisplayWhiteBalanceTintController(DisplayManagerInternal dm) { + private final DisplayManagerFlags mDisplayManagerFlags; + + DisplayWhiteBalanceTintController(DisplayManagerInternal dm, + DisplayManagerFlags displayManagerFlags) { mDisplayManagerInternal = dm; + mDisplayManagerFlags = displayManagerFlags; } @Override @@ -139,6 +146,16 @@ final class DisplayWhiteBalanceTintController extends ColorTemperatureTintContro mTransitionDuration = res.getInteger( R.integer.config_displayWhiteBalanceTransitionTime); + if (!mDisplayManagerFlags.isAdaptiveTone2Enabled()) { + mTransitionDurationDecrease = mTransitionDuration; + mTransitionDurationIncrease = mTransitionDuration; + } else { + mTransitionDurationIncrease = res.getInteger( + R.integer.config_displayWhiteBalanceTransitionTimeIncrease); + mTransitionDurationDecrease = res.getInteger( + R.integer.config_displayWhiteBalanceTransitionTimeDecrease); + } + int[] cctRangeMinimums = res.getIntArray( R.array.config_displayWhiteBalanceDisplayRangeMinimums); int[] steps = res.getIntArray(R.array.config_displayWhiteBalanceDisplaySteps); @@ -323,6 +340,11 @@ final class DisplayWhiteBalanceTintController extends ColorTemperatureTintContro } @Override + public long getTransitionDurationMilliseconds(boolean isIncreasing) { + return isIncreasing ? mTransitionDurationIncrease : mTransitionDurationDecrease; + } + + @Override public void dump(PrintWriter pw) { synchronized (mLock) { pw.println(" mSetUp = " + mSetUp); @@ -348,6 +370,9 @@ final class DisplayWhiteBalanceTintController extends ColorTemperatureTintContro pw.println(" mMatrixDisplayWhiteBalance = " + matrixToString(mMatrixDisplayWhiteBalance, 4)); pw.println(" mIsAllowed = " + mIsAllowed); + pw.println(" mTransitionDuration = " + mTransitionDuration); + pw.println(" mTransitionDurationIncrease = " + mTransitionDurationIncrease); + pw.println(" mTransitionDurationDecrease = " + mTransitionDurationDecrease); } } diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java index 384333a574b5..716661dd6c3c 100644 --- a/services/core/java/com/android/server/display/color/TintController.java +++ b/services/core/java/com/android/server/display/color/TintController.java @@ -75,6 +75,10 @@ abstract class TintController { return TRANSITION_DURATION; } + public long getTransitionDurationMilliseconds(boolean direction) { + return TRANSITION_DURATION; + } + /** * Dump debug information. */ diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index b6273e1daf82..a5e3b70802ef 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -47,25 +47,21 @@ public class DisplayManagerFlags { Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1, Flags::enableAdaptiveToneImprovements1); + private final FlagState mAdaptiveToneImprovements2 = new FlagState( + Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_2, + Flags::enableAdaptiveToneImprovements2); + private final FlagState mDisplayOffloadFlagState = new FlagState( Flags.FLAG_ENABLE_DISPLAY_OFFLOAD, Flags::enableDisplayOffload); - private final FlagState mDisplayResolutionRangeVotingState = new FlagState( - Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING, - Flags::enableDisplayResolutionRangeVoting); - - private final FlagState mUserPreferredModeVoteState = new FlagState( - Flags.FLAG_ENABLE_USER_PREFERRED_MODE_VOTE, - Flags::enableUserPreferredModeVote); - private final FlagState mExternalDisplayLimitModeState = new FlagState( Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY, Flags::enableModeLimitForExternalDisplay); - private final FlagState mDisplaysRefreshRatesSynchronizationState = new FlagState( - Flags.FLAG_ENABLE_DISPLAYS_REFRESH_RATES_SYNCHRONIZATION, - Flags::enableDisplaysRefreshRatesSynchronization); + private final FlagState mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState = new FlagState( + Flags.FLAG_BACK_UP_SMOOTH_DISPLAY_AND_FORCE_PEAK_REFRESH_RATE, + Flags::backUpSmoothDisplayAndForcePeakRefreshRate); /** Returns whether connected display management is enabled or not. */ public boolean isConnectedDisplayManagementEnabled() { @@ -88,9 +84,16 @@ public class DisplayManagerFlags { return mAdaptiveToneImprovements1.isEnabled(); } + /** + * Returns whether adaptive tone improvements are enabled + */ + public boolean isAdaptiveTone2Enabled() { + return mAdaptiveToneImprovements2.isEnabled(); + } + /** Returns whether resolution range voting feature is enabled or not. */ public boolean isDisplayResolutionRangeVotingEnabled() { - return mDisplayResolutionRangeVotingState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** @@ -98,7 +101,7 @@ public class DisplayManagerFlags { * {@link com.android.server.display.mode.DisplayModeDirector} */ public boolean isUserPreferredModeVoteEnabled() { - return mUserPreferredModeVoteState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** @@ -112,7 +115,7 @@ public class DisplayManagerFlags { * @return Whether displays refresh rate synchronization is enabled. */ public boolean isDisplaysRefreshRatesSynchronizationEnabled() { - return mDisplaysRefreshRatesSynchronizationState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** Returns whether displayoffload is enabled on not */ @@ -120,6 +123,10 @@ public class DisplayManagerFlags { return mDisplayOffloadFlagState.isEnabled(); } + public boolean isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled() { + return mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState.isEnabled(); + } + private static class FlagState { private final String mName; @@ -133,7 +140,6 @@ public class DisplayManagerFlags { mFlagFunction = flagFunction; } - // TODO(b/297159910): Simplify using READ-ONLY flags when available. private boolean isEnabled() { if (mEnabledSet) { if (DEBUG) { @@ -150,19 +156,13 @@ public class DisplayManagerFlags { } private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) { + boolean flagValue = flagFunction.get(); // TODO(b/299462337) Remove when the infrastructure is ready. - if ((Build.IS_ENG || Build.IS_USERDEBUG) - && SystemProperties.getBoolean("persist.sys." + flagName, false)) { - return true; - } - try { - return flagFunction.get(); - } catch (Throwable ex) { - if (DEBUG) { - Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex); - } - return false; + if (Build.IS_ENG || Build.IS_USERDEBUG) { + return SystemProperties.getBoolean("persist.sys." + flagName + "-override", + flagValue); } + return flagValue; } } } diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 542f26cbec69..3d203fb7427f 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -35,6 +35,14 @@ flag { } flag { + name: "enable_adaptive_tone_improvements_2" + namespace: "display_manager" + description: "Feature flag for Further Adaptive Tone Improvements" + bug: "294762632" + is_fixed_read_only: true +} + +flag { name: "enable_display_resolution_range_voting" namespace: "display_manager" description: "Feature flag to enable voting for ranges of resolutions" @@ -72,3 +80,11 @@ flag { bug: "299521647" is_fixed_read_only: true } + +flag { + name: "back_up_smooth_display_and_force_peak_refresh_rate" + namespace: "display_manager" + description: "Feature flag for backing up Smooth Display and Force Peak Refresh Rate" + bug: "211737588" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 71ea8cc30405..ca23844044ca 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -22,6 +22,7 @@ import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT; import static android.view.Display.Mode.INVALID_MODE_ID; import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE; +import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay; import android.annotation.IntegerRes; import android.annotation.NonNull; @@ -176,6 +177,8 @@ public class DisplayModeDirector { private final boolean mIsDisplaysRefreshRatesSynchronizationEnabled; + private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled; + public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, @NonNull DisplayManagerFlags displayManagerFlags) { this(context, handler, new RealInjector(context), displayManagerFlags); @@ -191,6 +194,8 @@ public class DisplayModeDirector { .isExternalDisplayLimitModeEnabled(); mIsDisplaysRefreshRatesSynchronizationEnabled = displayManagerFlags .isDisplaysRefreshRatesSynchronizationEnabled(); + mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags + .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled(); mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); mInjector = injector; @@ -1193,8 +1198,7 @@ public class DisplayModeDirector { public void observe() { final ContentResolver cr = mContext.getContentResolver(); mInjector.registerPeakRefreshRateObserver(cr, this); - cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, - UserHandle.USER_SYSTEM); + mInjector.registerMinRefreshRateObserver(cr, this); cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/, @@ -1292,10 +1296,34 @@ public class DisplayModeDirector { private void updateRefreshRateSettingLocked() { final ContentResolver cr = mContext.getContentResolver(); + float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext); + float minRefreshRate = Settings.System.getFloatForUser(cr, Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId()); + if (Float.isInfinite(minRefreshRate)) { + // Infinity means that we want the highest possible refresh rate + minRefreshRate = highestRefreshRate; + + if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) { + // The flag had been turned off, we need to restore the original value + Settings.System.putFloatForUser(cr, + Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId()); + } + } + float peakRefreshRate = Settings.System.getFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId()); + if (Float.isInfinite(peakRefreshRate)) { + // Infinity means that we want the highest possible refresh rate + peakRefreshRate = highestRefreshRate; + + if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) { + // The flag had been turned off, we need to restore the original value + Settings.System.putFloatForUser(cr, + Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId()); + } + } + updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); } @@ -3082,6 +3110,7 @@ public class DisplayModeDirector { interface Injector { Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); + Uri MIN_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE); @NonNull DeviceConfigInterface getDeviceConfig(); @@ -3089,6 +3118,9 @@ public class DisplayModeDirector { void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer); + void registerMinRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener, Handler handler); @@ -3140,6 +3172,13 @@ public class DisplayModeDirector { } @Override + public void registerMinRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.registerContentObserver(MIN_REFRESH_RATE_URI, false /*notifyDescendants*/, + observer, UserHandle.USER_SYSTEM); + } + + @Override public void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) { getDisplayManager().registerDisplayListener(listener, handler); diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index cd3d2f031455..0f40ca082663 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -619,8 +619,8 @@ final class UpdatableFontDir { } return new FontConfig( - config.getFontFamilies(), config.getAliases(), mergedFamilies, mLastModifiedMillis, - mConfigVersion); + config.getFontFamilies(), config.getAliases(), mergedFamilies, + config.getLocaleFallbackCustomizations(), mLastModifiedMillis, mConfigVersion); } private PersistentSystemFontConfig.Config readPersistentConfig() { diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java index 2ede56dcecd9..a2c8748a9142 100644 --- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java +++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java @@ -62,10 +62,10 @@ class GestureMonitorSpyWindow { mWindowHandle.ownerUid = uid; mWindowHandle.scaleFactor = 1.0f; mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); - mWindowHandle.inputConfig = - InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY; + mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SPY; final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR); t.setPosition(mInputSurface, 0, 0); diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 6b399def4d73..2533e0297679 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -117,6 +117,7 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.input.InputManagerInternal.LidSwitchCallback; +import com.android.server.input.debug.FocusEventDebugView; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.policy.WindowManagerPolicy; diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 0eb620f3f4df..bad6bf0f0141 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -71,6 +71,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.InputMethodSubtypeHandle; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; @@ -99,7 +100,7 @@ import java.util.stream.Stream; * * @hide */ -final class KeyboardLayoutManager implements InputManager.InputDeviceListener { +class KeyboardLayoutManager implements InputManager.InputDeviceListener { private static final String TAG = "KeyboardLayoutManager"; @@ -1295,7 +1296,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } @SuppressLint("MissingPermission") - private List<ImeInfo> getImeInfoListForLayoutMapping() { + @VisibleForTesting + public List<ImeInfo> getImeInfoListForLayoutMapping() { List<ImeInfo> imeInfoList = new ArrayList<>(); UserManager userManager = Objects.requireNonNull( mContext.getSystemService(UserManager.class)); @@ -1402,7 +1404,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } - private static class ImeInfo { + @VisibleForTesting + public static class ImeInfo { @UserIdInt int mUserId; @NonNull InputMethodSubtypeHandle mImeSubtypeHandle; @Nullable InputMethodSubtype mImeSubtype; diff --git a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java index 67c221f77037..2b21e49a4e03 100644 --- a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java +++ b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.input; +package com.android.server.input.debug; import android.view.Display; import android.view.InputEvent; @@ -22,6 +22,7 @@ import android.view.InputEventReceiver; import android.view.MotionEvent; import com.android.server.UiThread; +import com.android.server.input.InputManagerService; /** * Receives input events before they are dispatched and reports them to FocusEventDebugView. diff --git a/services/core/java/com/android/server/input/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java index 4b8fabde7d35..6eec0dee9152 100644 --- a/services/core/java/com/android/server/input/FocusEventDebugView.java +++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.input; +package com.android.server.input.debug; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.util.TypedValue.COMPLEX_UNIT_SP; @@ -24,11 +24,9 @@ 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; @@ -40,7 +38,6 @@ 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; @@ -50,19 +47,17 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.input.InputManagerService; 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 RelativeLayout { +public class FocusEventDebugView extends RelativeLayout { private static final String TAG = FocusEventDebugView.class.getSimpleName(); @@ -112,7 +107,7 @@ class FocusEventDebugView extends RelativeLayout { mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm); } - FocusEventDebugView(Context c, InputManagerService service) { + public FocusEventDebugView(Context c, InputManagerService service) { this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c)); } @@ -149,11 +144,13 @@ class FocusEventDebugView extends RelativeLayout { return super.dispatchKeyEvent(event); } + /** Determines whether to show the key presses visualization. */ @AnyThread public void updateShowKeyPresses(boolean enabled) { post(() -> handleUpdateShowKeyPresses(enabled)); } + /** Determines whether to show the rotary input visualization. */ @AnyThread public void updateShowRotaryInput(boolean enabled) { post(() -> handleUpdateShowRotaryInput(enabled)); @@ -358,13 +355,6 @@ class FocusEventDebugView extends RelativeLayout { 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[]{ @@ -473,376 +463,4 @@ class FocusEventDebugView extends RelativeLayout { 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/debug/RotaryInputGraphView.java b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java new file mode 100644 index 000000000000..95635d925855 --- /dev/null +++ b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java @@ -0,0 +1,342 @@ +/* + * 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.debug; + +import static android.util.TypedValue.COMPLEX_UNIT_SP; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewConfiguration; + +import java.util.Iterator; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +/** + * 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. + */ +public 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 = Locale.getDefault(); + 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; + + public 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(); + + 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. + */ + public 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; + } + } + + public float getFrameCenterPosition() { + return mFrameCenterPosition; + } + + /** + * 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); + } + + /** + * 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/debug/RotaryInputValueView.java b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java new file mode 100644 index 000000000000..9fadac57cef9 --- /dev/null +++ b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java @@ -0,0 +1,103 @@ +/* + * 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.debug; + +import static android.util.TypedValue.COMPLEX_UNIT_SP; + +import android.content.Context; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Typeface; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.ViewConfiguration; +import android.widget.TextView; + +import com.android.internal.R; + +import java.util.Locale; + +/** + * Draws the most recent rotary input value and indicates whether the source is active. + */ +public 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; + private final Locale mDefaultLocale = Locale.getDefault(); + + public 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); + } + + /** Updates the shown text with the formatted value. */ + public void updateValue(float value) { + removeCallbacks(mUpdateActivityStatusCallback); + + setText(getFormattedValue(value * mScaledVerticalScrollFactor)); + + updateActivityStatus(true); + postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION); + } + + /** Updates whether or not there's active rotary input. */ + public void updateActivityStatus(boolean active) { + if (active) { + setTextColor(ACTIVE_TEXT_COLOR); + getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER); + } else { + setTextColor(INACTIVE_TEXT_COLOR); + getBackground().clearColorFilter(); + } + } + + private String getFormattedValue(float value) { + return String.format(mDefaultLocale, "%s%.1f", value < 0 ? "-" : "+", Math.abs(value)); + } + + /** + * 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); + } +} diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index 7726f40fa2ae..dbbbed31df76 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -57,13 +57,13 @@ final class HandwritingEventReceiverSurface { InputConfig.NOT_FOCUSABLE | InputConfig.NOT_TOUCHABLE | InputConfig.SPY - | InputConfig.INTERCEPTS_STYLUS - | InputConfig.TRUSTED_OVERLAY; + | InputConfig.INTERCEPTS_STYLUS; // Configure the surface to receive stylus events across the entire display. mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE); t.setPosition(mInputSurface, 0, 0); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 93c66a19c89d..08cf3f775cc3 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -1431,13 +1431,17 @@ public class ContextHubService extends IContextHubService.Stub { mContextHubWrapper.onBtMainSettingChanged(btEnabled); } } else { - Log.d(TAG, "BT adapter not available. Defaulting to disabled"); - if (forceUpdate || mIsBtMainEnabled) { - mIsBtMainEnabled = false; + Log.d(TAG, "BT adapter not available. Getting permissions from user settings"); + boolean btEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.BLUETOOTH_ON, 0) == 1; + boolean btScanEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1; + if (forceUpdate || mIsBtMainEnabled != btEnabled) { + mIsBtMainEnabled = btEnabled; mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled); } - if (forceUpdate || mIsBtScanningEnabled) { - mIsBtScanningEnabled = false; + if (forceUpdate || mIsBtScanningEnabled != btScanEnabled) { + mIsBtScanningEnabled = btScanEnabled; mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled); } } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index c9528d8257c4..9dec1dff4cf0 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -63,6 +63,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.media.flags.Flags; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; @@ -161,11 +162,13 @@ class MediaRouter2ServiceImpl { mPowerManager = mContext.getSystemService(PowerManager.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); - IntentFilter screenOnOffIntentFilter = new IntentFilter(); - screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON); - screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF); + if (!Flags.disableScreenOffBroadcastReceiver()) { + IntentFilter screenOnOffIntentFilter = new IntentFilter(); + screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON); + screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF); + mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter); + } - mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter); mContext.getPackageManager().addOnPermissionsChangeListener(this::onPermissionsChanged); MediaFeatureFlagManager.getInstance() @@ -2779,7 +2782,8 @@ class MediaRouter2ServiceImpl { List<ManagerRecord> managerRecords = getManagerRecords(); boolean isManagerScanning = false; - if (service.mPowerManager.isInteractive()) { + if (Flags.disableScreenOffBroadcastReceiver() + || service.mPowerManager.isInteractive()) { isManagerScanning = managerRecords.stream().anyMatch(manager -> manager.mIsScanning && service.mActivityManager .getPackageImportance(manager.mOwnerPackageName) diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java index 75a0cf521a1d..6b7db2d8d071 100644 --- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java +++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java @@ -38,6 +38,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioAttributes; @@ -48,6 +49,7 @@ import android.os.Binder; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.UserManager; import android.os.VibrationEffect; import android.provider.Settings; import android.telephony.PhoneStateListener; @@ -61,18 +63,21 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.EventLogTags; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; +import com.android.server.notification.Flags; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -87,15 +92,24 @@ public final class NotificationAttentionHelper { static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean( "debug.notification.interruptiveness", false); + private static final float DEFAULT_VOLUME = 1.0f; + // TODO (b/291899544): remove for release + private static final String POLITE_STRATEGY1 = "rule1"; + private static final String POLITE_STRATEGY2 = "rule2"; + private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED = 1; + private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK = 0; + private static final int DEFAULT_NOTIFICATION_COOLDOWN_ALL = 1; + private static final int DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED = 0; + private final Context mContext; private final PackageManager mPackageManager; private final TelephonyManager mTelephonyManager; + private final UserManager mUm; private final NotificationManagerPrivate mNMP; private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver; private AccessibilityManager mAccessibilityManager; private KeyguardManager mKeyguardManager; private AudioManager mAudioManager; - private final LightsManager mLightsManager; private final NotificationUsageStats mUsageStats; private final ZenModeHelper mZenModeHelper; @@ -126,17 +140,26 @@ public final class NotificationAttentionHelper { private final float mInCallNotificationVolume; private Binder mCallNotificationToken = null; + // Settings flags + private boolean mNotificationCooldownEnabled; + private boolean mNotificationCooldownForWorkEnabled; + private boolean mNotificationCooldownApplyToAll; + private boolean mNotificationCooldownVibrateUnlocked; + + private boolean mEnablePoliteNotificationsFeature; + private final PolitenessStrategy mStrategy; + private int mCurrentWorkProfileId = UserHandle.USER_NULL; public NotificationAttentionHelper(Context context, LightsManager lightsManager, AccessibilityManager accessibilityManager, PackageManager packageManager, - NotificationUsageStats usageStats, + UserManager userManager, NotificationUsageStats usageStats, NotificationManagerPrivate notificationManagerPrivate, ZenModeHelper zenModeHelper, SystemUiSystemPropertiesFlags.FlagResolver flagResolver) { mContext = context; mPackageManager = packageManager; mTelephonyManager = context.getSystemService(TelephonyManager.class); mAccessibilityManager = accessibilityManager; - mLightsManager = lightsManager; + mUm = userManager; mNMP = notificationManagerPrivate; mUsageStats = usageStats; mZenModeHelper = zenModeHelper; @@ -144,8 +167,8 @@ public final class NotificationAttentionHelper { mVibratorHelper = new VibratorHelper(context); - mNotificationLight = mLightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); - mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION); + mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); + mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION); Resources resources = context.getResources(); mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight); @@ -169,7 +192,39 @@ public final class NotificationAttentionHelper { .build(); mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume); + mEnablePoliteNotificationsFeature = Flags.politeNotifications(); + + if (mEnablePoliteNotificationsFeature) { + mStrategy = getPolitenessStrategy(); + } else { + mStrategy = null; + } + mSettingsObserver = new SettingsObserver(); + loadUserSettings(); + } + + private PolitenessStrategy getPolitenessStrategy() { + final String politenessStrategy = mFlagResolver.getStringValue( + NotificationFlags.NOTIF_COOLDOWN_RULE); + + if (POLITE_STRATEGY2.equals(politenessStrategy)) { + return new Strategy2(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1), + mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2), + mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1), + mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2)); + } else { + if (!POLITE_STRATEGY1.equals(politenessStrategy)) { + Log.w(TAG, "Invalid cooldown strategy: " + politenessStrategy + ". Defaulting to " + + POLITE_STRATEGY1); + } + + return new Strategy1(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1), + mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2), + mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1), + mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2), + mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET)); + } } public void onSystemReady() { @@ -202,11 +257,59 @@ public final class NotificationAttentionHelper { filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(Intent.ACTION_USER_PRESENT); + filter.addAction(Intent.ACTION_USER_ADDED); + filter.addAction(Intent.ACTION_USER_REMOVED); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); mContext.getContentResolver().registerContentObserver( SettingsObserver.NOTIFICATION_LIGHT_PULSE_URI, false, mSettingsObserver, UserHandle.USER_ALL); + if (mEnablePoliteNotificationsFeature) { + mContext.getContentResolver().registerContentObserver( + SettingsObserver.NOTIFICATION_COOLDOWN_ENABLED_URI, false, mSettingsObserver, + UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver( + SettingsObserver.NOTIFICATION_COOLDOWN_ALL_URI, false, mSettingsObserver, + UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver( + SettingsObserver.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI, false, + mSettingsObserver, UserHandle.USER_ALL); + } + } + + private void loadUserSettings() { + if (mEnablePoliteNotificationsFeature) { + try { + mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser()); + + mNotificationCooldownEnabled = + Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, + DEFAULT_NOTIFICATION_COOLDOWN_ENABLED, UserHandle.USER_CURRENT) != 0; + if (mCurrentWorkProfileId != UserHandle.USER_NULL) { + mNotificationCooldownForWorkEnabled = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, + DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK, mCurrentWorkProfileId) + != 0; + } else { + mNotificationCooldownForWorkEnabled = false; + } + mNotificationCooldownApplyToAll = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ALL, DEFAULT_NOTIFICATION_COOLDOWN_ALL, + UserHandle.USER_CURRENT) != 0; + mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, + DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, + UserHandle.USER_CURRENT) != 0; + } catch (Exception e) { + Log.e(TAG, "Failed to read Settings: " + e); + } + } } @VisibleForTesting @@ -229,6 +332,10 @@ public final class NotificationAttentionHelper { Log.d(TAG, "buzzBeepBlinkLocked " + record); } + if (isPoliteNotificationFeatureEnabled(record)) { + mStrategy.onNotificationPosted(record); + } + // Should this notification make noise, vibe, or use the LED? final boolean aboveThreshold = mIsAutomotive @@ -269,6 +376,9 @@ public final class NotificationAttentionHelper { vibration = mVibratorHelper.createFallbackVibration(insistent); } hasValidVibrate = vibration != null; + // Vibration-only if unlocked and Settings flag set + boolean vibrateOnly = + hasValidVibrate && mNotificationCooldownVibrateUnlocked && mUserPresent; boolean hasAudibleAlert = hasValidSound || hasValidVibrate; if (hasAudibleAlert && !shouldMuteNotificationLocked(record, signals)) { if (!sentAccessibilityEvent) { @@ -277,7 +387,7 @@ public final class NotificationAttentionHelper { } if (DEBUG) Slog.v(TAG, "Interrupting!"); boolean isInsistentUpdate = isInsistentUpdate(record); - if (hasValidSound) { + if (hasValidSound && !vibrateOnly) { if (isInsistentUpdate) { // don't reset insistent sound, it's jarring beep = true; @@ -301,7 +411,7 @@ public final class NotificationAttentionHelper { if (isInsistentUpdate) { buzz = true; } else { - buzz = playVibration(record, vibration, hasValidSound); + buzz = playVibration(record, vibration, hasValidSound && !vibrateOnly); if (buzz) { mVibrateNotificationKey = key; } @@ -341,9 +451,7 @@ public final class NotificationAttentionHelper { } else if (wasShowLights) { updateLightsLocked(); } - final int buzzBeepBlink = - (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0); - if (buzzBeepBlink > 0) { + if (buzz || beep || blink) { // Ignore summary updates because we don't display most of the information. if (record.getSbn().isGroup() && record.getSbn().getNotification().isGroupSummary()) { if (DEBUG_INTERRUPTIVENESS) { @@ -362,15 +470,43 @@ public final class NotificationAttentionHelper { + record.getKey() + " is interruptive: alerted"); } } + } + final int buzzBeepBlinkLoggingCode = + (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0) | getPoliteBit(record); + if (buzzBeepBlinkLoggingCode > 0) { MetricsLogger.action(record.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_ALERT) .setType(MetricsEvent.TYPE_OPEN) - .setSubtype(buzzBeepBlink)); - EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0); + .setSubtype(buzzBeepBlinkLoggingCode)); + EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0, + getPolitenessState(record)); } record.setAudiblyAlerted(buzz || beep); + if (mEnablePoliteNotificationsFeature) { + // Update last alert time + if (buzz || beep) { + record.getChannel().setLastNotificationUpdateTimeMs(System.currentTimeMillis()); + } + } + return buzzBeepBlinkLoggingCode; + } - return buzzBeepBlink; + private int getPoliteBit(final NotificationRecord record) { + switch (getPolitenessState(record)) { + case PolitenessStrategy.POLITE_STATE_POLITE: + return MetricsProto.MetricsEvent.ALERT_POLITE; + case PolitenessStrategy.POLITE_STATE_MUTED: + return MetricsProto.MetricsEvent.ALERT_MUTED; + default: + return 0; + } + } + + private int getPolitenessState(final NotificationRecord record) { + if (!isPoliteNotificationFeatureEnabled(record)) { + return PolitenessStrategy.POLITE_STATE_DEFAULT; + } + return mStrategy.getPolitenessState(record); } boolean isInsistentUpdate(final NotificationRecord record) { @@ -468,7 +604,7 @@ public final class NotificationAttentionHelper { + record.getAudioAttributes()); } player.playAsync(soundUri, record.getSbn().getUser(), looping, - record.getAudioAttributes()); + record.getAudioAttributes(), getSoundVolume(record)); return true; } } catch (RemoteException e) { @@ -480,12 +616,56 @@ public final class NotificationAttentionHelper { return false; } + private boolean isPoliteNotificationFeatureEnabled(final NotificationRecord record) { + // Check feature flag + if (!mEnablePoliteNotificationsFeature) { + return false; + } + + // The user can enable/disable notifications cooldown from the Settings app + if (!mNotificationCooldownEnabled) { + return false; + } + + // The user can enable/disable notifications cooldown for work profile from the Settings app + if (isNotificationForWorkProfile(record) && !mNotificationCooldownForWorkEnabled) { + return false; + } + + // The user can choose to apply cooldown for all apps/conversations only from the + // Settings app + if (!mNotificationCooldownApplyToAll && record.getChannel().getConversationId() == null) { + return false; + } + + return true; + } + + private float getSoundVolume(final NotificationRecord record) { + if (!isPoliteNotificationFeatureEnabled(record)) { + return DEFAULT_VOLUME; + } + + return mStrategy.getSoundVolume(record); + } + + private float getVibrationIntensity(final NotificationRecord record) { + if (!isPoliteNotificationFeatureEnabled(record)) { + return DEFAULT_VOLUME; + } + + return mStrategy.getVibrationIntensity(record); + } + private boolean playVibration(final NotificationRecord record, final VibrationEffect effect, boolean delayVibForSound) { // Escalate privileges so we can use the vibrator even if the // notifying app does not have the VIBRATE permission. final long identity = Binder.clearCallingIdentity(); try { + final float scale = getVibrationIntensity(record); + final VibrationEffect scaledEffect = Float.compare(scale, DEFAULT_VOLUME) != 0 + ? mVibratorHelper.scale(effect, scale) : effect; if (delayVibForSound) { new Thread(() -> { // delay the vibration by the same amount as the notification sound @@ -503,7 +683,7 @@ public final class NotificationAttentionHelper { // so need to check that the notification is still valid for vibrate. if (mNMP.getNotificationByKey(record.getKey()) != null) { if (record.getKey().equals(mVibrateNotificationKey)) { - vibrate(record, effect, true); + vibrate(record, scaledEffect, true); } else { if (DEBUG) { Slog.v(TAG, "No vibration for notification " @@ -517,7 +697,7 @@ public final class NotificationAttentionHelper { } }).start(); } else { - vibrate(record, effect, false); + vibrate(record, scaledEffect, false); } return true; } finally { @@ -535,7 +715,7 @@ public final class NotificationAttentionHelper { } void playInCallNotification() { - // TODO: Should we apply politeness to mInCallNotificationVolume ? + // TODO b/270456865: Should we apply politeness to mInCallNotificationVolume ? final ContentResolver cr = mContext.getContentResolver(); if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL && Settings.Secure.getIntForUser(cr, @@ -760,6 +940,22 @@ public final class NotificationAttentionHelper { || signals.isCurrentProfile); } + private boolean isNotificationForWorkProfile(final NotificationRecord record) { + return (record.getUser().getIdentifier() == mCurrentWorkProfileId + && mCurrentWorkProfileId != UserHandle.USER_NULL); + } + + private int getManagedProfileId(int parentUserId) { + final List<UserInfo> profiles = mUm.getProfiles(parentUserId); + for (UserInfo profile : profiles) { + if (profile.isManagedProfile() + && profile.getUserHandle().getIdentifier() != parentUserId) { + return profile.getUserHandle().getIdentifier(); + } + } + return UserHandle.USER_NULL; + } + void sendAccessibilityEvent(NotificationRecord record) { if (!mAccessibilityManager.isEnabled()) { return; @@ -791,6 +987,16 @@ public final class NotificationAttentionHelper { mAccessibilityManager.sendAccessibilityEvent(event); } + /** + * Notify the attention helper of a user interaction with a notification + * @param record that was interacted with + */ + public void onUserInteraction(final NotificationRecord record) { + if (isPoliteNotificationFeatureEnabled(record)) { + mStrategy.onUserInteraction(record); + } + } + public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) { pw.println("\n Notification attention state:"); pw.print(prefix); @@ -834,6 +1040,243 @@ public final class NotificationAttentionHelper { } } + abstract private static class PolitenessStrategy { + static final int POLITE_STATE_DEFAULT = 0; + static final int POLITE_STATE_POLITE = 1; + static final int POLITE_STATE_MUTED = 2; + + @IntDef(prefix = { "POLITE_STATE_" }, value = { + POLITE_STATE_DEFAULT, + POLITE_STATE_POLITE, + POLITE_STATE_MUTED, + }) + @Retention(RetentionPolicy.SOURCE) + @interface PolitenessState {} + + protected final Map<String, Integer> mVolumeStates; + + // Cooldown timer for transitioning into polite state + protected final int mTimeoutPolite; + // Cooldown timer for transitioning into muted state + protected final int mTimeoutMuted; + // Volume for polite state + protected final float mVolumePolite; + // Volume for muted state + protected final float mVolumeMuted; + + public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite, + int volumeMuted) { + mVolumeStates = new HashMap<>(); + + this.mTimeoutPolite = timeoutPolite; + this.mTimeoutMuted = timeoutMuted; + this.mVolumePolite = volumePolite / 100.0f; + this.mVolumeMuted = volumeMuted / 100.0f; + } + + abstract void onNotificationPosted(NotificationRecord record); + + String getChannelKey(final NotificationRecord record) { + // use conversationId if it's a conversation + String channelId = record.getChannel().getConversationId() != null + ? record.getChannel().getConversationId() : record.getChannel().getId(); + return record.getSbn().getNormalizedUserId() + ":" + record.getSbn().getPackageName() + + ":" + channelId; + } + + public float getSoundVolume(final NotificationRecord record) { + float volume = DEFAULT_VOLUME; + final String key = getChannelKey(record); + final @PolitenessState int volState = getPolitenessState(record); + + switch (volState) { + case POLITE_STATE_DEFAULT: + volume = DEFAULT_VOLUME; + break; + case POLITE_STATE_POLITE: + volume = mVolumePolite; + break; + case POLITE_STATE_MUTED: + volume = mVolumeMuted; + break; + default: + Log.w(TAG, "getSoundVolume unexpected volume state: " + volState); + break; + } + + if (DEBUG) { + Log.i(TAG, + "getSoundVolume state: " + volState + " vol: " + volume + " key: " + key); + } + + return volume; + } + + private float getVibrationIntensity(final NotificationRecord record) { + // TODO b/270456865: maybe use different scaling for vibration/sound ? + return getSoundVolume(record); + } + + public void onUserInteraction(final NotificationRecord record) { + final String key = getChannelKey(record); + // reset to default state after user interaction + mVolumeStates.put(key, POLITE_STATE_DEFAULT); + record.getChannel().setLastNotificationUpdateTimeMs(0); + } + + public final @PolitenessState int getPolitenessState(final NotificationRecord record) { + return mVolumeStates.getOrDefault(getChannelKey(record), POLITE_STATE_DEFAULT); + } + } + + // TODO b/270456865: Only one of the two strategies will be released. + // The other one need to be removed + /** + * Polite notification strategy 1: + * - Transitions from default (loud) => polite (lower volume) state if a notification + * alerts the same channel before timeoutPolite. + * - Transitions from polite => muted state if a notification alerts the same channel + * before timeoutMuted OR transitions back to the default state if a notification alerts + * after timeoutPolite. + * - Transitions from muted => default state if the muted channel received more than maxPosted + * notifications OR transitions back to the polite state if a notification alerts + * after timeoutMuted. + * - Transitions back to the default state after a user interaction with a notification. + */ + public static class Strategy1 extends PolitenessStrategy { + // Keep track of the number of notifications posted per channel + private final Map<String, Integer> mNumPosted; + // Reset to default state if number of posted notifications exceed this value when muted + private final int mMaxPostedForReset; + + public Strategy1(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted, + int maxPosted) { + super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted); + + mNumPosted = new HashMap<>(); + mMaxPostedForReset = maxPosted; + + if (DEBUG) { + Log.i(TAG, "Strategy1: " + timeoutPolite + " " + timeoutMuted); + } + } + + @Override + public void onNotificationPosted(final NotificationRecord record) { + long timeSinceLastNotif = System.currentTimeMillis() + - record.getChannel().getLastNotificationUpdateTimeMs(); + + final String key = getChannelKey(record); + @PolitenessState int volState = getPolitenessState(record); + + int numPosted = mNumPosted.getOrDefault(key, 0) + 1; + mNumPosted.put(key, numPosted); + + switch (volState) { + case POLITE_STATE_DEFAULT: + if (timeSinceLastNotif < mTimeoutPolite) { + volState = POLITE_STATE_POLITE; + } + break; + case POLITE_STATE_POLITE: + if (timeSinceLastNotif < mTimeoutMuted) { + volState = POLITE_STATE_MUTED; + } else if (timeSinceLastNotif > mTimeoutPolite) { + volState = POLITE_STATE_DEFAULT; + } else { + volState = POLITE_STATE_POLITE; + } + break; + case POLITE_STATE_MUTED: + if (timeSinceLastNotif > mTimeoutMuted) { + volState = POLITE_STATE_POLITE; + } else { + volState = POLITE_STATE_MUTED; + } + if (numPosted >= mMaxPostedForReset) { + volState = POLITE_STATE_DEFAULT; + mNumPosted.put(key, 0); + } + break; + default: + Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState); + break; + } + + if (DEBUG) { + Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: " + + volState + " key: " + key + " numposted " + numPosted); + } + + mVolumeStates.put(key, volState); + } + + @Override + public void onUserInteraction(final NotificationRecord record) { + super.onUserInteraction(record); + mNumPosted.put(getChannelKey(record), 0); + } + } + + /** + * Polite notification strategy 2: + * - Transitions from default (loud) => muted state if a notification + * alerts the same channel before timeoutPolite. + * - Transitions from polite => default state if a notification + * alerts the same channel before timeoutMuted. + * - Transitions from muted => default state if a notification alerts after timeoutMuted, + * otherwise transitions to the polite state. + * - Transitions back to the default state after a user interaction with a notification. + */ + public static class Strategy2 extends PolitenessStrategy { + public Strategy2(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted) { + super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted); + + if (DEBUG) { + Log.i(TAG, "Strategy2: " + timeoutPolite + " " + timeoutMuted); + } + } + + @Override + public void onNotificationPosted(final NotificationRecord record) { + long timeSinceLastNotif = System.currentTimeMillis() + - record.getChannel().getLastNotificationUpdateTimeMs(); + + final String key = getChannelKey(record); + @PolitenessState int volState = getPolitenessState(record); + + switch (volState) { + case POLITE_STATE_DEFAULT: + if (timeSinceLastNotif < mTimeoutPolite) { + volState = POLITE_STATE_MUTED; + } + break; + case POLITE_STATE_POLITE: + if (timeSinceLastNotif > mTimeoutMuted) { + volState = POLITE_STATE_DEFAULT; + } + break; + case POLITE_STATE_MUTED: + if (timeSinceLastNotif > mTimeoutMuted) { + volState = POLITE_STATE_DEFAULT; + } else { + volState = POLITE_STATE_POLITE; + } + break; + default: + Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState); + break; + } + + if (DEBUG) { + Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: " + + volState + " key: " + key); + } + + mVolumeStates.put(key, volState); + } + } + //====================== Observers ============================= private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override @@ -859,6 +1302,11 @@ public final class NotificationAttentionHelper { if (mNotificationLight != null) { mNotificationLight.turnOff(); } + } else if (action.equals(Intent.ACTION_USER_ADDED) + || action.equals(Intent.ACTION_USER_REMOVED) + || action.equals(Intent.ACTION_USER_SWITCHED) + || action.equals(Intent.ACTION_USER_UNLOCKED)) { + loadUserSettings(); } } }; @@ -867,6 +1315,12 @@ public final class NotificationAttentionHelper { private static final Uri NOTIFICATION_LIGHT_PULSE_URI = Settings.System.getUriFor( Settings.System.NOTIFICATION_LIGHT_PULSE); + private static final Uri NOTIFICATION_COOLDOWN_ENABLED_URI = Settings.System.getUriFor( + Settings.System.NOTIFICATION_COOLDOWN_ENABLED); + private static final Uri NOTIFICATION_COOLDOWN_ALL_URI = Settings.System.getUriFor( + Settings.System.NOTIFICATION_COOLDOWN_ALL); + private static final Uri NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI = + Settings.System.getUriFor(Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED); public SettingsObserver() { super(null); } @@ -884,11 +1338,45 @@ public final class NotificationAttentionHelper { updateLightsLocked(); } } + if (mEnablePoliteNotificationsFeature) { + if (NOTIFICATION_COOLDOWN_ENABLED_URI.equals(uri)) { + mNotificationCooldownEnabled = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, + DEFAULT_NOTIFICATION_COOLDOWN_ENABLED, + UserHandle.USER_CURRENT) != 0; + + if (mCurrentWorkProfileId != UserHandle.USER_NULL) { + mNotificationCooldownForWorkEnabled = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, + DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK, + mCurrentWorkProfileId) + != 0; + } else { + mNotificationCooldownForWorkEnabled = false; + } + } + if (NOTIFICATION_COOLDOWN_ALL_URI.equals(uri)) { + mNotificationCooldownApplyToAll = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ALL, + DEFAULT_NOTIFICATION_COOLDOWN_ALL, UserHandle.USER_CURRENT) + != 0; + } + if (NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI.equals(uri)) { + mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, + DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, + UserHandle.USER_CURRENT) != 0; + } + } } } - //TODO: cleanup most (all?) of these + // TODO b/270456865: cleanup most (all?) of these //======================= FOR TESTS ===================== @VisibleForTesting void setIsAutomotive(boolean isAutomotive) { @@ -931,6 +1419,11 @@ public final class NotificationAttentionHelper { } @VisibleForTesting + void setUserPresent(boolean userPresent) { + mUserPresent = userPresent; + } + + @VisibleForTesting void setLights(LogicalLight light) { mNotificationLight = light; mAttentionLight = light; diff --git a/services/core/java/com/android/server/notification/NotificationBitmapJobService.java b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java index 4335a1dcfa75..e1a07076cb2d 100644 --- a/services/core/java/com/android/server/notification/NotificationBitmapJobService.java +++ b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java @@ -29,7 +29,12 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; -import java.util.Calendar; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.time.ZoneId; /** * This service runs everyday at 2am local time to remove expired bitmaps. @@ -69,26 +74,25 @@ public class NotificationBitmapJobService extends JobService { * @return Milliseconds until the next time the job should run. */ private static long getRunAfterMs() { - Calendar cal = Calendar.getInstance(); // Uses local time zone - final long now = cal.getTimeInMillis(); + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime now = Instant.now().atZone(zoneId); - cal.set(Calendar.HOUR_OF_DAY, 2); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.MILLISECOND, 0); - final long today2AM = cal.getTimeInMillis(); + LocalDate today = now.toLocalDate(); + LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0); - cal.add(Calendar.DAY_OF_YEAR, 1); - final long tomorrow2AM = cal.getTimeInMillis(); + ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId); + ZonedDateTime tomorrow2AM = today2AM.plusDays(1); return getTimeUntilRemoval(now, today2AM, tomorrow2AM); } @VisibleForTesting - static long getTimeUntilRemoval(long now, long today2AM, long tomorrow2AM) { - if (now < today2AM) { - return today2AM - now; + static long getTimeUntilRemoval(ZonedDateTime now, ZonedDateTime today2AM, + ZonedDateTime tomorrow2AM) { + if (Duration.between(now, today2AM).isNegative()) { + return Duration.between(now, tomorrow2AM).toMillis(); } - return tomorrow2AM - now; + return Duration.between(now, today2AM).toMillis(); } @Override diff --git a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java index 3776ad7a0799..c9317d17be37 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java @@ -27,6 +27,7 @@ import android.content.Context; import android.os.CancellationSignal; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import java.util.concurrent.TimeUnit; @@ -77,5 +78,11 @@ public class NotificationHistoryJobService extends JobService { } return false; } + + @Override + @VisibleForTesting + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a3c71c2e0218..53ed4aea3d19 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -396,7 +396,7 @@ public class NotificationManagerService extends SystemService { static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7; static final int MESSAGE_ON_PACKAGE_CHANGED = 8; - static final long BITMAP_EXPIRATION_TIME_MS = TimeUnit.HOURS.toMillis(24); + static final Duration BITMAP_DURATION = Duration.ofHours(24); // ranking thread messages private static final int MESSAGE_RECONSIDER_RANKING = 1000; @@ -541,6 +541,13 @@ public class NotificationManagerService extends SystemService { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2) private static final long NOTIFICATION_LOG_ASSISTANT_CANCEL = 195579280L; + /** + * NO_CLEAR flag will be set for any media notification. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L; + private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30); private IActivityManager mAm; @@ -2525,7 +2532,7 @@ public class NotificationManagerService extends SystemService { if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) { mAttentionHelper = new NotificationAttentionHelper(getContext(), lightsManager, - mAccessibilityManager, mPackageManagerClient, usageStats, + mAccessibilityManager, mPackageManagerClient, userManager, usageStats, mNotificationManagerPrivate, mZenModeHelper, flagResolver); } @@ -3362,6 +3369,10 @@ public class NotificationManagerService extends SystemService { mAppUsageStats.reportEvent(r.getSbn().getPackageName(), getRealUserId(r.getSbn().getUserId()), UsageEvents.Event.USER_INTERACTION); + + if (Flags.politeNotifications()) { + mAttentionHelper.onUserInteraction(r); + } } private int getRealUserId(int userId) { @@ -6698,7 +6709,7 @@ public class NotificationManagerService extends SystemService { final long timePostedMs = r.getSbn().getPostTime(); final long timeNowMs = System.currentTimeMillis(); - if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_EXPIRATION_TIME_MS)) { + if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_DURATION.toMillis())) { removeBitmapAndRepost(r); } } @@ -7189,6 +7200,12 @@ public class NotificationManagerService extends SystemService { + "MEDIA_CONTENT_CONTROL permission"); } } + + // Enforce NO_CLEAR flag on MediaStyle notification for apps with targetSdk >= V. + if (CompatChanges.isChangeEnabled(ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION, + notificationUid)) { + notification.flags |= Notification.FLAG_NO_CLEAR; + } } // Ensure only allowed packages have a substitute app name @@ -8599,7 +8616,7 @@ public class NotificationManagerService extends SystemService { .setCategory(MetricsEvent.NOTIFICATION_ALERT) .setType(MetricsEvent.TYPE_OPEN) .setSubtype(buzzBeepBlink)); - EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0); + EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0, 0); } record.setAudiblyAlerted(buzz || beep); return buzzBeepBlink; @@ -8744,7 +8761,7 @@ public class NotificationManagerService extends SystemService { if (DBG) Slog.v(TAG, "Playing sound " + soundUri + " with attributes " + record.getAudioAttributes()); player.playAsync(soundUri, record.getSbn().getUser(), looping, - record.getAudioAttributes()); + record.getAudioAttributes(), 1.0f); return true; } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java index e5d07bc152c0..7204d05fdce7 100644 --- a/services/core/java/com/android/server/notification/VibratorHelper.java +++ b/services/core/java/com/android/server/notification/VibratorHelper.java @@ -50,6 +50,7 @@ public final class VibratorHelper { private final long[] mFallbackPattern; @Nullable private final float[] mDefaultPwlePattern; @Nullable private final float[] mFallbackPwlePattern; + private final int mDefaultVibrationAmplitude; public VibratorHelper(Context context) { mVibrator = context.getSystemService(Vibrator.class); @@ -65,6 +66,8 @@ public final class VibratorHelper { com.android.internal.R.array.config_defaultNotificationVibeWaveform); mFallbackPwlePattern = getFloatArray(context.getResources(), com.android.internal.R.array.config_notificationFallbackVibeWaveform); + mDefaultVibrationAmplitude = context.getResources().getInteger( + com.android.internal.R.integer.config_defaultVibrationAmplitude); } /** @@ -136,6 +139,14 @@ public final class VibratorHelper { } /** + * Scale vibration effect, valid range is [0.0f, 1.0f] + * Resolves default amplitude value if not already set. + */ + public VibrationEffect scale(VibrationEffect effect, float scale) { + return effect.resolve(mDefaultVibrationAmplitude).scale(scale); + } + + /** * Vibrate the device with given {@code effect}. * * <p>We need to vibrate as "android" so we can breakthrough DND. diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index f9876299e8e0..79cd2a0b236f 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -489,6 +489,9 @@ public interface Computer extends PackageDataSnapshot { boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId); + /** Check if the package is in a stopped state for a given user. */ + boolean isPackageStoppedForUser(@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 5d2944e17943..7db7bf538c37 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -4938,7 +4938,7 @@ public class ComputerEngine implements Computer { int userId) { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, - false /* checkShell */, "isPackageSuspendedForUser for user " + userId); + false /* checkShell */, "when asking about packages for user " + userId); final PackageStateInternal ps = mSettings.getPackage(packageName); if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { throw new IllegalArgumentException("Unknown target package: " + packageName); @@ -4957,6 +4957,11 @@ public class ComputerEngine implements Computer { } @Override + public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) { + return getUserStageOrDefaultForUser(packageName, userId).isStopped(); + } + + @Override public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId) { for (final PackageStateInternal packageState : getPackageStates().values()) { diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java index 76203ac7650d..9a0306b77c41 100644 --- a/services/core/java/com/android/server/pm/IPackageManagerBase.java +++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java @@ -961,6 +961,12 @@ public abstract class IPackageManagerBase extends IPackageManager.Stub { } @Override + public final boolean isPackageStoppedForUser(@NonNull String packageName, + @UserIdInt int userId) { + return snapshot().isPackageStoppedForUser(packageName, userId); + } + + @Override @Deprecated public final boolean isSafeMode() { // allow instant applications diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 1c7024b7d239..2d192826ba9a 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -22,8 +22,6 @@ import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.os.Process.INVALID_UID; -import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult; -import static com.android.server.art.model.DexoptResult.PackageDexoptResult; import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY; import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; import static com.android.server.pm.PackageManagerService.TAG; @@ -58,7 +56,6 @@ import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; import java.util.ArrayList; -import java.util.LinkedHashSet; import java.util.List; final class InstallRequest { @@ -150,9 +147,6 @@ final class InstallRequest { @NonNull private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY; - @NonNull - private ArrayList<String> mWarnings = new ArrayList<>(); - // New install InstallRequest(InstallingSession params) { mUserId = params.getUser().getIdentifier(); @@ -664,11 +658,6 @@ final class InstallRequest { return mUpdateBroadcastInstantUserIds; } - @NonNull - public ArrayList<String> getWarnings() { - return mWarnings; - } - public void setScanFlags(int scanFlags) { mScanFlags = scanFlags; } @@ -866,10 +855,6 @@ final class InstallRequest { } } - public void addWarning(@NonNull String warning) { - mWarnings.add(warning); - } - public void onPrepareStarted() { if (mPackageMetrics != null) { mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE); @@ -919,37 +904,22 @@ final class InstallRequest { } public void onDexoptFinished(DexoptResult dexoptResult) { - // Only report external profile warnings when installing from adb. The goal is to warn app - // developers if they have provided bad external profiles, so it's not beneficial to report - // those warnings in the normal app install workflow. - if (isInstallFromAdb()) { - var externalProfileErrors = new LinkedHashSet<String>(); - for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) { - for (DexContainerFileDexoptResult fileResult : - packageResult.getDexContainerFileDexoptResults()) { - externalProfileErrors.addAll(fileResult.getExternalProfileErrors()); - } - } - if (!externalProfileErrors.isEmpty()) { - addWarning("Error occurred during dexopt when processing external profiles:\n " - + String.join("\n ", externalProfileErrors)); - } + if (mPackageMetrics == null) { + return; } - - // Report dexopt metrics. - if (mPackageMetrics != null) { - mDexoptStatus = dexoptResult.getFinalStatus(); - if (mDexoptStatus == DexoptResult.DEXOPT_PERFORMED) { - long durationMillis = 0; - for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) { - for (DexContainerFileDexoptResult fileResult : - packageResult.getDexContainerFileDexoptResults()) { - durationMillis += fileResult.getDex2oatWallTimeMillis(); - } - } - mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis); + mDexoptStatus = dexoptResult.getFinalStatus(); + if (mDexoptStatus != DexoptResult.DEXOPT_PERFORMED) { + return; + } + long durationMillis = 0; + for (DexoptResult.PackageDexoptResult packageResult : + dexoptResult.getPackageDexoptResults()) { + for (DexoptResult.DexContainerFileDexoptResult fileResult : + packageResult.getDexContainerFileDexoptResults()) { + durationMillis += fileResult.getDex2oatWallTimeMillis(); } } + mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis); } public void onInstallCompleted() { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d0e5f96f8d0f..662703992ad8 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2930,40 +2930,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * @return a future that will be completed when the whole process is completed. */ private CompletableFuture<Void> install() { - // `futures` either contains only one session (`this`) or contains one parent session - // (`this`) and n-1 child sessions. List<CompletableFuture<InstallResult>> futures = installNonStaged(); CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()]; return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> { if (t == null) { setSessionApplied(); - var multiPackageWarnings = new ArrayList<String>(); - if (isMultiPackage()) { - // This is a parent session. Collect warnings from children. - for (CompletableFuture<InstallResult> f : futures) { - InstallResult result = f.join(); - if (result.session != this && result.extras != null) { - ArrayList<String> childWarnings = result.extras.getStringArrayList( - PackageInstaller.EXTRA_WARNINGS); - if (!ArrayUtils.isEmpty(childWarnings)) { - multiPackageWarnings.addAll(childWarnings); - } - } - } - } for (CompletableFuture<InstallResult> f : futures) { InstallResult result = f.join(); - Bundle extras = result.extras; - if (isMultiPackage() && result.session == this - && !multiPackageWarnings.isEmpty()) { - if (extras == null) { - extras = new Bundle(); - } - extras.putStringArrayList( - PackageInstaller.EXTRA_WARNINGS, multiPackageWarnings); - } result.session.dispatchSessionFinished( - INSTALL_SUCCEEDED, "Session installed", extras); + INSTALL_SUCCEEDED, "Session installed", result.extras); } } else { PackageManagerException e = (PackageManagerException) t.getCause(); @@ -5214,10 +5189,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (!TextUtils.isEmpty(existing)) { fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing); } - ArrayList<String> warnings = extras.getStringArrayList(PackageInstaller.EXTRA_WARNINGS); - if (!ArrayUtils.isEmpty(warnings)) { - fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings); - } } try { final BroadcastOptions options = BroadcastOptions.makeBasic(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ddc8369738de..6260dd583bf9 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1434,9 +1434,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService break; } } - if (!request.getWarnings().isEmpty()) { - extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings()); - } return extras; } @@ -4501,6 +4498,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService boolean stopped, @UserIdInt int userId) { if (!mUserManager.exists(userId)) return; final int callingUid = Binder.getCallingUid(); + boolean wasStopped = false; if (snapshot.getInstantAppPackageName(callingUid) == null) { final int permission = mContext.checkCallingOrSelfPermission( Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); @@ -4522,6 +4520,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService ? null : packageState.getUserStateOrDefault(userId); if (packageState != null && packageUserState.isStopped() != stopped) { boolean wasNotLaunched = packageUserState.isNotLaunched(); + wasStopped = packageUserState.isStopped(); commitPackageStateMutation(null, packageName, state -> { PackageUserStateWrite userState = state.userState(userId); userState.setStopped(stopped); @@ -4553,6 +4552,24 @@ public class PackageManagerService implements PackageSender, TestUtilityService ah.setHibernatingGlobally(packageName, false); } }); + // Send UNSTOPPED broadcast if necessary + if (wasStopped && Flags.stayStopped()) { + final PackageManagerInternal pmi = + mInjector.getLocalService(PackageManagerInternal.class); + final int [] userIds = resolveUserIds(userId); + final SparseArray<int[]> broadcastAllowList = + snapshotComputer().getVisibilityAllowLists(packageName, userIds); + final Bundle extras = new Bundle(); + extras.putInt(Intent.EXTRA_UID, pmi.getPackageUid(packageName, 0, userId)); + extras.putInt(Intent.EXTRA_USER_HANDLE, userId); + mHandler.post(() -> { + mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED, + packageName, extras, + Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, + userIds, null, broadcastAllowList, null, + null); + }); + } } } @@ -6929,6 +6946,25 @@ public class PackageManagerService implements PackageSender, TestUtilityService public ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(int userId) { return mInstallerService.getHistoricalSessions(userId); } + + @Override + public void sendPackageRestartedBroadcast(@NonNull String packageName, + int uid, @Intent.Flags int flags) { + final int userId = UserHandle.getUserId(uid); + final int [] userIds = resolveUserIds(userId); + final SparseArray<int[]> broadcastAllowList = + snapshotComputer().getVisibilityAllowLists(packageName, userIds); + final Bundle extras = new Bundle(); + extras.putInt(Intent.EXTRA_UID, uid); + extras.putInt(Intent.EXTRA_USER_HANDLE, userId); + mHandler.post(() -> { + mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED, + packageName, extras, + flags, null, null, + userIds, null, broadcastAllowList, null, + null); + }); + } } private void setEnabledOverlayPackages(@UserIdInt int userId, diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 7264e2eff4aa..3a9272dc2003 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -4397,21 +4397,10 @@ class PackageManagerShellCommand extends ShellCommand { session.commit(receiver.getIntentSender()); if (!session.isStaged()) { final Intent result = receiver.getResult(); - int status = result.getIntExtra( - PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); - List<String> warnings = - result.getStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { - if (!ArrayUtils.isEmpty(warnings)) { - // Don't start the output string with "Success" because that will make adb - // treat this as a success. - for (String warning : warnings) { - pw.println("Warning: " + warning); - } - // Treat warnings as failure to draw app developers' attention. - status = PackageInstaller.STATUS_FAILURE; - pw.println("Completed with warning(s)"); - } else if (logSuccess) { + if (logSuccess) { pw.println("Success"); } } else { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 82d3e9157986..0e98158d7210 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -75,13 +75,13 @@ import android.content.pm.parsing.FrameworkParsingPackageUtils; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; +import android.multiuser.Flags; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Environment; import android.os.FileUtils; -import android.os.Flags; import android.os.Handler; import android.os.IBinder; import android.os.IProgressListener; @@ -286,6 +286,8 @@ public class UserManagerService extends IUserManager.Stub { private static final int USER_VERSION = 11; + private static final int MAX_USER_STRING_LENGTH = 500; + private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms static final int WRITE_USER_MSG = 1; @@ -1557,7 +1559,7 @@ public class UserManagerService extends IUserManager.Stub { logQuietModeEnabled(userId, enableQuietMode, callingPackage); // Broadcast generic intents for all profiles - if (Flags.allowPrivateProfile()) { + if (android.os.Flags.allowPrivateProfile()) { broadcastProfileAvailabilityChanges(profile, parent.getUserHandle(), enableQuietMode, false); } @@ -3783,6 +3785,8 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy({"mPackagesLock"}) private void readUserListLP() { + // Whether guest restrictions are present on userlist.xml + boolean guestRestrictionsArePresentOnUserListXml = false; try (ResilientAtomicFile file = getUserListFile()) { FileInputStream fin = null; try { @@ -3832,6 +3836,7 @@ public class UserManagerService extends IUserManager.Stub { } } } else if (name.equals(TAG_GUEST_RESTRICTIONS)) { + guestRestrictionsArePresentOnUserListXml = true; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.END_TAG) { if (type == XmlPullParser.START_TAG) { @@ -3850,6 +3855,7 @@ public class UserManagerService extends IUserManager.Stub { updateUserIds(); upgradeIfNecessaryLP(); + updateUsersWithFeatureFlags(guestRestrictionsArePresentOnUserListXml); } catch (Exception e) { // Remove corrupted file and retry. file.failRead(fin, e); @@ -3875,6 +3881,24 @@ public class UserManagerService extends IUserManager.Stub { } /** + * Update any user formats or Xml data that need to be updated based on the current user state + * and the feature flag settings. + */ + @GuardedBy({"mPackagesLock"}) + private void updateUsersWithFeatureFlags(boolean guestRestrictionsArePresentOnUserListXml) { + // User Xml re-writes are required when guest restrictions are saved on userlist.xml but + // as per the feature flag it should be on the SYSTEM user's xml or guest restrictions + // are saved on SYSTEM user's xml but as per the flags it should not be saved there. + if (guestRestrictionsArePresentOnUserListXml + == Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) { + for (int userId: getUserIds()) { + writeUserLP(getUserDataNoChecks(userId)); + } + writeUserListLP(); + } + } + + /** * Version of {@link #upgradeIfNecessaryLP()} that takes in the userVersion for testing * purposes. For non-tests, use {@link #upgradeIfNecessaryLP()}. */ @@ -4374,24 +4398,41 @@ public class UserManagerService extends IUserManager.Stub { // Write seed data if (userData.persistSeedData) { if (userData.seedAccountName != null) { - serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, userData.seedAccountName); + serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, + truncateString(userData.seedAccountName)); } if (userData.seedAccountType != null) { - serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType); + serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, + truncateString(userData.seedAccountType)); } } if (userInfo.name != null) { serializer.startTag(null, TAG_NAME); - serializer.text(userInfo.name); + serializer.text(truncateString(userInfo.name)); serializer.endTag(null, TAG_NAME); } synchronized (mRestrictionsLock) { UserRestrictionsUtils.writeRestrictions(serializer, mBaseUserRestrictions.getRestrictions(userInfo.id), TAG_RESTRICTIONS); - UserRestrictionsUtils.writeRestrictions(serializer, - mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL), - TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS); + if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) { + if (userInfo.id == UserHandle.USER_SYSTEM) { + UserRestrictionsUtils.writeRestrictions(serializer, + mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL), + TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS); + + serializer.startTag(null, TAG_GUEST_RESTRICTIONS); + synchronized (mGuestRestrictions) { + UserRestrictionsUtils.writeRestrictions(serializer, mGuestRestrictions, + TAG_RESTRICTIONS); + } + serializer.endTag(null, TAG_GUEST_RESTRICTIONS); + } + } else { + UserRestrictionsUtils.writeRestrictions(serializer, + mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL), + TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS); + } UserRestrictionsUtils.writeRestrictions(serializer, mDevicePolicyUserRestrictions.getRestrictions(userInfo.id), @@ -4431,6 +4472,13 @@ public class UserManagerService extends IUserManager.Stub { serializer.endDocument(); } + private String truncateString(String original) { + if (original == null || original.length() <= MAX_USER_STRING_LENGTH) { + return original; + } + return original.substring(0, MAX_USER_STRING_LENGTH); + } + /* * Writes the user list file in this format: * @@ -4460,12 +4508,15 @@ public class UserManagerService extends IUserManager.Stub { serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion); serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); - serializer.startTag(null, TAG_GUEST_RESTRICTIONS); - synchronized (mGuestRestrictions) { - UserRestrictionsUtils - .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS); + if (!Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) { + serializer.startTag(null, TAG_GUEST_RESTRICTIONS); + synchronized (mGuestRestrictions) { + UserRestrictionsUtils + .writeRestrictions(serializer, mGuestRestrictions, + TAG_RESTRICTIONS); + } + serializer.endTag(null, TAG_GUEST_RESTRICTIONS); } - serializer.endTag(null, TAG_GUEST_RESTRICTIONS); int[] userIdsToWrite; synchronized (mUsersLock) { userIdsToWrite = new int[mUsers.size()]; @@ -4609,6 +4660,19 @@ public class UserManagerService extends IUserManager.Stub { localRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) { globalRestrictions = UserRestrictionsUtils.readRestrictions(parser); + } else if (TAG_GUEST_RESTRICTIONS.equals(tag)) { + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.END_TAG) { + if (type == XmlPullParser.START_TAG) { + if (parser.getName().equals(TAG_RESTRICTIONS)) { + synchronized (mGuestRestrictions) { + UserRestrictionsUtils + .readRestrictions(parser, mGuestRestrictions); + } + } + break; + } + } } else if (TAG_ACCOUNT.equals(tag)) { type = parser.next(); if (type == XmlPullParser.TEXT) { @@ -4869,7 +4933,7 @@ public class UserManagerService extends IUserManager.Stub { @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t, @Nullable Object token) throws UserManager.CheckedUserOperationException { - + String truncatedName = truncateString(name); final UserTypeDetails userTypeDetails = mUserTypes.get(userType); if (userTypeDetails == null) { throwCheckedUserOperationException( @@ -4904,8 +4968,8 @@ public class UserManagerService extends IUserManager.Stub { // Try to use a pre-created user (if available). if (!preCreate && parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails)) { - final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, name, - token); + final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, + truncatedName, token); if (preCreatedUser != null) { return preCreatedUser; } @@ -5000,7 +5064,7 @@ public class UserManagerService extends IUserManager.Stub { flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE; } - userInfo = new UserInfo(userId, name, null, flags, userType); + userInfo = new UserInfo(userId, truncatedName, null, flags, userType); userInfo.serialNumber = mNextSerialNumber++; userInfo.creationTime = getCreationTime(); userInfo.partial = true; @@ -6456,8 +6520,8 @@ public class UserManagerService extends IUserManager.Stub { Slog.e(LOG_TAG, "No such user for settings seed data u=" + userId); return; } - userData.seedAccountName = accountName; - userData.seedAccountType = accountType; + userData.seedAccountName = truncateString(accountName); + userData.seedAccountType = truncateString(accountType); userData.seedAccountOptions = accountOptions; userData.persistSeedData = persist; } diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index 85b60a07b003..29e0c35d88bd 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -295,6 +295,8 @@ public final class UserTypeFactory { .setCredentialShareableWithParent(false) .setMediaSharedWithParent(false) .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE) + .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE) + .setHideInSettingsInQuietMode(true) .setCrossProfileIntentFilterAccessControl( UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM) .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)); diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index ebc7163cd05c..b83421fe78d7 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -42,6 +42,7 @@ import android.os.PackageTagsList; import android.os.Process; import android.os.SystemProperties; import android.os.UserHandle; +import android.permission.flags.Flags; import android.service.voice.VoiceInteractionManagerInternal; import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; import android.text.TextUtils; @@ -75,9 +76,6 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat private static final boolean SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED = SystemProperties.getBoolean("ro.hotword.detection_service_required", false); - //TODO(b/289087412): import this from the flag value in set up in device config. - private static final boolean IS_VOICE_ACTIVATION_OP_ENABLED = false; - @NonNull private final Object mLock = new Object(); @@ -212,7 +210,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat * @return the op that should be noted for the voice activations of the app by detected hotword. */ public static int getVoiceActivationOp() { - if (IS_VOICE_ACTIVATION_OP_ENABLED) { + if (Flags.voiceActivationPermissionApis()) { return OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; } return OP_RECORD_AUDIO_HOTWORD; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 097656cac7f7..3a6664a72439 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -333,6 +333,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0; static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1; + // must match: config_shortPressOnSettingsBehavior in config.xml + static final int SHORT_PRESS_SETTINGS_NOTHING = 0; + static final int SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL = 1; + static final int LAST_SHORT_PRESS_SETTINGS_BEHAVIOR = SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL; + static final int PENDING_KEY_NULL = -1; // Must match: config_shortPressOnStemPrimaryBehavior in config.xml @@ -611,6 +616,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // What we do when the user double-taps on home int mDoubleTapOnHomeBehavior; + // What we do when the user presses on settings + int mShortPressOnSettingsBehavior; + // Must match config_primaryShortPressTargetActivity in config.xml ComponentName mPrimaryShortPressTargetActivity; @@ -2766,6 +2774,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE; } + + mShortPressOnSettingsBehavior = res.getInteger( + com.android.internal.R.integer.config_shortPressOnSettingsBehavior); + if (mShortPressOnSettingsBehavior < SHORT_PRESS_SETTINGS_NOTHING + || mShortPressOnSettingsBehavior > LAST_SHORT_PRESS_SETTINGS_BEHAVIOR) { + mShortPressOnSettingsBehavior = SHORT_PRESS_SETTINGS_NOTHING; + } } private void updateSettings() { @@ -3632,6 +3647,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in" + " interceptKeyBeforeQueueing"); return true; + case KeyEvent.KEYCODE_SETTINGS: + if (mShortPressOnSettingsBehavior == SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL) { + if (!down) { + toggleNotificationPanel(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL); + } + return true; + } + break; } if (isValidGlobalKey(keyCode) && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { @@ -6272,6 +6296,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print("mLongPressOnPowerBehavior="); pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior)); pw.print(prefix); + pw.print("mShortPressOnSettingsBehavior="); + pw.println(shortPressOnSettingsBehaviorToString(mShortPressOnSettingsBehavior)); + pw.print(prefix); pw.print("mLongPressOnPowerAssistantTimeoutMs="); pw.println(mLongPressOnPowerAssistantTimeoutMs); pw.print(prefix); @@ -6470,6 +6497,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private static String shortPressOnSettingsBehaviorToString(int behavior) { + switch (behavior) { + case SHORT_PRESS_SETTINGS_NOTHING: + return "SHORT_PRESS_SETTINGS_NOTHING"; + case SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL: + return "SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL"; + default: + return Integer.toString(behavior); + } + } + private static String veryLongPressOnPowerBehaviorToString(int behavior) { switch (behavior) { case VERY_LONG_PRESS_POWER_NOTHING: diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java index c908acdd1d6c..d5bc91278aa8 100644 --- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java +++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java @@ -24,9 +24,10 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; -import android.security.keymaster.IKeyAttestationApplicationIdProvider; -import android.security.keymaster.KeyAttestationApplicationId; -import android.security.keymaster.KeyAttestationPackageInfo; +import android.security.keystore.IKeyAttestationApplicationIdProvider; +import android.security.keystore.KeyAttestationApplicationId; +import android.security.keystore.KeyAttestationPackageInfo; +import android.security.keystore.Signature; /** * @hide @@ -64,14 +65,25 @@ public class KeyAttestationApplicationIdProviderService for (int i = 0; i < packageNames.length; ++i) { PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i], PackageManager.GET_SIGNATURES, userId); - keyAttestationPackageInfos[i] = new KeyAttestationPackageInfo(packageNames[i], - packageInfo.getLongVersionCode(), packageInfo.signatures); + KeyAttestationPackageInfo pInfo = new KeyAttestationPackageInfo(); + pInfo.packageName = new String(packageNames[i]); + pInfo.versionCode = packageInfo.getLongVersionCode(); + pInfo.signatures = new Signature[packageInfo.signatures.length]; + for (int index = 0; index < packageInfo.signatures.length; index++) { + Signature sign = new Signature(); + sign.data = packageInfo.signatures[index].toByteArray(); + pInfo.signatures[index] = sign; + } + + keyAttestationPackageInfos[i] = pInfo; } } catch (NameNotFoundException nnfe) { throw new RemoteException(nnfe.getMessage()); } finally { Binder.restoreCallingIdentity(token); } - return new KeyAttestationApplicationId(keyAttestationPackageInfos); + KeyAttestationApplicationId attestAppId = new KeyAttestationApplicationId(); + attestAppId.packageInfos = keyAttestationPackageInfos; + return attestAppId; } } diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java index 3d89afaae88f..becbbf2ea76a 100644 --- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java +++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java @@ -25,6 +25,8 @@ import android.os.VibratorInfo; import android.util.Slog; import android.util.SparseArray; import android.view.HapticFeedbackConstants; +import android.view.flags.FeatureFlags; +import android.view.flags.FeatureFlagsImpl; import com.android.internal.annotations.VisibleForTesting; @@ -54,6 +56,7 @@ public final class HapticFeedbackVibrationProvider { // If present and valid, a vibration here will be used for an effect. // Otherwise, the system's default vibration will be used. @Nullable private final SparseArray<VibrationEffect> mHapticCustomizations; + private final FeatureFlags mViewFeatureFlags; /** @hide */ public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) { @@ -62,14 +65,16 @@ public final class HapticFeedbackVibrationProvider { /** @hide */ public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) { - this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo)); + this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo), + new FeatureFlagsImpl()); } /** @hide */ @VisibleForTesting HapticFeedbackVibrationProvider( Resources res, VibratorInfo vibratorInfo, - @Nullable SparseArray<VibrationEffect> hapticCustomizations) { + @Nullable SparseArray<VibrationEffect> hapticCustomizations, + FeatureFlags viewFeatureFlags) { mVibratorInfo = vibratorInfo; mHapticTextHandleEnabled = res.getBoolean( com.android.internal.R.bool.config_enableHapticTextHandle); @@ -78,6 +83,7 @@ public final class HapticFeedbackVibrationProvider { hapticCustomizations = null; } mHapticCustomizations = hapticCustomizations; + mViewFeatureFlags = viewFeatureFlags; mSafeModeEnabledVibrationEffect = effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED) @@ -201,12 +207,16 @@ public final class HapticFeedbackVibrationProvider { default: attrs = TOUCH_VIBRATION_ATTRIBUTES; } + + int flags = 0; if (bypassVibrationIntensitySetting) { - attrs = new VibrationAttributes.Builder(attrs) - .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .build(); + flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF; + } + if (shouldBypassInterruptionPolicy(effectId, mViewFeatureFlags)) { + flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY; } - return attrs; + + return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build(); } /** Dumps relevant state. */ @@ -295,4 +305,19 @@ public final class HapticFeedbackVibrationProvider { return null; } } + + private static boolean shouldBypassInterruptionPolicy( + int effectId, FeatureFlags viewFeatureFlags) { + switch (effectId) { + case HapticFeedbackConstants.SCROLL_TICK: + case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: + case HapticFeedbackConstants.SCROLL_LIMIT: + // The SCROLL_* constants should bypass interruption filter, so that scroll haptics + // can play regardless of focus modes like DND. Guard this behavior by the feature + // flag controlling the general scroll feedback APIs. + return viewFeatureFlags.scrollFeedbackApi(); + default: + return false; + } + } } diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index ee3d697cddb8..45bd1521bc35 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -448,6 +448,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { String reason, IBinder token) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason); try { + attrs = fixupVibrationAttributes(attrs, effect); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.VIBRATE, "vibrate"); return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token); @@ -457,7 +458,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg, - @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, + @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason); try { @@ -468,7 +469,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } private HalVibration vibrateInternal(int uid, int displayId, String opPkg, - @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, + @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token) { if (token == null) { Slog.e(TAG, "token must not be null"); @@ -478,7 +479,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (!isEffectValid(effect)) { return null; } - attrs = fixupVibrationAttributes(attrs, effect); // Create Vibration.Stats as close to the received request as possible, for tracking. HalVibration vib = new HalVibration(token, effect, new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason)); diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java index 10a2b9717555..05da9dfe7921 100644 --- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java +++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java @@ -15,6 +15,9 @@ */ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -23,6 +26,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.graphics.Point; @@ -78,7 +82,14 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, protected final WindowManagerService mService; protected final float mHighResSnapshotScale; - private final Rect mTmpRect = new Rect(); + + /** + * The transition change info of the target to capture screenshot. It is only non-null when + * capturing a snapshot with a given change info. It must be cleared after + * {@link #recordSnapshotInner} is done. + */ + protected Transition.ChangeInfo mCurrentChangeInfo; + /** * Flag indicating whether we are running on an Android TV device. */ @@ -137,41 +148,35 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, protected abstract boolean use16BitFormat(); /** - * This is different than {@link #recordSnapshotInner(TYPE, boolean)} because it doesn't store + * This is different than {@link #recordSnapshotInner(TYPE)} because it doesn't store * the snapshot to the cache and returns the TaskSnapshot immediately. * * This is only used for testing so the snapshot content can be verified. */ - // TODO(b/264551777): clean up the "snapshotHome" argument @VisibleForTesting - TaskSnapshot captureSnapshot(TYPE source, boolean snapshotHome) { + TaskSnapshot captureSnapshot(TYPE source) { final TaskSnapshot snapshot; - if (snapshotHome) { - snapshot = snapshot(source); - } else { - switch (getSnapshotMode(source)) { - case SNAPSHOT_MODE_NONE: - return null; - case SNAPSHOT_MODE_APP_THEME: - snapshot = drawAppThemeSnapshot(source); - break; - case SNAPSHOT_MODE_REAL: - snapshot = snapshot(source); - break; - default: - snapshot = null; - break; - } + switch (getSnapshotMode(source)) { + case SNAPSHOT_MODE_NONE: + return null; + case SNAPSHOT_MODE_APP_THEME: + snapshot = drawAppThemeSnapshot(source); + break; + case SNAPSHOT_MODE_REAL: + snapshot = snapshot(source); + break; + default: + snapshot = null; + break; } return snapshot; } - final TaskSnapshot recordSnapshotInner(TYPE source, boolean allowSnapshotHome) { + final TaskSnapshot recordSnapshotInner(TYPE source) { if (shouldDisableSnapshots()) { return null; } - final boolean snapshotHome = allowSnapshotHome && source.isActivityTypeHome(); - final TaskSnapshot snapshot = captureSnapshot(source, snapshotHome); + final TaskSnapshot snapshot = captureSnapshot(source); if (snapshot == null) { return null; } @@ -189,30 +194,32 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, @VisibleForTesting int getSnapshotMode(TYPE source) { - final ActivityRecord topChild = getTopActivity(source); - if (!source.isActivityTypeStandardOrUndefined() && !source.isActivityTypeAssistant()) { + final int type = source.getActivityType(); + if (type == ACTIVITY_TYPE_RECENTS || type == ACTIVITY_TYPE_DREAM) { return SNAPSHOT_MODE_NONE; - } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) { - return SNAPSHOT_MODE_APP_THEME; - } else { + } + if (type == ACTIVITY_TYPE_HOME) { return SNAPSHOT_MODE_REAL; } + final ActivityRecord topChild = getTopActivity(source); + if (topChild != null && topChild.shouldUseAppThemeSnapshot()) { + return SNAPSHOT_MODE_APP_THEME; + } + return SNAPSHOT_MODE_REAL; } @Nullable TaskSnapshot snapshot(TYPE source) { - return snapshot(source, PixelFormat.UNKNOWN); - } - - @Nullable - TaskSnapshot snapshot(TYPE source, int pixelFormat) { TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); - if (!prepareTaskSnapshot(source, pixelFormat, builder)) { + final Rect crop = prepareTaskSnapshot(source, builder); + if (crop == null) { // Failed some pre-req. Has been logged. return null; } - final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = - createSnapshot(source, builder); + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot"); + final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshot(source, + mHighResSnapshotScale, crop, builder); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); if (screenshotBuffer == null) { // Failed to acquire image. Has been logged. return null; @@ -225,37 +232,13 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, @Nullable ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source, - TaskSnapshot.Builder builder) { - Point taskSize = new Point(); - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot"); - final ScreenCapture.ScreenshotHardwareBuffer taskSnapshot = createSnapshot(source, - mHighResSnapshotScale, builder.getPixelFormat(), taskSize, builder); - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - builder.setTaskSize(taskSize); - return taskSnapshot; - } - - @Nullable - ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source, - float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) { + float scaleFraction, Rect crop, TaskSnapshot.Builder builder) { if (source.getSurfaceControl() == null) { if (DEBUG_SCREENSHOT) { Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + source); } return null; } - mTmpRect.setEmpty(); - if (source.mTransitionController.inFinishingTransition(source)) { - final Transition.ChangeInfo changeInfo = source.mTransitionController - .mFinishingTransition.mChanges.get(source); - if (changeInfo != null) { - mTmpRect.set(changeInfo.mAbsoluteBounds); - } - } - if (mTmpRect.isEmpty()) { - source.getBounds(mTmpRect); - } - mTmpRect.offsetTo(0, 0); SurfaceControl[] excludeLayers; final WindowState imeWindow = source.getDisplayContent().mInputMethodWindow; // Exclude IME window snapshot when IME isn't proper to attach to app. @@ -281,12 +264,8 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible()); final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = ScreenCapture.captureLayersExcluding( - source.getSurfaceControl(), mTmpRect, scaleFraction, - pixelFormat, excludeLayers); - if (outTaskSize != null) { - outTaskSize.x = mTmpRect.width(); - outTaskSize.y = mTmpRect.height(); - } + source.getSurfaceControl(), crop, scaleFraction, + builder.getPixelFormat(), excludeLayers); final HardwareBuffer buffer = screenshotBuffer == null ? null : screenshotBuffer.getHardwareBuffer(); if (isInvalidHardwareBuffer(buffer)) { @@ -305,17 +284,16 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, * information from the task and populates the builder. * * @param source the window to capture - * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to - * automatically select * @param builder the snapshot builder to populate * * @return true if the state of the task is ok to proceed */ @VisibleForTesting - boolean prepareTaskSnapshot(TYPE source, int pixelFormat, TaskSnapshot.Builder builder) { + @Nullable + Rect prepareTaskSnapshot(TYPE source, TaskSnapshot.Builder builder) { final Pair<ActivityRecord, WindowState> result = checkIfReadyToSnapshot(source); if (result == null) { - return false; + return null; } final ActivityRecord activity = result.first; final WindowState mainWindow = result.second; @@ -329,6 +307,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setLetterboxInsets(letterboxInsets); final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE; final boolean isShowWallpaper = mainWindow.hasWallpaper(); + int pixelFormat = builder.getPixelFormat(); if (pixelFormat == PixelFormat.UNKNOWN) { pixelFormat = use16BitFormat() && activity.fillsParent() && !(isWindowTranslucent && isShowWallpaper) @@ -340,11 +319,29 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setTopActivityComponent(activity.mActivityComponent); builder.setPixelFormat(pixelFormat); builder.setIsTranslucent(isTranslucent); - builder.setOrientation(activity.getTask().getConfiguration().orientation); - builder.setRotation(activity.getTask().getDisplayContent().getRotation()); builder.setWindowingMode(source.getWindowingMode()); builder.setAppearance(getAppearance(source)); - return true; + + final Configuration taskConfig = activity.getTask().getConfiguration(); + final int displayRotation = taskConfig.windowConfiguration.getDisplayRotation(); + final Rect outCrop = new Rect(); + final Transition.ChangeInfo changeInfo = mCurrentChangeInfo; + if (changeInfo != null && changeInfo.mRotation != displayRotation) { + // For example, the source is closing and display rotation changes at the same time. + // The snapshot should record the state in previous rotation. + outCrop.set(changeInfo.mAbsoluteBounds); + builder.setRotation(changeInfo.mRotation); + builder.setOrientation(changeInfo.mAbsoluteBounds.height() + >= changeInfo.mAbsoluteBounds.width() + ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE); + } else { + outCrop.set(taskConfig.windowConfiguration.getBounds()); + builder.setRotation(displayRotation); + builder.setOrientation(taskConfig.orientation); + } + outCrop.offsetTo(0, 0); + builder.setTaskSize(new Point(outCrop.right, outCrop.bottom)); + return outCrop; } /** diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index ed10346d8495..9ffbc8edd660 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5367,18 +5367,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // rather than just direct membership. inFinishingTransition = mTransitionController.inFinishingTransition(this); if (!inFinishingTransition && (visible || !mDisplayContent.isSleeping())) { - Slog.e(TAG, "setVisibility=" + visible - + " while transition is not collecting or finishing " - + this + " caller=" + Debug.getCallers(8)); - // Force showing the parents because they may be hidden by previous transition. if (visible) { - final Transaction t = getSyncTransaction(); - for (WindowContainer<?> p = getParent(); p != null && p != mDisplayContent; - p = p.getParent()) { - if (p.mSurfaceControl != null) { - t.show(p.mSurfaceControl); - } - } + mTransitionController.onVisibleWithoutCollectingTransition(this, + Debug.getCallers(1, 1)); + } else { + Slog.w(TAG, "Set invisible without transition " + this); } } } diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index 148bf9bfdcd9..01b8bf794dae 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -280,7 +280,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity); } - final TaskSnapshot snapshot = recordSnapshotInner(activity, false /* allowSnapshotHome */); + final TaskSnapshot snapshot = recordSnapshotInner(activity); if (snapshot != null) { final int code = getSystemHashCode(activity); addUserSavedFile(code, activity.mUserId, snapshot); diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index c39b266a7701..4a5311b14397 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -577,7 +577,8 @@ public class ActivityStartController { .getRootTask(WINDOWING_MODE_UNDEFINED, activityType); if (rootTask == null) return false; final ActivityRecord r = rootTask.topRunningActivity(); - if (r == null || r.isVisibleRequested() || !r.attachedToProcess() + if (r == null || (r.isVisibleRequested() && rootTask.isTopRootTaskInDisplayArea()) + || !r.attachedToProcess() || !r.mActivityComponent.equals(intent.getComponent()) || !mService.isCallerRecents(r.getUid()) // Recents keeps invisible while device is locked. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a450b4d71a7e..0b673211a1c9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3858,11 +3858,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return null; } if (updateCache) { - return mWindowManager.mTaskSnapshotController.recordSnapshot(task, - true /* snapshotHome */); + return mWindowManager.mTaskSnapshotController.recordSnapshot(task); } else { - return mWindowManager.mTaskSnapshotController.captureSnapshot(task, - true /* snapshotHome */); + return mWindowManager.mTaskSnapshotController.captureSnapshot(task); } } } finally { @@ -6450,19 +6448,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (!restarting && hasVisibleActivities) { deferWindowLayout(); try { - final Task topTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); - if (topTask != null - && topTask.topRunningActivity(true /* focusableOnly */) == null) { - topTask.adjustFocusToNextFocusableTask("handleAppDied"); - } - if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) { - // If there was nothing to resume, and we are not already restarting - // this process, but there is a visible activity that is hosted by the - // process...then make sure all visible activities are running, taking - // care of restarting this process. - mRootWindowContainer.ensureActivitiesVisible(null, 0, - !PRESERVE_WINDOWS); - } + mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed( + "handleAppDied"); } finally { continueWindowLayout(); } @@ -6903,7 +6890,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) { synchronized (mGlobalLock) { - return mRootWindowContainer.finishTopCrashedActivities(crashedApp, reason); + deferWindowLayout(); + try { + final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities( + crashedApp, reason); + if (finishedTask != null) { + mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(reason); + return finishedTask.mTaskId; + } + return INVALID_TASK_ID; + } finally { + continueWindowLayout(); + } } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 9fd472057e43..12e1e2cd1e41 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -47,6 +47,7 @@ import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; @@ -1592,6 +1593,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } private void removePinnedRootTaskInSurfaceTransaction(Task rootTask) { + rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_TO_BACK, 0 /* flags */, + rootTask, rootTask.mDisplayContent, null /* remoteTransition */, + null /* displayChange */); /** * Workaround: Force-stop all the activities in the root pinned task before we reparent them * to the fullscreen root task. This is to guarantee that when we are removing a root task, diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index c2885dab12f0..42376685498a 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -163,7 +163,8 @@ class BackNavigationController { if (window == null) { EmbeddedWindowController.EmbeddedWindow embeddedWindow = - wmService.mEmbeddedWindowController.getByFocusToken(focusedWindowToken); + wmService.mEmbeddedWindowController.getByInputTransferToken( + focusedWindowToken); if (embeddedWindow != null) { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window is embeddedWindow. Dispatch KEYCODE_BACK."); diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index 2439159164fd..73bcc8d94252 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -66,29 +66,29 @@ import java.util.Map; public final class CompatModePackages { /** - * {@link CompatModePackages#DOWNSCALED_INVERSE} is the gatekeeper of all per-app buffer inverse - * downscale changes. Enabling this change will allow the following scaling factors: - * {@link CompatModePackages#DOWNSCALE_90} - * {@link CompatModePackages#DOWNSCALE_85} - * {@link CompatModePackages#DOWNSCALE_80} - * {@link CompatModePackages#DOWNSCALE_75} - * {@link CompatModePackages#DOWNSCALE_70} - * {@link CompatModePackages#DOWNSCALE_65} - * {@link CompatModePackages#DOWNSCALE_60} - * {@link CompatModePackages#DOWNSCALE_55} - * {@link CompatModePackages#DOWNSCALE_50} - * {@link CompatModePackages#DOWNSCALE_45} - * {@link CompatModePackages#DOWNSCALE_40} - * {@link CompatModePackages#DOWNSCALE_35} - * {@link CompatModePackages#DOWNSCALE_30} + * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is the gatekeeper of all per-app buffer + * inverse downscale changes. Enabling this change will allow the following scaling factors: + * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> + * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> + * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> + * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> + * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> + * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> + * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> + * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> + * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> + * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> + * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> + * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> + * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> * - * If {@link CompatModePackages#DOWNSCALED_INVERSE} is enabled for an app package, then the app - * will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both 1/0.8 and - * 1/0.7 (* 100%) were enabled. + * If <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is enabled for an app package, then + * the app will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both + * 1/0.8 and 1/0.7 (* 100%) were enabled. * - * When both {@link CompatModePackages#DOWNSCALED_INVERSE} - * and {@link CompatModePackages#DOWNSCALED} are enabled, then - * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence. + * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> + * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then + * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. */ @ChangeId @Disabled @@ -96,29 +96,29 @@ public final class CompatModePackages { public static final long DOWNSCALED_INVERSE = 273564678L; // This is a Bug ID. /** - * {@link CompatModePackages#DOWNSCALED} is the gatekeeper of all per-app buffer downscaling + * <a href="#DOWNSCALED">DOWNSCALED</a> is the gatekeeper of all per-app buffer downscaling * changes. Enabling this change will allow the following scaling factors: - * {@link CompatModePackages#DOWNSCALE_90} - * {@link CompatModePackages#DOWNSCALE_85} - * {@link CompatModePackages#DOWNSCALE_80} - * {@link CompatModePackages#DOWNSCALE_75} - * {@link CompatModePackages#DOWNSCALE_70} - * {@link CompatModePackages#DOWNSCALE_65} - * {@link CompatModePackages#DOWNSCALE_60} - * {@link CompatModePackages#DOWNSCALE_55} - * {@link CompatModePackages#DOWNSCALE_50} - * {@link CompatModePackages#DOWNSCALE_45} - * {@link CompatModePackages#DOWNSCALE_40} - * {@link CompatModePackages#DOWNSCALE_35} - * {@link CompatModePackages#DOWNSCALE_30} + * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> + * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> + * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> + * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> + * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> + * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> + * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> + * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> + * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> + * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> + * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> + * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> + * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> * - * If {@link CompatModePackages#DOWNSCALED} is enabled for an app package, then the app will be + * If <a href="#DOWNSCALED">DOWNSCALED</a> is enabled for an app package, then the app will be * forcibly resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were * enabled. * - * When both {@link CompatModePackages#DOWNSCALED_INVERSE} - * and {@link CompatModePackages#DOWNSCALED} are enabled, then - * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence. + * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> + * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then + * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. */ @ChangeId @Disabled @@ -126,12 +126,13 @@ public final class CompatModePackages { public static final long DOWNSCALED = 168419799L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_90} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> for a package will force the app to assume it's * running on a display with 90% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 111.11% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 111.11% the vertical and horizontal resolution of + * the real display */ @ChangeId @Disabled @@ -139,12 +140,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_90 = 182811243L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_85} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> for a package will force the app to assume it's * running on a display with 85% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 117.65% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 117.65% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -152,12 +154,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_85 = 189969734L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_80} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> for a package will force the app to assume it's * running on a display with 80% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 125% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 125% the vertical and horizontal resolution of the real + * display */ @ChangeId @Disabled @@ -165,12 +168,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_80 = 176926753L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_75} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> for a package will force the app to assume it's * running on a display with 75% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 133.33% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 133.33% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -178,12 +182,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_75 = 189969779L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_70} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> for a package will force the app to assume it's * running on a display with 70% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 142.86% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 142.86% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -191,12 +196,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_70 = 176926829L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_65} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> for a package will force the app to assume it's * running on a display with 65% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 153.85% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 153.85% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -204,12 +210,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_65 = 189969744L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_60} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> for a package will force the app to assume it's * running on a display with 60% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 166.67% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 166.67% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -217,12 +224,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_60 = 176926771L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_55} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> for a package will force the app to assume it's * running on a display with 55% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 181.82% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 181.82% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -230,12 +238,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_55 = 189970036L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_50} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> for a package will force the app to assume it's * running on a display with 50% vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 200% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 200% the vertical and horizontal resolution of the real + * display */ @ChangeId @Disabled @@ -243,12 +252,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_50 = 176926741L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_45} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> for a package will force the app to assume it's * running on a display with 45% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 222.22% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 222.22% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -256,12 +266,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_45 = 189969782L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_40} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> for a package will force the app to assume it's * running on a display with 40% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 250% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 250% the vertical and horizontal resolution of the real + * display */ @ChangeId @Disabled @@ -269,12 +280,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_40 = 189970038L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_35} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> for a package will force the app to assume it's * running on a display with 35% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 285.71% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 285.71% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -282,12 +294,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_35 = 189969749L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_30} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> for a package will force the app to assume it's * running on a display with 30% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 333.33% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 333.33% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index f6fe9b1dc63d..b7b5c2af0e3e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -218,6 +218,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.DisplayShape; import android.view.Gravity; +import android.view.IDecorViewGestureListener; import android.view.IDisplayWindowInsetsController; import android.view.ISystemGestureExclusionListener; import android.view.IWindow; @@ -471,6 +472,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp private final RemoteCallbackList<ISystemGestureExclusionListener> mSystemGestureExclusionListeners = new RemoteCallbackList<>(); + private final RemoteCallbackList<IDecorViewGestureListener> mDecorViewGestureListener = + new RemoteCallbackList<>(); private final Region mSystemGestureExclusion = new Region(); private boolean mSystemGestureExclusionWasRestricted = false; private final Region mSystemGestureExclusionUnrestricted = new Region(); @@ -5968,6 +5971,27 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mSystemGestureExclusionListeners.unregister(listener); } + void registerDecorViewGestureListener(IDecorViewGestureListener listener) { + mDecorViewGestureListener.register(listener); + } + + void unregisterDecorViewGestureListener(IDecorViewGestureListener listener) { + mDecorViewGestureListener.unregister(listener); + } + + void updateDecorViewGestureIntercepted(IBinder token, boolean intercepted) { + for (int i = mDecorViewGestureListener.beginBroadcast() - 1; i >= 0; --i) { + try { + mDecorViewGestureListener + .getBroadcastItem(i) + .onInterceptionChanged(token, intercepted); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to notify DecorViewGestureListener", e); + } + } + mDecorViewGestureListener.finishBroadcast(); + } + void updateKeepClearAreas() { final Set<Rect> restrictedKeepClearAreas = new ArraySet<>(); final Set<Rect> unrestrictedKeepClearAreas = new ArraySet<>(); diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 0f1a1053716e..7af4aadb2f0e 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -48,7 +48,6 @@ import android.hardware.input.InputManagerGlobal; import android.os.Binder; import android.os.Build; import android.os.IBinder; -import android.os.InputConfig; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -186,6 +185,10 @@ class DragState { // Crop the input surface to the display size. mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); + // Make trusted overlay to not block any touches while D&D ongoing and allowing + // touches to pass through to windows underneath. This allows user to interact with the + // UI to navigate while dragging. + h.setTrustedOverlay(mTransaction, mInputSurface, true); mTransaction.show(mInputSurface) .setInputWindowInfo(mInputSurface, h) .setLayer(mInputSurface, Integer.MAX_VALUE) @@ -377,11 +380,6 @@ class DragState { mDragWindowHandle.ownerUid = MY_UID; mDragWindowHandle.scaleFactor = 1.0f; - // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing - // touches to pass through to windows underneath. This allows user to interact with the - // UI to navigate while dragging. - mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY; - // The drag window cannot receive new touches. mDragWindowHandle.touchableRegion.setEmpty(); diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index c9bae127b800..275396f459bd 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -45,8 +45,8 @@ class EmbeddedWindowController { private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM; /* maps input token to an embedded window */ private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>(); - private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken = - new ArrayMap<>(); + private ArrayMap<IBinder /*input transfer token */, EmbeddedWindow> + mWindowsByInputTransferToken = new ArrayMap<>(); private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken = new ArrayMap<>(); private final Object mGlobalLock; @@ -67,14 +67,14 @@ class EmbeddedWindowController { void add(IBinder inputToken, EmbeddedWindow window) { try { mWindows.put(inputToken, window); - final IBinder focusToken = window.getFocusGrantToken(); - mWindowsByFocusToken.put(focusToken, window); + final IBinder inputTransferToken = window.getInputTransferToken(); + mWindowsByInputTransferToken.put(inputTransferToken, window); mWindowsByWindowToken.put(window.getWindowToken(), window); updateProcessController(window); window.mClient.asBinder().linkToDeath(()-> { synchronized (mGlobalLock) { mWindows.remove(inputToken); - mWindowsByFocusToken.remove(focusToken); + mWindowsByInputTransferToken.remove(inputTransferToken); } }, 0); } catch (RemoteException e) { @@ -105,7 +105,7 @@ class EmbeddedWindowController { EmbeddedWindow ew = mWindows.valueAt(i); if (ew.mClient.asBinder() == client.asBinder()) { mWindows.removeAt(i).onRemoved(); - mWindowsByFocusToken.remove(ew.getFocusGrantToken()); + mWindowsByInputTransferToken.remove(ew.getInputTransferToken()); mWindowsByWindowToken.remove(ew.getWindowToken()); return; } @@ -117,7 +117,7 @@ class EmbeddedWindowController { EmbeddedWindow ew = mWindows.valueAt(i); if (ew.mHostWindowState == host) { mWindows.removeAt(i).onRemoved(); - mWindowsByFocusToken.remove(ew.getFocusGrantToken()); + mWindowsByInputTransferToken.remove(ew.getInputTransferToken()); mWindowsByWindowToken.remove(ew.getWindowToken()); } } @@ -127,8 +127,8 @@ class EmbeddedWindowController { return mWindows.get(inputToken); } - EmbeddedWindow getByFocusToken(IBinder focusGrantToken) { - return mWindowsByFocusToken.get(focusGrantToken); + EmbeddedWindow getByInputTransferToken(IBinder inputTransferToken) { + return mWindowsByInputTransferToken.get(inputTransferToken); } EmbeddedWindow getByWindowToken(IBinder windowToken) { @@ -153,7 +153,7 @@ class EmbeddedWindowController { * to request focus transfer to the embedded. This is not the input token since we don't * want to give clients access to each others input token. */ - private final IBinder mFocusGrantToken; + private final IBinder mInputTransferToken; private boolean mIsFocusable; @@ -171,7 +171,7 @@ class EmbeddedWindowController { */ EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken, WindowState hostWindowState, int ownerUid, int ownerPid, int windowType, - int displayId, IBinder focusGrantToken, String inputHandleName, + int displayId, IBinder inputTransferToken, String inputHandleName, boolean isFocusable) { mSession = session; mWmService = service; @@ -183,7 +183,7 @@ class EmbeddedWindowController { mOwnerPid = ownerPid; mWindowType = windowType; mDisplayId = displayId; - mFocusGrantToken = focusGrantToken; + mInputTransferToken = inputTransferToken; final String hostWindowName = (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString() : ""; @@ -260,8 +260,8 @@ class EmbeddedWindowController { return mOwnerUid; } - IBinder getFocusGrantToken() { - return mFocusGrantToken; + IBinder getInputTransferToken() { + return mInputTransferToken; } IBinder getInputChannelToken() { @@ -290,7 +290,7 @@ class EmbeddedWindowController { // Use null session since this is being granted by system server and doesn't // require the host session to be passed in mWmService.grantEmbeddedWindowFocus(null, mHostWindowState.mClient, - mFocusGrantToken, grantFocus); + mInputTransferToken, grantFocus); if (grantFocus) { // If granting focus to the embedded when tapped, we need to ensure the host // gains focus as well or the transfer won't take effect since it requires @@ -298,7 +298,7 @@ class EmbeddedWindowController { mHostWindowState.handleTapOutsideFocusInsideSelf(); } } else { - mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus); + mWmService.grantEmbeddedWindowFocus(mSession, mInputTransferToken, grantFocus); } } } diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 39622c1c5aaf..c21930dab5eb 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -74,7 +74,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mWindowHandle.ownerPid = WindowManagerService.MY_PID; mWindowHandle.ownerUid = WindowManagerService.MY_UID; mWindowHandle.scaleFactor = 1.0f; - mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY; + mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE; mInputSurface = mService.makeSurfaceBuilder( mService.mRoot.getDisplayContent(displayId).getSession()) @@ -129,12 +129,14 @@ class InputConsumerImpl implements IBinder.DeathRecipient { void show(SurfaceControl.Transaction t, WindowContainer w) { t.show(mInputSurface); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1); } void show(SurfaceControl.Transaction t, int layer) { t.show(mInputSurface); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, layer); } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 825d38b3eed7..af307ec3c2a9 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -675,6 +675,11 @@ final class InputMonitor { w.getKeyInterceptionInfo()); if (w.mWinAnimator.hasSurface()) { + // Update trusted overlay changes here because they are tied to input info. Input + // changes can be updated even if surfaces aren't. + inputWindowHandle.setTrustedOverlay(mInputTransaction, + w.mWinAnimator.mSurfaceController.mSurfaceControl, + w.isWindowTrustedOverlay()); populateInputWindowHandle(inputWindowHandle, w); setInputWindowInfoIfNeeded(mInputTransaction, w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); @@ -732,7 +737,7 @@ final class InputMonitor { new InputWindowHandle(null /* inputApplicationHandle */, displayId)); inputWindowHandle.setName(name); inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY); - inputWindowHandle.setTrustedOverlay(true); + inputWindowHandle.setTrustedOverlay(t, sc, true); populateOverlayInputInfo(inputWindowHandle); setInputWindowInfoIfNeeded(t, sc, inputWindowHandle); } diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java index 64b7a6064e45..90d81bd82087 100644 --- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -195,6 +195,11 @@ class InputWindowHandleWrapper { mChanged = true; } + void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc, + boolean trustedOverlay) { + mHandle.setTrustedOverlay(t, sc, trustedOverlay); + } + void setOwnerPid(int pid) { if (mHandle.ownerPid == pid) { return; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index def32a1bda87..82d4b90d06be 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -62,6 +62,7 @@ import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.IResultReceiver; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; import com.android.server.inputmethod.InputMethodManagerInternal; @@ -244,7 +245,8 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) { + public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, + IResultReceiver finishCb) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled); final long token = Binder.clearCallingIdentity(); @@ -257,6 +259,13 @@ public class RecentsAnimationController implements DeathRecipient { } finally { Binder.restoreCallingIdentity(token); } + if (finishCb != null) { + try { + finishCb.send(0, new Bundle()); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report animation finished", e); + } + } } @Override @@ -901,7 +910,8 @@ public class RecentsAnimationController implements DeathRecipient { for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final TaskAnimationAdapter adapter = mPendingAnimations.get(i); final Task task = adapter.mTask; - snapshotController.recordSnapshot(task, false /* allowSnapshotHome */); + if (task.isActivityTypeHome()) continue; + snapshotController.recordSnapshot(task); final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); if (snapshot != null) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 553375985ed6..cf6a1feef5ee 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -60,6 +60,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATE import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH; +import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; @@ -2017,6 +2018,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, @Nullable ActivityRecord launchIntoPipHostActivity, String reason, @Nullable Transition transition) { + moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, transition, + null /* bounds */); + } + + void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, + @Nullable ActivityRecord launchIntoPipHostActivity, String reason, + @Nullable Transition transition, @Nullable Rect bounds) { final TaskDisplayArea taskDisplayArea = r.getDisplayArea(); final Task task = r.getTask(); final Task rootTask; @@ -2053,7 +2061,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating // TODO(task-org): Figure-out more structured way to do this long term. - r.setWindowingMode(r.getWindowingMode()); + if (!isPip2ExperimentEnabled()) { + r.setWindowingMode(r.getWindowingMode()); + } final TaskFragment organizedTf = r.getOrganizedTaskFragment(); final boolean singleActivity = task.getNonFinishingActivityCount() == 1; @@ -2169,21 +2179,27 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // TODO(remove-legacy-transit): Move this to the `singleActivity` case when removing // legacy transit. rootTask.setWindowingMode(WINDOWING_MODE_PINNED); + if (isPip2ExperimentEnabled() && bounds != null) { + // set the final pip bounds in advance if pip2 is enabled + rootTask.setBounds(bounds); + } + // Set the launch bounds for launch-into-pip Activity on the root task. if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) { // Record the snapshot now, it will be later fetched for content-pip animation. // We do this early in the process to make sure the right snapshot is used for // entering content-pip animation. - mWindowManager.mTaskSnapshotController.recordSnapshot( - task, false /* allowSnapshotHome */); + mWindowManager.mTaskSnapshotController.recordSnapshot(task); rootTask.setBounds(r.pictureInPictureArgs.getSourceRectHint()); } rootTask.setDeferTaskAppear(false); - // After setting this, it is not expected to change activity configuration until the - // transition animation is finished. So the activity can keep consistent appearance - // when animating. - r.mWaitForEnteringPinnedMode = true; + if (!isPip2ExperimentEnabled()) { + // After setting this, it is not expected to change activity configuration until the + // transition animation is finished. So the activity can keep consistent appearance + // when animating. + r.mWaitForEnteringPinnedMode = true; + } // Reset the state that indicates it can enter PiP while pausing after we've moved it // to the root pinned task r.supportsEnterPipOnTaskSwitch = false; @@ -2198,7 +2214,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } finally { mService.continueWindowLayout(); try { - ensureActivitiesVisible(null, 0, false /* preserveWindows */); + if (!isPip2ExperimentEnabled()) { + ensureActivitiesVisible(null, 0, false /* preserveWindows */); + } } finally { transitionController.continueTransitionReady(); } @@ -2214,7 +2232,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> newTransition.setReady(rootTask, true /* ready */); } - resumeFocusedTasksTopActivities(); + if (!isPip2ExperimentEnabled()) { + resumeFocusedTasksTopActivities(); + } notifyActivityPipModeChanged(r.getTask(), r); } @@ -2299,19 +2319,36 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * * @param app The app that crashed. * @param reason Reason to perform this action. - * @return The task id that was finished in this root task, or INVALID_TASK_ID if none was - * finished. + * @return The finished task which was on top or visible, otherwise {@code null} if the crashed + * app doesn't have activity in visible task. */ - int finishTopCrashedActivities(WindowProcessController app, String reason) { + @Nullable + Task finishTopCrashedActivities(WindowProcessController app, String reason) { Task focusedRootTask = getTopDisplayFocusedRootTask(); final Task[] finishedTask = new Task[1]; forAllRootTasks(rootTask -> { + final boolean recordTopOrVisible = finishedTask[0] == null + && (focusedRootTask == rootTask || rootTask.isVisibleRequested()); final Task t = rootTask.finishTopCrashedActivityLocked(app, reason); - if (rootTask == focusedRootTask || finishedTask[0] == null) { + if (recordTopOrVisible) { finishedTask[0] = t; } }); - return finishedTask[0] != null ? finishedTask[0].mTaskId : INVALID_TASK_ID; + return finishedTask[0]; + } + + void ensureVisibilityOnVisibleActivityDiedOrCrashed(String reason) { + final Task topTask = getTopDisplayFocusedRootTask(); + if (topTask != null && topTask.topRunningActivity(true /* focusableOnly */) == null) { + // Move the next focusable task to front. + topTask.adjustFocusToNextFocusableTask(reason); + } + if (!resumeFocusedTasksTopActivities()) { + // It may be nothing to resume because there are pausing activities or all the top + // activities are resumed. Then it still needs to make sure all visible activities are + // running in case the tasks were reordered or there are non-top visible activities. + ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS); + } } boolean resumeFocusedTasksTopActivities() { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index bbe44c540c39..3775ccd79d4c 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -560,6 +560,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override + public void reportDecorViewGestureInterceptionChanged(IWindow window, boolean intercepted) { + final long ident = Binder.clearCallingIdentity(); + try { + mService.reportDecorViewGestureChanged(this, window, intercepted); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public void reportKeepClearAreasChanged(IWindow window, List<Rect> restricted, List<Rect> unrestricted) { if (!mSetsUnrestrictedKeepClearAreas && !unrestricted.isEmpty()) { @@ -884,8 +894,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, - int inputFeatures, IBinder windowToken, IBinder focusGrantToken, String inputHandleName, - InputChannel outInputChannel) { + int inputFeatures, IBinder windowToken, IBinder inputTransferToken, + String inputHandleName, InputChannel outInputChannel) { if (hostInputToken == null && !mCanAddInternalSystemWindow) { // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to // embedded windows without providing a host window input token @@ -896,7 +906,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { try { mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0, - type, inputFeatures, windowToken, focusGrantToken, inputHandleName, + type, inputFeatures, windowToken, inputTransferToken, inputHandleName, outInputChannel); } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index 37f9730d5443..2e7ff7a6b9b8 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -83,9 +83,10 @@ class SnapshotController { 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 */); + if (info.mContainer.isActivityTypeHome()) continue; + final Task task = info.mContainer.asTask(); + if (task != null && !task.isVisibleRequested()) { + mTaskSnapshotController.recordSnapshot(task, info); } // Won't need to capture activity snapshot in close transition. if (isTransitionClose) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 4eb42908c6d4..2b12e7497233 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -118,6 +118,7 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot mTmpTasks.clear(); for (int i = closingApps.size() - 1; i >= 0; i--) { final ActivityRecord activity = closingApps.valueAt(i); + if (activity.isActivityTypeHome()) continue; final Task task = activity.getTask(); if (task == null) continue; @@ -144,23 +145,32 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot } void snapshotTasks(ArraySet<Task> tasks) { - snapshotTasks(tasks, false /* allowSnapshotHome */); + for (int i = tasks.size() - 1; i >= 0; i--) { + recordSnapshot(tasks.valueAt(i)); + } } - TaskSnapshot recordSnapshot(Task task, boolean allowSnapshotHome) { - final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome(); - final TaskSnapshot snapshot = recordSnapshotInner(task, allowSnapshotHome); - if (!snapshotHome && snapshot != null) { - mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot); - task.onSnapshotChanged(snapshot); + /** + * The attributes of task snapshot are based on task configuration. But sometimes the + * configuration may have been changed during a transition, so supply the ChangeInfo that + * stored the previous appearance of the closing task. + */ + void recordSnapshot(Task task, Transition.ChangeInfo changeInfo) { + mCurrentChangeInfo = changeInfo; + try { + recordSnapshot(task); + } finally { + mCurrentChangeInfo = null; } - return snapshot; } - private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) { - for (int i = tasks.size() - 1; i >= 0; i--) { - recordSnapshot(tasks.valueAt(i), allowSnapshotHome); + TaskSnapshot recordSnapshot(Task task) { + final TaskSnapshot snapshot = recordSnapshotInner(task); + if (snapshot != null && !task.isActivityTypeHome()) { + mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot); + task.onSnapshotChanged(snapshot); } + return snapshot; } /** @@ -320,19 +330,22 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot if (displayContent == null) { return; } + // Allow taking snapshot of home when turning screen off to reduce the delay of waking from + // secure lock to home. + final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY + && mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId); mTmpTasks.clear(); - displayContent.forAllTasks(task -> { + displayContent.forAllLeafTasks(task -> { + if (!allowSnapshotHome && task.isActivityTypeHome()) { + return; + } // Since RecentsAnimation will handle task snapshot while switching apps with the best // capture timing (e.g. IME window capture), No need additional task capture while task // is controlled by RecentsAnimation. if (task.isVisible() && !isAnimatingByRecents(task)) { mTmpTasks.add(task); } - }); - // Allow taking snapshot of home when turning screen off to reduce the delay of waking from - // secure lock to home. - final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY - && mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId); - snapshotTasks(mTmpTasks, allowSnapshotHome); + }, true /* traverseTopToBottom */); + snapshotTasks(mTmpTasks); } } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 774be9e33a61..471dea846ef4 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -372,8 +372,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { parent.forAllTasks(t -> { // Skip transient-launch task if (t == transientRootTask) return false; - if (t.isVisibleRequested() && !t.isAlwaysOnTop() - && !t.getWindowConfiguration().tasksAreFloating()) { + if (t.isVisibleRequested() && !t.isAlwaysOnTop()) { if (t.isRootTask()) { mTransientHideTasks.add(t); } @@ -1197,7 +1196,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Commit activity becoming invisible: %s", ar); final SnapshotController snapController = mController.mSnapshotController; - if (mTransientLaunches != null && !task.isVisibleRequested()) { + if (mTransientLaunches != null && !task.isVisibleRequested() + && !task.isActivityTypeHome()) { final long startTimeNs = mLogger.mSendTimeNs; final long lastSnapshotTimeNs = snapController.mTaskSnapshotController .getSnapshotCaptureTime(task.mTaskId); @@ -1205,8 +1205,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // transition only if a snapshot was not already captured by request // during the transition if (lastSnapshotTimeNs < startTimeNs) { - snapController.mTaskSnapshotController - .recordSnapshot(task, false /* allowSnapshotHome */); + snapController.mTaskSnapshotController.recordSnapshot(task); } else { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Skipping post-transition snapshot for task %d", @@ -1392,7 +1391,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { dc.handleCompleteDeferredRemoval(); } validateKeyguardOcclusion(); - validateVisibility(); mState = STATE_FINISHED; // Rotation change may be deferred while there is a display change transition, so check @@ -2767,29 +2765,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } - private void validateVisibility() { - for (int i = mTargets.size() - 1; i >= 0; --i) { - if (reduceMode(mTargets.get(i).mReadyMode) != TRANSIT_CLOSE) { - return; - } - } - // All modes are CLOSE. The surfaces may be hidden by the animation unexpectedly. - // If the window container should be visible, then recover it. - mController.mStateValidators.add(() -> { - for (int i = mTargets.size() - 1; i >= 0; --i) { - final ChangeInfo change = mTargets.get(i); - if (!change.mContainer.isVisibleRequested() - || change.mContainer.mSurfaceControl == null) { - continue; - } - Slog.e(TAG, "Force show for visible " + change.mContainer - + " which may be hidden by transition unexpectedly"); - change.mContainer.getSyncTransaction().show(change.mContainer.mSurfaceControl); - change.mContainer.scheduleAnimation(); - } - }); - } - /** * Returns {@code true} if the transition and the corresponding transaction should be applied * on display thread. Currently, this only checks for display rotation change because the order diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 710913712aa0..e686f1b73c8c 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -721,6 +721,7 @@ class TransitionController { // set the pip task in the request if provided if (mCollectingTransition.getPipActivity() != null) { pipTaskInfo = mCollectingTransition.getPipActivity().getTask().getTaskInfo(); + mCollectingTransition.setPipActivity(null); } final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType, @@ -959,6 +960,36 @@ class TransitionController { mValidateDisplayVis.clear(); } + void onVisibleWithoutCollectingTransition(WindowContainer<?> wc, String caller) { + final boolean isPlaying = !mPlayingTransitions.isEmpty(); + Slog.e(TAG, "Set visible without transition " + wc + " playing=" + isPlaying + + " caller=" + caller); + if (!isPlaying) { + enforceSurfaceVisible(wc); + return; + } + // Update surface visibility after the playing transitions are finished, so the last + // visibility won't be replaced by the finish transaction of transition. + mStateValidators.add(() -> { + if (wc.isVisibleRequested()) { + enforceSurfaceVisible(wc); + } + }); + } + + private void enforceSurfaceVisible(WindowContainer<?> wc) { + if (wc.mSurfaceControl == null) return; + wc.getSyncTransaction().show(wc.mSurfaceControl); + // Force showing the parents because they may be hidden by previous transition. + for (WindowContainer<?> p = wc.getParent(); p != null && p != wc.mDisplayContent; + p = p.getParent()) { + if (p.mSurfaceControl != null) { + p.getSyncTransaction().show(p.mSurfaceControl); + } + } + wc.scheduleAnimation(); + } + /** * Called when the transition has a complete set of participants for its operation. In other * words, it is when the transition is "ready" but is still waiting for participants to draw. diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java new file mode 100644 index 000000000000..00b9b4c490a4 --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java @@ -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.server.wm; + +import com.android.window.flags.Flags; + +/** + * Utility class to read the flags used in the WindowManager server. + * + * It is not very cheap to read trunk stable flag, so having a centralized place to cache the flag + * values in the system server side. + * + * Flags should be defined in `core.java.android.window.flags` to allow access from client side. + * + * To override flag: + * adb shell device_config put [namespace] [package].[name] [true/false] + * adb reboot + * + * To access in wm: + * {@link WindowManagerService#mFlags} + * + * Notes: + * The system may use flags at anytime, so changing flags will only take effect after device + * reboot. Otherwise, it may result unexpected behavior, such as broken transition. + * When a flag needs to be read from both the server side and the client side, changing the flag + * value will result difference in server and client until device reboot. + */ +class WindowManagerFlags { + + /* Start Available Flags */ + + final boolean mSyncWindowConfigUpdateFlag = Flags.syncWindowConfigUpdateFlag(); + + /* End Available Flags */ +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 82452ccc3d06..52936ef28988 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -249,6 +249,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.ICrossWindowBlurEnabledListener; +import android.view.IDecorViewGestureListener; import android.view.IDisplayChangeWindowController; import android.view.IDisplayFoldListener; import android.view.IDisplayWindowInsetsController; @@ -546,6 +547,8 @@ public class WindowManagerService extends IWindowManager.Stub @VisibleForTesting WindowManagerPolicy mPolicy; + final WindowManagerFlags mFlags; + final IActivityManager mActivityManager; final ActivityManagerInternal mAmInternal; final UserManagerInternal mUmInternal; @@ -1152,6 +1155,7 @@ public class WindowManagerService extends IWindowManager.Stub mGlobalLock = atm.getGlobalLock(); mAtmService = atm; mContext = context; + mFlags = new WindowManagerFlags(); mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); mAllowBootMessages = showBootMsgs; mLimitedAlphaCompositing = context.getResources().getBoolean( @@ -2493,14 +2497,6 @@ public class WindowManagerService extends IWindowManager.Stub outInsetsState.set(win.getCompatInsetsState(), true /* copySources */); } - // TODO (b/298562855): Remove this after identifying the reason why the frame is empty. - if (win.mAttrs.providedInsets != null && win.getFrame().isEmpty()) { - Slog.w(TAG, "Empty frame of " + win - + " configChanged=" + configChanged - + " frame=" + win.getFrame().toShortString() - + " attrs=" + attrs); - } - ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b", win, focusMayChange); @@ -4637,8 +4633,9 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); if (displayContent == null) { - throw new IllegalArgumentException("Trying to register visibility event " - + "for invalid display: " + displayId); + throw new IllegalArgumentException( + "Trying to register system gesture exclusion event for invalid display: " + + displayId); } displayContent.registerSystemGestureExclusionListener(listener); } @@ -4650,13 +4647,64 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); if (displayContent == null) { - throw new IllegalArgumentException("Trying to register visibility event " - + "for invalid display: " + displayId); + throw new IllegalArgumentException( + "Trying to unregister system gesture exclusion event for invalid display: " + + displayId); } displayContent.unregisterSystemGestureExclusionListener(listener); } } + @Override + public void registerDecorViewGestureListener( + IDecorViewGestureListener listener, int displayId) { + if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT, + "registerDecorViewGestureListener()")) { + throw new SecurityException("Requires MONITOR_INPUT permission"); + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException( + "Trying to register DecorView gesture event listener" + + "for invalid display: " + + displayId); + } + displayContent.registerDecorViewGestureListener(listener); + } + } + + @Override + public void unregisterDecorViewGestureListener( + IDecorViewGestureListener listener, int displayId) { + if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT, + "unregisterSystemGestureExclusionListener()")) { + throw new SecurityException("Requires MONITOR_INPUT permission"); + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException( + "Trying to unregister DecorView gesture event listener" + + "for invalid display: " + + displayId); + } + displayContent.unregisterDecorViewGestureListener(listener); + } + } + + void reportDecorViewGestureChanged(Session session, IWindow window, boolean intercepted) { + synchronized (mGlobalLock) { + final WindowState win = + windowForClientLocked(session, window, false /* throwOnError */); + if (win == null) { + return; + } + win.getDisplayContent() + .updateDecorViewGestureIntercepted(win.mToken.token, intercepted); + } + } + void reportSystemGestureExclusionChanged(Session session, IWindow window, List<Rect> exclusionRects) { synchronized (mGlobalLock) { @@ -8803,7 +8851,7 @@ public class WindowManagerService extends IWindowManager.Stub void grantInputChannel(Session session, int callingUid, int callingPid, int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken, - IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { + IBinder inputTransferToken, String inputHandleName, InputChannel outInputChannel) { final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type); final InputApplicationHandle applicationHandle; final String name; @@ -8812,7 +8860,7 @@ public class WindowManagerService extends IWindowManager.Stub EmbeddedWindowController.EmbeddedWindow win = new EmbeddedWindowController.EmbeddedWindow(session, this, window, mInputToWindowMap.get(hostInputToken), callingUid, callingPid, - sanitizedType, displayId, focusGrantToken, inputHandleName, + sanitizedType, displayId, inputTransferToken, inputHandleName, (flags & FLAG_NOT_FOCUSABLE) == 0); win.openInputChannel(outInputChannel); mEmbeddedWindowController.add(outInputChannel.getToken(), win); @@ -8884,11 +8932,6 @@ public class WindowManagerService extends IWindowManager.Stub h.inputConfig |= InputConfig.NOT_FOCUSABLE; } - // Check private trusted overlay flag to set trustedOverlay field of input window handle. - if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { - h.inputConfig |= InputConfig.TRUSTED_OVERLAY; - } - h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; h.ownerUid = callingUid; h.ownerPid = callingPid; @@ -8908,6 +8951,8 @@ public class WindowManagerService extends IWindowManager.Stub } final SurfaceControl.Transaction t = mTransactionFactory.get(); + // Check private trusted overlay flag to set trustedOverlay field of input window handle. + h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0); t.setInputWindowInfo(surface, h); t.apply(); t.close(); @@ -9139,10 +9184,10 @@ public class WindowManagerService extends IWindowManager.Stub return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId); } - void grantEmbeddedWindowFocus(Session session, IBinder focusToken, boolean grantFocus) { + void grantEmbeddedWindowFocus(Session session, IBinder inputTransferToken, boolean grantFocus) { synchronized (mGlobalLock) { final EmbeddedWindowController.EmbeddedWindow embeddedWindow = - mEmbeddedWindowController.getByFocusToken(focusToken); + mEmbeddedWindowController.getByInputTransferToken(inputTransferToken); if (embeddedWindow == null) { Slog.e(TAG, "Embedded window not found"); return; @@ -9187,8 +9232,8 @@ public class WindowManagerService extends IWindowManager.Stub } } - void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetFocusToken, - boolean grantFocus) { + void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, + IBinder inputTransferToken, boolean grantFocus) { synchronized (mGlobalLock) { final WindowState hostWindow = windowForClientLocked(session, callingWindow, false /* throwOnError*/); @@ -9201,7 +9246,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } final EmbeddedWindowController.EmbeddedWindow embeddedWindow = - mEmbeddedWindowController.getByFocusToken(targetFocusToken); + mEmbeddedWindowController.getByInputTransferToken(inputTransferToken); if (embeddedWindow == null) { Slog.e(TAG, "Embedded window not found"); return; @@ -9430,7 +9475,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalArgumentException( "Failed to find matching task for taskId=" + taskId); } - taskSnapshot = mTaskSnapshotController.captureSnapshot(task, false); + taskSnapshot = mTaskSnapshotController.captureSnapshot(task); } } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index f8e9a56e54fd..dd9a88f72bde 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -41,6 +41,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK; @@ -1088,6 +1089,26 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } break; } + case HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK: { + final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); + Task pipTask = container.asTask(); + if (pipTask == null) { + break; + } + ActivityRecord[] pipActivity = new ActivityRecord[1]; + pipTask.forAllActivities((activity) -> { + if (activity.pictureInPictureArgs != null) { + pipActivity[0] = activity; + } + }); + + Rect entryBounds = hop.getBounds(); + mService.mRootWindowContainer.moveActivityToPinnedRootTask( + pipActivity[0], null /* launchIntoPipHostActivity */, + "moveActivityToPinnedRootTask", null /* transition */, entryBounds); + effects |= TRANSACT_EFFECTS_LIFECYCLE; + break; + } case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: { if (finishTransition == null) break; final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ebef606a8d60..4beec2bc79e6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -178,6 +178,7 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY; import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; +import static com.android.window.flags.Flags.surfaceTrustedOverlay; import android.annotation.CallSuper; import android.annotation.NonNull; @@ -1110,7 +1111,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mInputWindowHandle.setName(getName()); mInputWindowHandle.setPackageName(mAttrs.packageName); mInputWindowHandle.setLayoutParamsType(mAttrs.type); - mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s)); + if (!surfaceTrustedOverlay()) { + mInputWindowHandle.setTrustedOverlay(isWindowTrustedOverlay()); + } if (DEBUG) { Slog.v(TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); @@ -1185,12 +1188,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - boolean shouldWindowHandleBeTrusted(Session s) { + public boolean isWindowTrustedOverlay() { return InputMonitor.isTrustedOverlay(mAttrs.type) || ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0 - && s.mCanAddInternalSystemWindow) + && mSession.mCanAddInternalSystemWindow) || ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0 - && s.mCanCreateSystemApplicationOverlay); + && mSession.mCanCreateSystemApplicationOverlay); } int getTouchOcclusionMode() { @@ -5187,6 +5190,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateFrameRateSelectionPriorityIfNeeded(); updateScaleIfNeeded(); mWinAnimator.prepareSurfaceLocked(getSyncTransaction()); + if (surfaceTrustedOverlay()) { + getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay()); + } } super.prepareSurfaces(); } @@ -5939,7 +5945,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean isTrustedOverlay() { - return mInputWindowHandle.isTrustedOverlay(); + if (surfaceTrustedOverlay()) { + WindowState parentWindow = getParentWindow(); + return isWindowTrustedOverlay() || (parentWindow != null + && parentWindow.isWindowTrustedOverlay()); + } else { + return mInputWindowHandle.isTrustedOverlay(); + } } public boolean receiveFocusFromTapOutside() { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index ec5378f01ce3..bc70658a06c4 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -78,6 +78,7 @@ cc_library_static { "onload.cpp", ":lib_cachedAppOptimizer_native", ":lib_gameManagerService_native", + ":lib_oomConnection_native", ], include_dirs: [ @@ -122,6 +123,7 @@ cc_defaults { "libhardware_legacy", "libhidlbase", "libmeminfo", + "libmemevents", "libmemtrackproxy", "libmtp", "libnativehelper", @@ -239,3 +241,8 @@ filegroup { "com_android_server_app_GameManagerService.cpp", ], } + +filegroup { + name: "lib_oomConnection_native", + srcs: ["com_android_server_am_OomConnection.cpp",], +} diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp new file mode 100644 index 000000000000..e892d23460c9 --- /dev/null +++ b/services/core/jni/com_android_server_am_OomConnection.cpp @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#define LOG_TAG "OomConnection" + +#include <core_jni_helpers.h> +#include <jni.h> +#include <memevents/memevents.h> + +namespace android { + +// Used to cache the results of the JNI name lookup +static struct { + jclass clazz; + jmethodID ctor; +} sOomKillRecordInfo; + +static memevents::MemEventListener memevent_listener; + +/** + * Initialize listening and waiting for new out-of-memory (OOM) events to occur. + * Once a OOM event is detected, we then fetch the list of OOM kills, and return + * a corresponding java array with the information gathered. + * + * In the case that we encounter an error, we make sure to close the epfd, and + * the OOM file descriptor, by calling `deregisterAllEvents()`. + * + * @return list of `android.os.OomKillRecord` + * @throws java.lang.RuntimeException + */ +static jobjectArray android_server_am_OomConnection_waitOom(JNIEnv* env, jobject) { + const memevents::MemEvent oom_event = memevents::MemEvent::OOM_KILL; + if (!memevent_listener.registerEvent(oom_event)) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "listener failed to register to OOM events"); + return nullptr; + } + + memevents::MemEvent event_received; + do { + event_received = memevent_listener.listen(); + if (event_received == memevents::MemEvent::ERROR) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "listener received error event"); + return nullptr; + } + } while (event_received != oom_event); + + std::vector<memevents::OomKill> oom_events; + if (!memevent_listener.getOomEvents(oom_events)) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed to get OOM events"); + return nullptr; + } + + jobjectArray java_oom_array = + env->NewObjectArray(oom_events.size(), sOomKillRecordInfo.clazz, nullptr); + if (java_oom_array == NULL) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed to create OomKillRecord array"); + return nullptr; + } + + for (int i = 0; i < oom_events.size(); i++) { + const memevents::OomKill oom_event = oom_events[i]; + jstring process_name = env->NewStringUTF(oom_event.process_name); + if (process_name == NULL) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed creating java string for process name"); + } + jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor, + oom_event.timestamp_ms, oom_event.pid, oom_event.uid, + process_name, oom_event.oom_score_adj); + if (java_oom_kill == NULL) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed to create OomKillRecord object"); + return java_oom_array; + } + env->SetObjectArrayElement(java_oom_array, i, java_oom_kill); + } + return java_oom_array; +} + +static const JNINativeMethod sOomConnectionMethods[] = { + /* name, signature, funcPtr */ + {"waitOom", "()[Landroid/os/OomKillRecord;", + (void*)android_server_am_OomConnection_waitOom}, +}; + +int register_android_server_am_OomConnection(JNIEnv* env) { + sOomKillRecordInfo.clazz = FindClassOrDie(env, "android/os/OomKillRecord"); + sOomKillRecordInfo.clazz = MakeGlobalRefOrDie(env, sOomKillRecordInfo.clazz); + + sOomKillRecordInfo.ctor = + GetMethodIDOrDie(env, sOomKillRecordInfo.clazz, "<init>", "(JIILjava/lang/String;S)V"); + + return RegisterMethodsOrDie(env, "com/android/server/am/OomConnection", sOomConnectionMethods, + NELEM(sOomConnectionMethods)); +} +} // namespace android
\ No newline at end of file diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index f6f673746609..df4489528be5 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -52,6 +52,7 @@ int register_android_server_Watchdog(JNIEnv* env); int register_android_server_HardwarePropertiesManagerService(JNIEnv* env); int register_android_server_SyntheticPasswordManager(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); +int register_android_server_am_OomConnection(JNIEnv* env); int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env); @@ -112,6 +113,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_storage_AppFuse(env); register_android_server_SyntheticPasswordManager(env); register_android_hardware_display_DisplayViewport(env); + register_android_server_am_OomConnection(env); register_android_server_am_CachedAppOptimizer(env); register_android_server_am_LowMemDetector(env); register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env); diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp index 74fdabf95016..68e2c9a57c24 100644 --- a/services/core/jni/tvinput/JTvInputHal.cpp +++ b/services/core/jni/tvinput/JTvInputHal.cpp @@ -168,6 +168,24 @@ const std::vector<AidlTvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId return list; } +static const std::map<std::pair<AidlAudioDeviceType, std::string>, audio_devices_t> + aidlToNativeAudioType = { + {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_ANALOG}, + AUDIO_DEVICE_IN_LINE}, + {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_HDMI}, + AUDIO_DEVICE_IN_HDMI}, + {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_HDMI_ARC}, + AUDIO_DEVICE_IN_HDMI_ARC}, + {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_HDMI_EARC}, + AUDIO_DEVICE_IN_HDMI_EARC}, + {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_IP_V4}, + AUDIO_DEVICE_IN_IP}, + {{AidlAudioDeviceType::IN_DEVICE, AidlAudioDeviceDescription::CONNECTION_SPDIF}, + AUDIO_DEVICE_IN_SPDIF}, + {{AidlAudioDeviceType::IN_LOOPBACK, ""}, AUDIO_DEVICE_IN_LOOPBACK}, + {{AidlAudioDeviceType::IN_TV_TUNER, ""}, AUDIO_DEVICE_IN_TV_TUNER}, +}; + void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfoWrapper& info) { { Mutex::Autolock autoLock(&mLock); @@ -187,9 +205,15 @@ void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfoWrapper& info) { if (info.isHidl) { hidlSetUpAudioInfo(env, builder, info); } else { - AidlAudioDeviceType audioType = info.aidlAudioDevice.type.type; - env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType, audioType); - if (audioType != AidlAudioDeviceType::NONE) { + auto it = aidlToNativeAudioType.find({info.aidlAudioDevice.type.type, + info.aidlAudioDevice.type.connection}); + audio_devices_t nativeAudioType = AUDIO_DEVICE_NONE; + if (it != aidlToNativeAudioType.end()) { + nativeAudioType = it->second; + } + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType, + nativeAudioType); + if (info.aidlAudioDevice.type.type != AidlAudioDeviceType::NONE) { std::stringstream ss; switch (info.aidlAudioDevice.address.getTag()) { case AidlAudioDeviceAddress::id: diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h index e8b1c9931372..b7b4b1621512 100644 --- a/services/core/jni/tvinput/JTvInputHal.h +++ b/services/core/jni/tvinput/JTvInputHal.h @@ -54,6 +54,7 @@ using ::aidl::android::hardware::tv::input::TvMessageEventType; using AidlAudioDevice = ::aidl::android::media::audio::common::AudioDevice; using AidlAudioDeviceAddress = ::aidl::android::media::audio::common::AudioDeviceAddress; +using AidlAudioDeviceDescription = ::aidl::android::media::audio::common::AudioDeviceDescription; using AidlAudioDeviceType = ::aidl::android::media::audio::common::AudioDeviceType; using AidlITvInput = ::aidl::android::hardware::tv::input::ITvInput; using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle; diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index d0a9c458a20f..debd891400b6 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -46,6 +46,8 @@ <xs:annotation name="nonnull"/> <xs:annotation name="final"/> </xs:element> + <xs:element type="powerThrottlingConfig" name="powerThrottlingConfig" minOccurs="0" + maxOccurs="1"/> <xs:element type="luxThrottling" name="luxThrottling" minOccurs="0" maxOccurs="1"/> <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" @@ -350,6 +352,43 @@ </xs:sequence> </xs:complexType> + <xs:complexType name="powerThrottlingMap"> + <xs:sequence> + <xs:element name="powerThrottlingPoint" type="powerThrottlingPoint" maxOccurs="unbounded" minOccurs="1"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + <xs:attribute name="id" type="xs:string"/> + </xs:complexType> + + <xs:complexType name="powerThrottlingPoint"> + <xs:sequence> + <xs:element type="thermalStatus" name="thermalStatus"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="nonNegativeDecimal" name="powerQuotaMilliWatts"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="powerThrottlingConfig"> + <xs:element type="nonNegativeDecimal" name="brightnessLowestCapAllowed"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="pollingWindowMillis" type="xs:nonNegativeInteger"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="powerThrottlingMap" name="powerThrottlingMap" maxOccurs="unbounded"> + <xs:annotation name="final"/> + </xs:element> + </xs:complexType> + <xs:complexType name="nitsMap"> <xs:sequence> <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2"> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 949b1f2cb74b..2d27f0c79660 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -106,6 +106,7 @@ package com.android.server.display.config { method public final com.android.server.display.config.SensorDetails getLightSensor(); method public com.android.server.display.config.LuxThrottling getLuxThrottling(); method @Nullable public final String getName(); + method public com.android.server.display.config.PowerThrottlingConfig getPowerThrottlingConfig(); method public final com.android.server.display.config.SensorDetails getProxSensor(); method public com.android.server.display.config.DisplayQuirks getQuirks(); method public com.android.server.display.config.RefreshRateConfigs getRefreshRate(); @@ -138,6 +139,7 @@ package com.android.server.display.config { method public final void setLightSensor(com.android.server.display.config.SensorDetails); method public void setLuxThrottling(com.android.server.display.config.LuxThrottling); method public final void setName(@Nullable String); + method public void setPowerThrottlingConfig(com.android.server.display.config.PowerThrottlingConfig); method public final void setProxSensor(com.android.server.display.config.SensorDetails); method public void setQuirks(com.android.server.display.config.DisplayQuirks); method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs); @@ -246,6 +248,30 @@ package com.android.server.display.config { method public final void setValue(@NonNull java.math.BigDecimal); } + public class PowerThrottlingConfig { + ctor public PowerThrottlingConfig(); + method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed(); + method @NonNull public final java.math.BigInteger getPollingWindowMillis(); + method public final java.util.List<com.android.server.display.config.PowerThrottlingMap> getPowerThrottlingMap(); + method public final void setBrightnessLowestCapAllowed(@NonNull java.math.BigDecimal); + method public final void setPollingWindowMillis(@NonNull java.math.BigInteger); + } + + public class PowerThrottlingMap { + ctor public PowerThrottlingMap(); + method public String getId(); + method @NonNull public final java.util.List<com.android.server.display.config.PowerThrottlingPoint> getPowerThrottlingPoint(); + method public void setId(String); + } + + public class PowerThrottlingPoint { + ctor public PowerThrottlingPoint(); + method @NonNull public final java.math.BigDecimal getPowerQuotaMilliWatts(); + method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatus(); + method public final void setPowerQuotaMilliWatts(@NonNull java.math.BigDecimal); + method public final void setThermalStatus(@NonNull com.android.server.display.config.ThermalStatus); + } + public enum PredefinedBrightnessLimitNames { method public String getRawName(); enum_constant public static final com.android.server.display.config.PredefinedBrightnessLimitNames _default; diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd index 57b5d00f75a0..4e954653bcbb 100644 --- a/services/core/xsd/display-layout-config/display-layout-config.xsd +++ b/services/core/xsd/display-layout-config/display-layout-config.xsd @@ -52,6 +52,7 @@ <xs:element name="address" type="xs:nonNegativeInteger"/> <xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" /> + <xs:element name="powerThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="refreshRateThermalThrottlingMapId" type="xs:string" minOccurs="0" /> <xs:element name="leadDisplayAddress" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1" /> </xs:sequence> diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt index 2d4f7a428db1..195cae5aee14 100644 --- a/services/core/xsd/display-layout-config/schema/current.txt +++ b/services/core/xsd/display-layout-config/schema/current.txt @@ -8,6 +8,7 @@ package com.android.server.display.config.layout { method public String getDisplayGroup(); method public java.math.BigInteger getLeadDisplayAddress(); method public String getPosition(); + method public String getPowerThrottlingMapId(); method public String getRefreshRateThermalThrottlingMapId(); method public String getRefreshRateZoneId(); method public boolean isDefaultDisplay(); @@ -19,6 +20,7 @@ package com.android.server.display.config.layout { method public void setEnabled(boolean); method public void setLeadDisplayAddress(java.math.BigInteger); method public void setPosition(String); + method public void setPowerThrottlingMapId(String); method public void setRefreshRateThermalThrottlingMapId(String); method public void setRefreshRateZoneId(String); } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index a4adf5866f3d..08df65169224 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -65,6 +65,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.PackageMonitor; import com.android.server.credentials.metrics.ApiName; import com.android.server.credentials.metrics.ApiStatus; import com.android.server.infra.AbstractMasterSystemService; @@ -89,7 +90,7 @@ import java.util.stream.Collectors; */ public final class CredentialManagerService extends AbstractMasterSystemService< - CredentialManagerService, CredentialManagerServiceImpl> { + CredentialManagerService, CredentialManagerServiceImpl> { private static final String TAG = "CredManSysService"; private static final String PERMISSION_DENIED_ERROR = "permission_denied"; @@ -110,8 +111,7 @@ public final class CredentialManagerService /** Cache of all ongoing request sessions per user id. */ @GuardedBy("mLock") - private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions = - new SparseArray<>(); + private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions = new SparseArray<>(); private final SessionManager mSessionManager = new SessionManager(); @@ -123,6 +123,8 @@ public final class CredentialManagerService null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER); mContext = context; + + mPackageMonitor.register(context, context.getMainLooper(), false); } @NonNull @@ -139,8 +141,7 @@ public final class CredentialManagerService serviceInfos.forEach( info -> { services.add( - new CredentialManagerServiceImpl(this, mLock, resolvedUserId, - info)); + new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info)); }); return services; } @@ -216,8 +217,8 @@ public final class CredentialManagerService for (CredentialManagerServiceImpl serviceToBeRemoved : servicesToBeRemoved) { removeServiceFromCache(serviceToBeRemoved, userId); removeServiceFromSystemServicesCache(serviceToBeRemoved, userId); - removeServiceFromMultiModeSettings(serviceToBeRemoved.getComponentName() - .flattenToString(), userId); + removeServiceFromMultiModeSettings( + serviceToBeRemoved.getComponentName().flattenToString(), userId); CredentialDescriptionRegistry.forUser(userId) .evictProviderWithPackageName(serviceToBeRemoved.getServicePackageName()); } @@ -286,13 +287,20 @@ public final class CredentialManagerService } private static Set<ComponentName> getPrimaryProvidersForUserId(Context context, int userId) { - final int resolvedUserId = ActivityManager.handleIncomingUser( - Binder.getCallingPid(), Binder.getCallingUid(), - userId, false, false, - "getPrimaryProvidersForUserId", null); - SecureSettingsServiceNameResolver resolver = new SecureSettingsServiceNameResolver( - context, Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, - /* isMultipleMode= */ true); + final int resolvedUserId = + ActivityManager.handleIncomingUser( + Binder.getCallingPid(), + Binder.getCallingUid(), + userId, + false, + false, + "getPrimaryProvidersForUserId", + null); + SecureSettingsServiceNameResolver resolver = + new SecureSettingsServiceNameResolver( + context, + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, + /* isMultipleMode= */ true); String[] serviceNames = resolver.readServiceNameList(resolvedUserId); if (serviceNames == null) { return new HashSet<ComponentName>(); @@ -329,7 +337,8 @@ public final class CredentialManagerService final long origId = Binder.clearCallingIdentity(); try { return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, + DeviceConfig.NAMESPACE_CREDENTIAL, + DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, false); } finally { Binder.restoreCallingIdentity(origId); @@ -345,13 +354,14 @@ public final class CredentialManagerService List<ProviderSession> providerSessions = new ArrayList<>(); for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result : activeCredentialContainers) { - ProviderSession providerSession = ProviderRegistryGetSession.createNewSession( - mContext, - UserHandle.getCallingUserId(), - session, - session.mClientAppInfo, - result.second.mPackageName, - result.first); + ProviderSession providerSession = + ProviderRegistryGetSession.createNewSession( + mContext, + UserHandle.getCallingUserId(), + session, + session.mClientAppInfo, + result.second.mPackageName, + result.first); providerSessions.add(providerSession); session.addProviderSession(providerSession.getComponentName(), providerSession); } @@ -367,23 +377,23 @@ public final class CredentialManagerService List<ProviderSession> providerSessions = new ArrayList<>(); for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result : activeCredentialContainers) { - ProviderSession providerSession = ProviderRegistryGetSession.createNewSession( - mContext, - UserHandle.getCallingUserId(), - session, - session.mClientAppInfo, - result.second.mPackageName, - result.first); + ProviderSession providerSession = + ProviderRegistryGetSession.createNewSession( + mContext, + UserHandle.getCallingUserId(), + session, + session.mClientAppInfo, + result.second.mPackageName, + result.first); providerSessions.add(providerSession); session.addProviderSession(providerSession.getComponentName(), providerSession); } return providerSessions; } - @NonNull private Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> - getFilteredResultFromRegistry(List<CredentialOption> options) { + getFilteredResultFromRegistry(List<CredentialOption> options) { // Session for active/provisioned credential descriptions; CredentialDescriptionRegistry registry = CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId()); @@ -393,10 +403,12 @@ public final class CredentialManagerService options.stream() .map( getCredentialOption -> - new HashSet<>(getCredentialOption - .getCredentialRetrievalData() - .getStringArrayList( - CredentialOption.SUPPORTED_ELEMENT_KEYS))) + new HashSet<>( + getCredentialOption + .getCredentialRetrievalData() + .getStringArrayList( + CredentialOption + .SUPPORTED_ELEMENT_KEYS))) .collect(Collectors.toSet()); // All requested credential descriptions based on the given request. @@ -408,12 +420,14 @@ public final class CredentialManagerService for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) { for (CredentialOption credentialOption : options) { - Set<String> requestedElementKeys = new HashSet<>( - credentialOption - .getCredentialRetrievalData() - .getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS)); - if (CredentialDescriptionRegistry.checkForMatch(filterResult.mElementKeys, - requestedElementKeys)) { + Set<String> requestedElementKeys = + new HashSet<>( + credentialOption + .getCredentialRetrievalData() + .getStringArrayList( + CredentialOption.SUPPORTED_ELEMENT_KEYS)); + if (CredentialDescriptionRegistry.checkForMatch( + filterResult.mElementKeys, requestedElementKeys)) { result.add(new Pair<>(credentialOption, filterResult)); } } @@ -449,9 +463,7 @@ public final class CredentialManagerService } private CallingAppInfo constructCallingAppInfo( - String realPackageName, - int userId, - @Nullable String origin) { + String realPackageName, int userId, @Nullable String origin) { final PackageInfo packageInfo; CallingAppInfo callingAppInfo; try { @@ -477,8 +489,7 @@ public final class CredentialManagerService GetCredentialRequest request, IGetCandidateCredentialsCallback callback, final String callingPackage) { - Slog.i(TAG, "starting getCandidateCredentials with callingPackage: " - + callingPackage); + Slog.i(TAG, "starting getCandidateCredentials with callingPackage: " + callingPackage); ICancellationSignal cancelTransport = CancellationSignal.createTransport(); final int userId = UserHandle.getCallingUserId(); @@ -496,8 +507,7 @@ public final class CredentialManagerService request, constructCallingAppInfo(callingPackage, userId, request.getOrigin()), getEnabledProvidersForUser(userId), - CancellationSignal.fromTransport(cancelTransport) - ); + CancellationSignal.fromTransport(cancelTransport)); addSessionLocked(userId, session); List<ProviderSession> providerSessions = @@ -531,8 +541,7 @@ public final class CredentialManagerService IGetCredentialCallback callback, final String callingPackage) { final long timestampBegan = System.nanoTime(); - Slog.i(TAG, "starting executeGetCredential with callingPackage: " - + callingPackage); + Slog.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage); ICancellationSignal cancelTransport = CancellationSignal.createTransport(); final int userId = UserHandle.getCallingUserId(); @@ -557,8 +566,7 @@ public final class CredentialManagerService timestampBegan); addSessionLocked(userId, session); - List<ProviderSession> providerSessions = - prepareProviderSessions(request, session); + List<ProviderSession> providerSessions = prepareProviderSessions(request, session); if (providerSessions.isEmpty()) { try { @@ -617,15 +625,17 @@ public final class CredentialManagerService if (providerSessions.isEmpty()) { try { prepareGetCredentialCallback.onResponse( - new PrepareGetCredentialResponseInternal(PermissionUtils.hasPermission( - mContext, - callingPackage, - Manifest.permission - .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS), - /*credentialResultTypes=*/null, - /*hasAuthenticationResults=*/false, - /*hasRemoteResults=*/false, - /*pendingIntent=*/null)); + new PrepareGetCredentialResponseInternal( + PermissionUtils.hasPermission( + mContext, + callingPackage, + Manifest.permission + .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS + ), + /* credentialResultTypes= */ null, + /* hasAuthenticationResults= */ false, + /* hasRemoteResults= */ false, + /* pendingIntent= */ null)); } catch (RemoteException e) { Slog.e( TAG, @@ -641,27 +651,32 @@ public final class CredentialManagerService } private List<ProviderSession> prepareProviderSessions( - GetCredentialRequest request, - GetRequestSession session) { + GetCredentialRequest request, GetRequestSession session) { List<ProviderSession> providerSessions; if (isCredentialDescriptionApiEnabled()) { List<CredentialOption> optionsThatRequireActiveCredentials = request.getCredentialOptions().stream() - .filter(credentialOption -> credentialOption - .getCredentialRetrievalData() - .getStringArrayList( - CredentialOption - .SUPPORTED_ELEMENT_KEYS) != null) + .filter( + credentialOption -> + credentialOption + .getCredentialRetrievalData() + .getStringArrayList( + CredentialOption + .SUPPORTED_ELEMENT_KEYS) + != null) .toList(); List<CredentialOption> optionsThatDoNotRequireActiveCredentials = request.getCredentialOptions().stream() - .filter(credentialOption -> credentialOption - .getCredentialRetrievalData() - .getStringArrayList( - CredentialOption - .SUPPORTED_ELEMENT_KEYS) == null) + .filter( + credentialOption -> + credentialOption + .getCredentialRetrievalData() + .getStringArrayList( + CredentialOption + .SUPPORTED_ELEMENT_KEYS) + == null) .toList(); List<ProviderSession> sessionsWithoutRemoteService = @@ -706,8 +721,7 @@ public final class CredentialManagerService ICreateCredentialCallback callback, String callingPackage) { final long timestampBegan = System.nanoTime(); - Slog.i(TAG, "starting executeCreateCredential with callingPackage: " - + callingPackage); + Slog.i(TAG, "starting executeCreateCredential with callingPackage: " + callingPackage); ICancellationSignal cancelTransport = CancellationSignal.createTransport(); if (request.getOrigin() != null) { @@ -756,8 +770,8 @@ public final class CredentialManagerService } catch (RemoteException e) { Slog.e( TAG, - "Issue invoking onError on ICreateCredentialCallback " - + "callback: ", e); + "Issue invoking onError on ICreateCredentialCallback " + "callback: ", + e); } } @@ -770,8 +784,8 @@ public final class CredentialManagerService try { var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric(); initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime()); - MetricUtilities.logApiCalledInitialPhase(initMetric, - session.mRequestSessionMetric.returnIncrementSequence()); + MetricUtilities.logApiCalledInitialPhase( + initMetric, session.mRequestSessionMetric.returnIncrementSequence()); } catch (Exception e) { Slog.i(TAG, "Unexpected error during metric logging: ", e); } @@ -779,25 +793,32 @@ public final class CredentialManagerService @Override public void setEnabledProviders( - List<String> primaryProviders, List<String> providers, int userId, + List<String> primaryProviders, + List<String> providers, + int userId, ISetEnabledProvidersCallback callback) { final int callingUid = Binder.getCallingUid(); if (!hasWriteSecureSettingsPermission()) { try { MetricUtilities.logApiCalledSimpleV2( - ApiName.SET_ENABLED_PROVIDERS, - ApiStatus.FAILURE, callingUid); + ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid); callback.onError( PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR); } catch (RemoteException e) { MetricUtilities.logApiCalledSimpleV2( - ApiName.SET_ENABLED_PROVIDERS, - ApiStatus.FAILURE, callingUid); + ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid); Slog.e(TAG, "Issue with invoking response: ", e); } return; } + // If we don't have any primary providers enabled anymore then + // we should erase all the providers since the feature is + // now disabled. + if (primaryProviders.isEmpty()) { + providers.clear(); + } + userId = ActivityManager.handleIncomingUser( Binder.getCallingPid(), @@ -808,17 +829,19 @@ public final class CredentialManagerService "setEnabledProviders", null); - Set<String> enableProvider = new HashSet<>(providers); - enableProvider.addAll(primaryProviders); + Set<String> enabledProviders = new HashSet<>(providers); + enabledProviders.addAll(primaryProviders); boolean writeEnabledStatus = - Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.putStringForUser( + getContext().getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE, - String.join(":", enableProvider), + String.join(":", enabledProviders), userId); boolean writePrimaryStatus = - Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.putStringForUser( + getContext().getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, String.join(":", primaryProviders), userId); @@ -827,15 +850,13 @@ public final class CredentialManagerService Slog.e(TAG, "Failed to store setting containing enabled or primary providers"); try { MetricUtilities.logApiCalledSimpleV2( - ApiName.SET_ENABLED_PROVIDERS, - ApiStatus.FAILURE, callingUid); + ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid); callback.onError( "failed_setting_store", "Failed to store setting containing enabled or primary providers"); } catch (RemoteException e) { MetricUtilities.logApiCalledSimpleV2( - ApiName.SET_ENABLED_PROVIDERS, - ApiStatus.FAILURE, callingUid); + ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid); Slog.e(TAG, "Issue with invoking error response: ", e); return; } @@ -844,13 +865,11 @@ public final class CredentialManagerService // Call the callback. try { MetricUtilities.logApiCalledSimpleV2( - ApiName.SET_ENABLED_PROVIDERS, - ApiStatus.SUCCESS, callingUid); + ApiName.SET_ENABLED_PROVIDERS, ApiStatus.SUCCESS, callingUid); callback.onResponse(); } catch (RemoteException e) { MetricUtilities.logApiCalledSimpleV2( - ApiName.SET_ENABLED_PROVIDERS, - ApiStatus.FAILURE, callingUid); + ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid); Slog.e(TAG, "Issue with invoking response: ", e); // TODO: Propagate failure } @@ -859,8 +878,10 @@ public final class CredentialManagerService @Override public boolean isEnabledCredentialProviderService( ComponentName componentName, String callingPackage) { - Slog.i(TAG, "isEnabledCredentialProviderService with componentName: " - + componentName.flattenToString()); + Slog.i( + TAG, + "isEnabledCredentialProviderService with componentName: " + + componentName.flattenToString()); // TODO(253157366): Check additional set of services. final int userId = UserHandle.getCallingUserId(); @@ -877,7 +898,8 @@ public final class CredentialManagerService // The component name and the package name do not match. MetricUtilities.logApiCalledSimpleV2( ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, - ApiStatus.FAILURE, callingUid); + ApiStatus.FAILURE, + callingUid); Slog.w( TAG, "isEnabledCredentialProviderService: Component name does " @@ -886,7 +908,8 @@ public final class CredentialManagerService } MetricUtilities.logApiCalledSimpleV2( ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, - ApiStatus.SUCCESS, callingUid); + ApiStatus.SUCCESS, + callingUid); return true; } } @@ -901,13 +924,14 @@ public final class CredentialManagerService verifyGetProvidersPermission(); final int callingUid = Binder.getCallingUid(); MetricUtilities.logApiCalledSimpleV2( - ApiName.GET_CREDENTIAL_PROVIDER_SERVICES, - ApiStatus.SUCCESS, callingUid); - return CredentialProviderInfoFactory - .getCredentialProviderServices( - mContext, userId, providerFilter, getEnabledProvidersForUser(userId), - getPrimaryProvidersForUserId(mContext, userId)); + ApiName.GET_CREDENTIAL_PROVIDER_SERVICES, ApiStatus.SUCCESS, callingUid); + return CredentialProviderInfoFactory.getCredentialProviderServices( + mContext, + userId, + providerFilter, + getEnabledProvidersForUser(userId), + getPrimaryProvidersForUserId(mContext, userId)); } @Override @@ -917,7 +941,10 @@ public final class CredentialManagerService final int userId = UserHandle.getCallingUserId(); return CredentialProviderInfoFactory.getCredentialProviderServicesForTesting( - mContext, userId, providerFilter, getEnabledProvidersForUser(userId), + mContext, + userId, + providerFilter, + getEnabledProvidersForUser(userId), getPrimaryProvidersForUserId(mContext, userId)); } @@ -935,15 +962,22 @@ public final class CredentialManagerService } private Set<ComponentName> getEnabledProvidersForUser(int userId) { - final int resolvedUserId = ActivityManager.handleIncomingUser( - Binder.getCallingPid(), Binder.getCallingUid(), - userId, false, false, - "getEnabledProvidersForUser", null); + final int resolvedUserId = + ActivityManager.handleIncomingUser( + Binder.getCallingPid(), + Binder.getCallingUid(), + userId, + false, + false, + "getEnabledProvidersForUser", + null); Set<ComponentName> enabledProviders = new HashSet<>(); - String directValue = Settings.Secure.getStringForUser( - mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE, - resolvedUserId); + String directValue = + Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.CREDENTIAL_SERVICE, + resolvedUserId); if (!TextUtils.isEmpty(directValue)) { String[] components = directValue.split(":"); @@ -964,8 +998,7 @@ public final class CredentialManagerService IClearCredentialStateCallback callback, String callingPackage) { final long timestampBegan = System.nanoTime(); - Slog.i(TAG, "starting clearCredentialState with callingPackage: " - + callingPackage); + Slog.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage); final int userId = UserHandle.getCallingUserId(); int callingUid = Binder.getCallingUid(); enforceCallingPackage(callingPackage, callingUid); @@ -996,13 +1029,13 @@ public final class CredentialManagerService if (providerSessions.isEmpty()) { try { // TODO("Replace with properly defined error type") - callback.onError("UNKNOWN", "No credentials available on " - + "this device"); + callback.onError("UNKNOWN", "No credentials available on " + "this device"); } catch (RemoteException e) { Slog.e( TAG, "Issue invoking onError on IClearCredentialStateCallback " - + "callback: ", e); + + "callback: ", + e); } } @@ -1035,9 +1068,7 @@ public final class CredentialManagerService public void unregisterCredentialDescription( UnregisterCredentialDescriptionRequest request, String callingPackage) throws IllegalArgumentException { - Slog.i(TAG, "unregisterCredentialDescription with callingPackage: " - + callingPackage); - + Slog.i(TAG, "unregisterCredentialDescription with callingPackage: " + callingPackage); if (!isCredentialDescriptionApiEnabled()) { throw new UnsupportedOperationException("Feature not supported"); @@ -1061,18 +1092,18 @@ public final class CredentialManagerService } private void enforcePermissionForAllowedProviders(GetCredentialRequest request) { - boolean containsAllowedProviders = request.getCredentialOptions() - .stream() - .anyMatch(option -> option.getAllowedProviders() != null - && !option.getAllowedProviders().isEmpty()); + boolean containsAllowedProviders = + request.getCredentialOptions().stream() + .anyMatch( + option -> + option.getAllowedProviders() != null + && !option.getAllowedProviders().isEmpty()); if (containsAllowedProviders) { - mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS, - null); + mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS, null); } } - private void addSessionLocked(@UserIdInt int userId, - RequestSession requestSession) { + private void addSessionLocked(@UserIdInt int userId, RequestSession requestSession) { synchronized (mLock) { mSessionManager.addSession(userId, requestSession.mRequestId, requestSession); } @@ -1080,11 +1111,11 @@ public final class CredentialManagerService private void enforceCallingPackage(String callingPackage, int callingUid) { int packageUid; - PackageManager pm = mContext.createContextAsUser( - UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager(); + PackageManager pm = + mContext.createContextAsUser(UserHandle.getUserHandleForUid(callingUid), 0) + .getPackageManager(); try { - packageUid = pm.getPackageUid(callingPackage, - PackageManager.PackageInfoFlags.of(0)); + packageUid = pm.getPackageUid(callingPackage, PackageManager.PackageInfoFlags.of(0)); } catch (PackageManager.NameNotFoundException e) { throw new SecurityException(callingPackage + " not found"); } @@ -1110,4 +1141,72 @@ public final class CredentialManagerService mRequestSessions.get(userId).put(token, requestSession); } } + + /** Updates settings when packages are removed. */ + private final PackageMonitor mPackageMonitor = + new PackageMonitor() { + + @Override + public void onPackageRemoved(String packageName, int uid) { + Slog.d(TAG, "onPackageRemoved: " + packageName); + + // Remove any providers from the primary setting that contain the package name + // being removed. + Set<String> primaryProviders = + getStoredProviders( + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, packageName); + if (!Settings.Secure.putString( + getContext().getContentResolver(), + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, + String.join(":", primaryProviders))) { + Slog.w(TAG, "Failed to remove primary package: " + packageName); + return; + } + + // Get the secondary providers and if there are no primary providers then + // we should erase all the providers from the secondary list because the + // feature is now disabled. + if (!primaryProviders.isEmpty()) { + return; + } + + if (!Settings.Secure.putString( + getContext().getContentResolver(), + Settings.Secure.CREDENTIAL_SERVICE, + "")) { + Slog.w(TAG, "Failed to remove secondary package: " + packageName); + return; + } + } + + private Set<String> getStoredProviders(String key, String packageName) { + // Get the current providers. + String rawProviders = + Settings.Secure.getStringForUser( + getContext().getContentResolver(), key, + UserHandle.myUserId()); + if (rawProviders == null) { + Slog.w(TAG, "settings key is null: " + key); + return new HashSet<>(); + } + + // If the app being removed matches any of the package names from + // this list then don't add it in the output. + Set<String> providers = new HashSet<>(); + for (String rawComponentName : rawProviders.split(":")) { + if (TextUtils.isEmpty(rawComponentName) + || rawComponentName.equals("null")) { + Slog.d(TAG, "provider component name is empty or null"); + continue; + } + + ComponentName cn = ComponentName.unflattenFromString(rawComponentName); + if (cn != null && !cn.getPackageName().equals(packageName)) { + providers.add(cn.flattenToString()); + } + } + + return providers; + } + }; } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java index b90f08e420e7..3c190bf7ad11 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java @@ -32,6 +32,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ResultReceiver; +import android.os.UserHandle; import android.service.credentials.CredentialProviderInfoFactory; import android.util.Slog; @@ -171,7 +172,9 @@ public class CredentialManagerUi { .setAction(UUID.randomUUID().toString()); //TODO: Create unique pending intent using request code and cancel any pre-existing pending // intents - return PendingIntent.getActivity( - mContext, /*requestCode=*/0, intent, PendingIntent.FLAG_IMMUTABLE); + return PendingIntent.getActivityAsUser( + mContext, /*requestCode=*/0, intent, + PendingIntent.FLAG_IMMUTABLE, /*options=*/null, + UserHandle.of(mUserId)); } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 3eb6718c0a95..7bd1cc4ca6c8 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -31,6 +31,7 @@ import android.credentials.ui.Entry; import android.credentials.ui.GetCredentialProviderData; import android.credentials.ui.ProviderPendingIntentResponse; import android.os.ICancellationSignal; +import android.service.autofill.Flags; import android.service.credentials.Action; import android.service.credentials.BeginGetCredentialOption; import android.service.credentials.BeginGetCredentialRequest; @@ -42,6 +43,7 @@ import android.service.credentials.GetCredentialRequest; import android.service.credentials.RemoteEntry; import android.util.Pair; import android.util.Slog; +import android.view.autofill.AutofillId; import java.util.ArrayList; import java.util.HashMap; @@ -379,13 +381,23 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential // but does not resolve to a valid option. For now, not skipping it because // it may be possible that the provider adds their own extras and expects to receive // those and complete the flow. - if (mBeginGetOptionToCredentialOptionMap.get(id) == null) { + Intent intent = new Intent(); + CredentialOption credentialOption = mBeginGetOptionToCredentialOptionMap.get(id); + if (credentialOption == null) { Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option"); - return new Intent(); + return intent; } - return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST, + AutofillId autofillId = credentialOption + .getCandidateQueryData() + .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class); + if (autofillId != null && Flags.autofillCredmanIntegration()) { + intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId); + } + return intent.putExtra( + CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST, new GetCredentialRequest( - mCallingAppInfo, List.of(mBeginGetOptionToCredentialOptionMap.get(id)))); + mCallingAppInfo, + List.of(credentialOption))); } private Intent setUpFillInIntentWithQueryRequest() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6aa135a3eaa9..43e47d7a45fc 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; @@ -15781,57 +15782,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else { long ident = mInjector.binderClearCallingIdentity(); try { - // TODO(b/277908283): check in the policy engine instead of calling user manager. - List<UserManager.EnforcingUser> sources = mUserManager - .getUserRestrictionSources(restriction, UserHandle.of(userId)); - if (sources == null) { - // The restriction is not enforced. - return null; - } - int sizeBefore = sources.size(); - if (sizeBefore > 1) { - Slogf.d(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): " - + "%d sources found, excluding those set by UserManager", - userId, restriction, sizeBefore); - sources = getDevicePolicySources(sources); - } - if (sources.isEmpty()) { - // The restriction is not enforced (or is just enforced by the system) + if (getEnforcingAdminsForRestrictionInternal(userId, restriction).size() == 0) { return null; } - if (sources.size() > 1) { - // In this case, we'll show an admin support dialog that does not - // specify the admin. - // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return - // the admin for the calling user. - Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple " - + "sources for restriction %s on user %d", - userId, restriction, restriction, userId); + ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId); + if (admin != null) { result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, userId); + result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier()); + result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, + admin.info.getComponent()); return result; } - final UserManager.EnforcingUser enforcingUser = sources.get(0); - final int sourceType = enforcingUser.getUserRestrictionSource(); - if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER - || sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) { - ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId); - if (admin != null) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier()); - result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - admin.info.getComponent()); - return result; - } - } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) { - /* - * In this case, the user restriction is enforced by the system. - * So we won't show an admin support intent, even if it is also - * enforced by a profile/device owner. - */ - return null; - } + return null; } finally { mInjector.binderRestoreCallingIdentity(ident); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index 16876ac64e71..eb893fcfee1f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -251,7 +251,14 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private int runSetDeviceOwner(PrintWriter pw) { parseArgs(); - mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId); + boolean isAdminAdded = false; + try { + mService.setActiveAdmin(mComponent, /* refreshing= */ false, mUserId); + isAdminAdded = true; + } catch (IllegalArgumentException e) { + pw.printf("%s was already an admin for user %d. No need to set it again.\n", + mComponent.flattenToShortString(), mUserId); + } try { if (!mService.setDeviceOwner(mComponent, mUserId, @@ -260,8 +267,10 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { "Can't set package " + mComponent + " as device owner."); } } catch (Exception e) { - // Need to remove the admin that we just added. - mService.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM); + if (isAdminAdded) { + // Need to remove the admin that we just added. + mService.removeActiveAdmin(mComponent, mUserId); + } throw e; } diff --git a/services/foldables/devicestateprovider/tests/Android.bp b/services/foldables/devicestateprovider/tests/Android.bp index a8db05e99179..84a6df38e0a0 100644 --- a/services/foldables/devicestateprovider/tests/Android.bp +++ b/services/foldables/devicestateprovider/tests/Android.bp @@ -20,11 +20,11 @@ android_test { "foldable-device-state-provider", "androidx.test.rules", "junit", - "truth-prebuilt", + "truth", "mockito-target-extended-minus-junit4", "androidx.test.uiautomator_uiautomator", "androidx.test.ext.junit", "testables", ], - test_suites: ["device-tests"] + test_suites: ["device-tests"], } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 57fa12d20de5..49ad84a8e6d9 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -264,6 +264,8 @@ public final class SystemServer implements Dumpable { "com.android.server.backup.BackupManagerService$Lifecycle"; private static final String APPWIDGET_SERVICE_CLASS = "com.android.server.appwidget.AppWidgetService"; + private static final String ARC_SYSTEM_HEALTH_SERVICE = + "com.android.server.arc.health.ArcSystemHealthService"; private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS = "com.android.server.voiceinteraction.VoiceInteractionManagerService"; private static final String APP_HIBERNATION_SERVICE_CLASS = @@ -1287,6 +1289,12 @@ public final class SystemServer implements Dumpable { } } + if (Build.IS_ARC) { + t.traceBegin("StartArcSystemHealthService"); + mSystemServiceManager.startService(ARC_SYSTEM_HEALTH_SERVICE); + t.traceEnd(); + } + t.traceBegin("StartUserManagerService"); mSystemServiceManager.startService(UserManagerService.LifeCycle.class); t.traceEnd(); diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt index 240585c000fd..4addab3cd424 100644 --- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt @@ -85,10 +85,10 @@ class DevicePermissionPolicy : SchemePolicy() { appId: Int, userId: Int ) { - resetPermissionStates(packageName, userId) + resetRuntimePermissions(packageName, userId) } - private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) { + fun MutateStateScope.resetRuntimePermissions(packageName: String, userId: Int) { // It's okay to skip resetting permissions for packages that are removed, // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved() val packageState = newState.externalState.packageStates[packageName] ?: return diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index cee2524657dc..2a292655317e 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -41,6 +41,7 @@ import android.os.RemoteException import android.os.ServiceManager import android.os.UserHandle import android.os.UserManager +import android.permission.flags.Flags import android.permission.IOnPermissionsChangeListener import android.permission.PermissionControllerManager import android.permission.PermissionManager @@ -1409,7 +1410,7 @@ class PermissionService( permissionName: String, deviceId: Int, ): Int { - return if (deviceId == Context.DEVICE_ID_DEFAULT) { + return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) { with(policy) { getPermissionFlags(appId, userId, permissionName) } } else { if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) { @@ -1442,7 +1443,7 @@ class PermissionService( deviceId: Int, flags: Int ): Boolean { - return if (deviceId == Context.DEVICE_ID_DEFAULT) { + return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) { with(policy) { setPermissionFlags(appId, userId, permissionName, flags) } @@ -1737,6 +1738,9 @@ class PermissionService( with(policy) { resetRuntimePermissions(androidPackage.packageName, userId) } + with(devicePolicy) { + resetRuntimePermissions(androidPackage.packageName, userId) + } } } @@ -1747,6 +1751,9 @@ class PermissionService( with(policy) { resetRuntimePermissions(packageState.packageName, userId) } + with(devicePolicy) { + resetRuntimePermissions(packageState.packageName, userId) + } } } } diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp index e04dd688f2a2..8b9efb312efe 100644 --- a/services/robotests/backup/Android.bp +++ b/services/robotests/backup/Android.bp @@ -59,7 +59,7 @@ android_robolectric_test { "mockito-robolectric-prebuilt", "platform-test-annotations", "testng", - "truth-prebuilt", + "truth", ], instrumentation_for: "BackupFrameworksServicesLib", diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp index 36446f64bdce..ffe6dc5d1c63 100644 --- a/services/tests/InputMethodSystemServerTests/Android.bp +++ b/services/tests/InputMethodSystemServerTests/Android.bp @@ -44,7 +44,7 @@ android_test { "service-permission.stubs.system_server", "servicestests-core-utils", "servicestests-utils-mockito-extended", - "truth-prebuilt", + "truth", ], libs: [ @@ -92,7 +92,7 @@ android_test { "service-permission.stubs.system_server", "servicestests-core-utils", "servicestests-utils-mockito-extended", - "truth-prebuilt", + "truth", "SimpleImeTestingLib", "SimpleImeImsLib", ], diff --git a/services/tests/PackageManager/packageinstaller/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp index 35d754b4adc5..e8fce8e72601 100644 --- a/services/tests/PackageManager/packageinstaller/Android.bp +++ b/services/tests/PackageManager/packageinstaller/Android.bp @@ -32,7 +32,7 @@ android_test { "androidx.test.runner", "junit", "kotlin-test", - "truth-prebuilt", + "truth", ], platform_apis: true, test_suites: ["device-tests"], diff --git a/services/tests/PackageManagerComponentOverrideTests/Android.bp b/services/tests/PackageManagerComponentOverrideTests/Android.bp index bc369701b2d4..00850a5e5be0 100644 --- a/services/tests/PackageManagerComponentOverrideTests/Android.bp +++ b/services/tests/PackageManagerComponentOverrideTests/Android.bp @@ -38,7 +38,7 @@ android_test { "service-permission.stubs.system_server", "servicestests-utils-mockito-extended", "testng", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows - "truth-prebuilt", + "truth", ], jni_libs: [ diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp index 9c4e6fd66ceb..ad7af44d4089 100644 --- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp +++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp @@ -29,7 +29,7 @@ android_test { static_libs: [ "compatibility-device-util-axt", "androidx.test.runner", - "truth-prebuilt", + "truth", "Harrier", ], platform_apis: true, diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index ce28682c0a25..6eacef767042 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -30,7 +30,7 @@ java_test_host { libs: [ "tradefed", "junit", - "truth-prebuilt", + "truth", ], static_libs: [ "ApexInstallHelper", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp index 462c5801e952..cea9c599317d 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp @@ -38,6 +38,6 @@ android_test_helper_app { "junit-params", "androidx.test.ext.junit", "androidx.test.rules", - "truth-prebuilt", + "truth", ], } diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp index 57184748d074..ed5f2b51b9ed 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp @@ -28,6 +28,6 @@ android_test_helper_app { "androidx.test.runner", "junit", "kotlin-test", - "truth-prebuilt", + "truth", ], } diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp index a1d846e0f426..3aca1cafbf75 100644 --- a/services/tests/PackageManagerServiceTests/server/Android.bp +++ b/services/tests/PackageManagerServiceTests/server/Android.bp @@ -41,7 +41,7 @@ android_test { "mockito-target-minus-junit4", "platform-test-annotations", "ShortcutManagerTestUtils", - "truth-prebuilt", + "truth", "testables", "platformprotosnano", "framework-protos", @@ -51,7 +51,7 @@ android_test { "servicestests-utils", "service-permission.impl", "testng", - "truth-prebuilt", + "truth", "junit", "junit-params", "platform-compat-test-rules", diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp index 9b3b8c35736c..8505983894a8 100644 --- a/services/tests/PackageManagerServiceTests/unit/Android.bp +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -37,7 +37,7 @@ android_test { "services.core", "servicestests-utils", "servicestests-core-utils", - "truth-prebuilt", + "truth", ], jni_libs: [ "libdexmakerjvmtiagent", diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp index fc2c0857146b..19c913620760 100644 --- a/services/tests/RemoteProvisioningServiceTests/Android.bp +++ b/services/tests/RemoteProvisioningServiceTests/Android.bp @@ -30,8 +30,8 @@ android_test { "mockito-target", "service-rkp.impl", "services.core", - "truth-prebuilt", - "truth-java8-extension-jar", + "truth", + "truth-java8-extension", ], test_suites: [ "device-tests", diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp index e724e804f4e7..9dacfeabf1ef 100644 --- a/services/tests/apexsystemservices/Android.bp +++ b/services/tests/apexsystemservices/Android.bp @@ -34,7 +34,7 @@ java_test_host { "compatibility-host-util", "cts-install-lib-host", "frameworks-base-hostutils", - "truth-prebuilt", + "truth", "modules-utils-build-testing", ], test_suites: [ diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index 6ef150c80037..c37d21ae1cc0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -179,6 +179,89 @@ public final class DisplayDeviceConfigTest { } @Test + public void testPowerThrottlingConfigFromDisplayConfig() throws IOException { + setupDisplayDeviceConfigFromDisplayConfigFile(); + + DisplayDeviceConfig.PowerThrottlingConfigData powerThrottlingConfigData = + mDisplayDeviceConfig.getPowerThrottlingConfigData(); + assertNotNull(powerThrottlingConfigData); + assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA); + assertEquals(10, powerThrottlingConfigData.pollingWindowMillis); + } + + @Test + public void testPowerThrottlingDataFromDisplayConfig() throws IOException { + setupDisplayDeviceConfigFromDisplayConfigFile(); + + List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel> + defaultThrottlingLevels = new ArrayList<>(); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f + )); + + DisplayDeviceConfig.PowerThrottlingData defaultThrottlingData = + new DisplayDeviceConfig.PowerThrottlingData(defaultThrottlingLevels); + + List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel> + concurrentThrottlingLevels = new ArrayList<>(); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f + )); + DisplayDeviceConfig.PowerThrottlingData concurrentThrottlingData = + new DisplayDeviceConfig.PowerThrottlingData(concurrentThrottlingLevels); + + HashMap<String, DisplayDeviceConfig.PowerThrottlingData> throttlingDataMap = + new HashMap<>(2); + throttlingDataMap.put("default", defaultThrottlingData); + throttlingDataMap.put("concurrent", concurrentThrottlingData); + + assertEquals(throttlingDataMap, + mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId()); + } + + @Test public void testConfigValuesFromConfigResource() { setupDisplayDeviceConfigFromConfigResourceFile(); verifyConfigValuesFromConfigResource(); @@ -845,6 +928,64 @@ public final class DisplayDeviceConfigTest { + "</screenBrightnessRampSlowIncreaseIdle>\n"; } + private String getPowerThrottlingConfig() { + return "<powerThrottlingConfig >\n" + + "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n" + + "<pollingWindowMillis>10</pollingWindowMillis>\n" + + "<powerThrottlingMap>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>light</thermalStatus>\n" + + "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>moderate</thermalStatus>\n" + + "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>severe</thermalStatus>\n" + + "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>critical</thermalStatus>\n" + + "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>emergency</thermalStatus>\n" + + "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>shutdown</thermalStatus>\n" + + "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "</powerThrottlingMap>\n" + + "<powerThrottlingMap id=\"concurrent\">\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>light</thermalStatus>\n" + + "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>moderate</thermalStatus>\n" + + "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>severe</thermalStatus>\n" + + "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>critical</thermalStatus>\n" + + "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>emergency</thermalStatus>\n" + + "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>shutdown</thermalStatus>\n" + + "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "</powerThrottlingMap>\n" + + "</powerThrottlingConfig>\n"; + } private String getScreenBrightnessRampCapsIdle() { return "<screenBrightnessRampIncreaseMaxIdleMillis>" + "4000" @@ -915,6 +1056,7 @@ public final class DisplayDeviceConfigTest { + "</displayBrightnessPoint>\n" + "</displayBrightnessMapping>\n" + "</autoBrightness>\n" + + getPowerThrottlingConfig() + "<highBrightnessMode enabled=\"true\">\n" + "<transitionPoint>" + BRIGHTNESS[1] + "</transitionPoint>\n" + "<minimumLux>10000</minimumLux>\n" diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java new file mode 100644 index 000000000000..5c50acb13f30 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java @@ -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.server.display; + +import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import android.hardware.display.DisplayManager; +import android.testing.TestableContext; +import android.view.Display; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.display.RefreshRateSettingsUtils; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RefreshRateSettingsUtilsTest { + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getContext()); + + @Mock + private DisplayManager mDisplayManagerMock; + @Mock + private Display mDisplayMock; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock); + + Display.Mode[] modes = new Display.Mode[]{ + new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, + /* refreshRate= */ 120), + new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, + /* refreshRate= */ 90) + }; + + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); + when(mDisplayMock.getSupportedModes()).thenReturn(modes); + } + + @Test + public void testFindHighestRefreshRateForDefaultDisplay() { + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null); + assertEquals(DEFAULT_REFRESH_RATE, + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), + /* delta= */ 0); + + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); + assertEquals(120, + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), + /* delta= */ 0); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java index c63fac9832e9..ee187baf524e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java @@ -22,6 +22,9 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +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 static org.mockito.Mockito.when; @@ -97,6 +100,37 @@ public class HdrClamperTest { } @Test + public void testRegisterHdrListener() { + verify(mMockHdrInfoListener).register(mMockBinder); + } + + @Test + public void testRegisterOtherHdrListenerWhenCalledWithOtherToken() { + IBinder otherBinder = mock(IBinder.class); + mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, otherBinder); + + verify(mMockHdrInfoListener).unregister(mMockBinder); + verify(mMockHdrInfoListener).register(otherBinder); + } + + @Test + public void testRegisterHdrListenerOnceWhenCalledWithSameToken() { + mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder); + + verify(mMockHdrInfoListener, never()).unregister(mMockBinder); + verify(mMockHdrInfoListener, times(1)).register(mMockBinder); + } + + @Test + public void testRegisterNotCalledIfHbmConfigIsMissing() { + IBinder otherBinder = mock(IBinder.class); + mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, -1, otherBinder); + + verify(mMockHdrInfoListener).unregister(mMockBinder); + verify(mMockHdrInfoListener, never()).register(otherBinder); + } + + @Test public void testClamper_AmbientLuxChangesAboveLimit() { mHdrClamper.onAmbientLuxChange(500); diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java index c280349a0559..79222c063989 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java @@ -23,6 +23,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -37,6 +38,7 @@ import androidx.test.InstrumentationRegistry; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.internal.R; +import com.android.server.display.feature.DisplayManagerFlags; import org.junit.After; import org.junit.Before; @@ -54,6 +56,8 @@ public class DisplayWhiteBalanceTintControllerTest { private Resources mMockedResources; @Mock private DisplayManagerInternal mDisplayManagerInternal; + @Mock + private DisplayManagerFlags mDisplayManagerFlagsMock; private MockitoSession mSession; private Resources mResources; @@ -63,10 +67,6 @@ public class DisplayWhiteBalanceTintControllerTest { @Before public void setUp() { DisplayManagerInternal displayManagerInternal = mock(DisplayManagerInternal.class); - mDisplayWhiteBalanceTintController = - new DisplayWhiteBalanceTintController(displayManagerInternal); - mDisplayWhiteBalanceTintController.setUp(InstrumentationRegistry.getContext(), true); - mDisplayWhiteBalanceTintController.setActivated(true); mSession = ExtendedMockito.mockitoSession() .initMocks(this) @@ -74,6 +74,13 @@ public class DisplayWhiteBalanceTintControllerTest { .strictness(Strictness.LENIENT) .startMocking(); + mDisplayWhiteBalanceTintController = + new DisplayWhiteBalanceTintController(displayManagerInternal, + mDisplayManagerFlagsMock); + mDisplayWhiteBalanceTintController.setUp(InstrumentationRegistry.getContext(), true); + mDisplayWhiteBalanceTintController.setActivated(true); + + mResources = InstrumentationRegistry.getContext().getResources(); // These Resources are common to all tests. doReturn(4000) @@ -360,9 +367,47 @@ public class DisplayWhiteBalanceTintControllerTest { 1e-6f /* tolerance */); } + @Test + public void testDisplayWhiteBalance_TransitionTimes() { + when(mDisplayManagerFlagsMock.isAdaptiveTone2Enabled()).thenReturn(false); + setUpTransitionTimes(); + setUpTintController(); + + assertEquals(30L, + mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(true)); + assertEquals(30L, + mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(false)); + } + + @Test + public void testDisplayWhiteBalance_TransitionTimesDirectional() { + when(mDisplayManagerFlagsMock.isAdaptiveTone2Enabled()).thenReturn(true); + setUpTransitionTimes(); + setUpTintController(); + + assertEquals(400L, + mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(true)); + assertEquals(5000L, + mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(false)); + } + + + private void setUpTransitionTimes() { + doReturn(mResources.getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries)) + .when(mMockedResources) + .getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries); + when(mMockedResources.getInteger( + R.integer.config_displayWhiteBalanceTransitionTime)).thenReturn(30); + when(mMockedResources.getInteger( + R.integer.config_displayWhiteBalanceTransitionTimeIncrease)).thenReturn(400); + when(mMockedResources.getInteger( + R.integer.config_displayWhiteBalanceTransitionTimeDecrease)).thenReturn(5000); + + } + private void setUpTintController() { mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController( - mDisplayManagerInternal); + mDisplayManagerInternal, mDisplayManagerFlagsMock); mDisplayWhiteBalanceTintController.setUp(mMockedContext, true); mDisplayWhiteBalanceTintController.setActivated(true); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index b8c18e070397..c4f72b307eb7 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -27,6 +27,7 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.server.display.mode.Vote.INVALID_SIZE; import static com.google.common.truth.Truth.assertThat; @@ -85,10 +86,12 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.display.BrightnessSynchronizer; +import com.android.internal.display.RefreshRateSettingsUtils; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; +import com.android.modules.utils.testing.ExtendedMockitoRule; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.TestUtils; import com.android.server.display.feature.DisplayManagerFlags; @@ -106,7 +109,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.ArrayList; @@ -252,9 +255,15 @@ public class DisplayModeDirectorTest { @Mock private DisplayManagerFlags mDisplayManagerFlags; + @Rule + public final ExtendedMockitoRule mExtendedMockitoRule = + new ExtendedMockitoRule.Builder(this) + .setStrictness(Strictness.LENIENT) + .spyStatic(RefreshRateSettingsUtils.class) + .build(); + @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext); when(mContext.getContentResolver()).thenReturn(resolver); @@ -1470,6 +1479,94 @@ public class DisplayModeDirectorTest { } @Test + public void testPeakRefreshRate_FlagEnabled() { + when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) + .thenReturn(true); + float highestRefreshRate = 130; + doReturn(highestRefreshRate).when(() -> + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + setPeakRefreshRate(Float.POSITIVE_INFINITY); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + highestRefreshRate); + } + + @Test + public void testPeakRefreshRate_FlagDisabled() { + when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) + .thenReturn(false); + float peakRefreshRate = 130; + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + setPeakRefreshRate(peakRefreshRate); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + peakRefreshRate); + } + + @Test + public void testMinRefreshRate_FlagEnabled() { + when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) + .thenReturn(true); + float highestRefreshRate = 130; + doReturn(highestRefreshRate).when(() -> + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + setMinRefreshRate(Float.POSITIVE_INFINITY); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate, + /* frameRateHigh= */ Float.POSITIVE_INFINITY); + } + + @Test + public void testMinRefreshRate_FlagDisabled() { + when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) + .thenReturn(false); + float minRefreshRate = 130; + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + setMinRefreshRate(minRefreshRate); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate, + /* frameRateHigh= */ Float.POSITIVE_INFINITY); + } + + @Test public void testSensorRegistration() { // First, configure brightness zones or DMD won't register for sensor data. final FakeDeviceConfig config = mInjector.getDeviceConfig(); @@ -3167,6 +3264,13 @@ public class DisplayModeDirectorTest { waitForIdleSync(); } + private void setMinRefreshRate(float fps) { + Settings.System.putFloat(mContext.getContentResolver(), Settings.System.MIN_REFRESH_RATE, + fps); + mInjector.notifyMinRefreshRateChanged(); + waitForIdleSync(); + } + private static SensorManager createMockSensorManager(Sensor... sensors) { SensorManager sensorManager = mock(SensorManager.class); when(sensorManager.getSensorList(anyInt())).then((invocation) -> { @@ -3216,6 +3320,7 @@ public class DisplayModeDirectorTest { private final SensorManagerInternal mSensorManagerInternal; private ContentObserver mPeakRefreshRateObserver; + private ContentObserver mMinRefreshRateObserver; FakesInjector() { this(null, null, null); @@ -3247,6 +3352,12 @@ public class DisplayModeDirectorTest { } @Override + public void registerMinRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + mMinRefreshRateObserver = observer; + } + + @Override public void registerDisplayListener(DisplayListener listener, Handler handler) {} @Override @@ -3318,5 +3429,12 @@ public class DisplayModeDirectorTest { PEAK_REFRESH_RATE_URI); } } + + void notifyMinRefreshRateChanged() { + if (mMinRefreshRateObserver != null) { + mMinRefreshRateObserver.dispatchChange(false /*selfChange*/, + MIN_REFRESH_RATE_URI); + } + } } } diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp index 7c237ac6befb..086e84b86aca 100644 --- a/services/tests/inprocesstests/Android.bp +++ b/services/tests/inprocesstests/Android.bp @@ -14,7 +14,7 @@ android_test { "androidx.test.core", "androidx.test.rules", "services.core", - "truth-prebuilt", + "truth", "platform-test-annotations", ], test_suites: ["general-tests"], diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 101498aef183..063af573e1f3 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -46,6 +46,7 @@ android_test { "androidx.test.espresso.core", "androidx.test.espresso.contrib", "androidx.test.ext.truth", + "flag-junit", "frameworks-base-testutils", "hamcrest-library", "kotlin-test", @@ -67,7 +68,7 @@ android_test { "servicestests-core-utils", "servicestests-utils-mockito-extended", "testables", - "truth-prebuilt", + "truth", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows "testng", "compatibility-device-util-axt", diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp index f1dc1fa1a108..1eb9888489cb 100644 --- a/services/tests/mockingservicestests/jni/Android.bp +++ b/services/tests/mockingservicestests/jni/Android.bp @@ -22,6 +22,7 @@ cc_library_shared { srcs: [ ":lib_cachedAppOptimizer_native", ":lib_gameManagerService_native", + ":lib_oomConnection_native", "onload.cpp", ], @@ -42,6 +43,7 @@ cc_library_shared { "libgui", "libhidlbase", "liblog", + "libmemevents", "libmeminfo", "libnativehelper", "libprocessgroup", diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp index 23ccb22321b2..fb910513adda 100644 --- a/services/tests/mockingservicestests/jni/onload.cpp +++ b/services/tests/mockingservicestests/jni/onload.cpp @@ -26,6 +26,7 @@ namespace android { int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_app_GameManagerService(JNIEnv* env); +int register_android_server_am_OomConnection(JNIEnv* env); }; using namespace android; @@ -42,6 +43,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) ALOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_am_CachedAppOptimizer(env); register_android_server_app_GameManagerService(env); + register_android_server_am_OomConnection(env); return JNI_VERSION_1_4; } diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 5b1508b8393b..be29163e7677 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -1290,7 +1290,8 @@ public class DeviceIdleControllerTest { } @Test - public void testLightStepIdleStateIdlingTimeIncreases() { + public void testLightStepIdleStateIdlingTimeIncreasesExponentially() { + mConstants.LIGHT_IDLE_INCREASE_LINEARLY = false; final long maintenanceTimeMs = 60_000L; mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs; mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs; @@ -1335,13 +1336,88 @@ public class DeviceIdleControllerTest { eq(mInjector.nowElapsed + idlingTimeMs), anyLong(), anyString(), any(), any(Handler.class)); - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < 10; ++i) { // IDLE->MAINTENANCE alarm mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); alarmListener.onAlarm(); verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE); long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs; idlingTimeMs *= mConstants.LIGHT_IDLE_FACTOR; + idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT); + // Set MAINTENANCE->IDLE + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(maintenanceExpiryTime), + anyLong(), anyString(), any(), any(Handler.class)); + + // MAINTENANCE->IDLE alarm + mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); + alarmListener.onAlarm(); + verifyLightStateConditions(LIGHT_STATE_IDLE); + // Set IDLE->MAINTENANCE again + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(mInjector.nowElapsed + idlingTimeMs), + anyLong(), anyString(), any(), any(Handler.class)); + } + } + + @Test + public void testLightStepIdleStateIdlingTimeIncreasesLinearly() { + mConstants.LIGHT_IDLE_INCREASE_LINEARLY = true; + final long maintenanceTimeMs = 60_000L; + mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs; + mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs; + mConstants.LIGHT_IDLE_TIMEOUT = 5 * 60_000L; + mConstants.LIGHT_MAX_IDLE_TIMEOUT = 30 * 60_000L; + mConstants.LIGHT_IDLE_FACTOR = 2f; + mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = 2 * 60_000L; + + setNetworkConnected(true); + mDeviceIdleController.setJobsActive(false); + mDeviceIdleController.setAlarmsActive(false); + mDeviceIdleController.setActiveIdleOpsForTest(0); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = ArgumentCaptor + .forClass(AlarmManager.OnAlarmListener.class); + doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(), + eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any()); + + // Set state to INACTIVE. + mDeviceIdleController.becomeActiveLocked("testing", 0); + setChargingOn(false); + setScreenOn(false); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + long idlingTimeMs = mConstants.LIGHT_IDLE_TIMEOUT; + final long idleAfterInactiveExpiryTime = + mInjector.nowElapsed + mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT; + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(idleAfterInactiveExpiryTime), + anyLong(), anyString(), any(), any(Handler.class)); + + final AlarmManager.OnAlarmListener alarmListener = + alarmListenerCaptor.getAllValues().get(0); + + // INACTIVE -> IDLE alarm + mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); + alarmListener.onAlarm(); + verifyLightStateConditions(LIGHT_STATE_IDLE); + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(mInjector.nowElapsed + idlingTimeMs), + anyLong(), anyString(), any(), any(Handler.class)); + + for (int i = 0; i < 10; ++i) { + // IDLE->MAINTENANCE alarm + mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); + alarmListener.onAlarm(); + verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE); + long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs; + idlingTimeMs += mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS; + idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT); // Set MAINTENANCE->IDLE alarmManagerInOrder.verify(mAlarmManager).setWindow( eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java index 64cc3979f181..9ba4f5b4fe30 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java @@ -218,4 +218,9 @@ public class ActivityManagerInternalTest { assertEquals(errMsg, Thread.State.TERMINATED, getState()); } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 2bc66ace454b..40b5458b06b9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -1210,4 +1210,9 @@ public class ActivityManagerServiceTest { return returnValueForstartUserOnSecondaryDisplay; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java index 1c0989c4fb65..9391d5bfeed4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java @@ -338,4 +338,8 @@ public class AppChildProcessTest { } } + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index d56229c9681f..e15942bb8f9a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -1126,4 +1126,9 @@ public class ApplicationExitInfoTest { }; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java index 0abf46b8ee55..596a3f3d0400 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java @@ -284,4 +284,9 @@ public class AsyncProcessStartTest { return app; } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java index 434d20035369..dfb8fda56edf 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java @@ -803,4 +803,9 @@ public class CacheOomRankerTest { return mHandler; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java index fd1b06830a89..7ec27be0bfc3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java @@ -201,4 +201,9 @@ public final class ServiceTimeoutTest { return mActiveServices; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 2b56ea8bba33..bded9b40e591 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -34,6 +34,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER; import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; @@ -66,6 +67,7 @@ import android.net.NetworkPolicyManager; import android.os.Build; import android.os.Looper; import android.os.SystemClock; +import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.CellSignalStrength; import android.telephony.SignalStrength; import android.telephony.TelephonyCallback; @@ -79,6 +81,7 @@ import com.android.server.job.JobSchedulerService.Constants; import com.android.server.net.NetworkPolicyManagerInternal; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -107,6 +110,9 @@ public class ConnectivityControllerTest { @Mock private PackageManager mPackageManager; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private Constants mConstants; private FlexibilityController mFlexibilityController; @@ -898,7 +904,8 @@ public class ConnectivityControllerTest { assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); } - // Metered network is only when prefetching, late, and in opportunistic quota + // Metered network is only when prefetching, charging*, late, and in opportunistic quota + // *Charging only when the flag is enabled { final Network net = mock(Network.class); final NetworkCapabilities caps = createCapabilitiesBuilder() @@ -910,10 +917,27 @@ public class ConnectivityControllerTest { assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants)); assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); + mSetFlagsRule.disableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER); + when(mService.isBatteryCharging()).thenReturn(false); + + when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota( + any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS))) + .thenReturn(9876543210L); + assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); + // Only relax restrictions when we at least know the estimated download bytes. + assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); + assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); + mSetFlagsRule.enableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER); when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota( any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS))) .thenReturn(9876543210L); + assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants)); + // Only relax restrictions when we at least know the estimated download bytes. + assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); + assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); + + when(mService.isBatteryCharging()).thenReturn(true); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); // Only relax restrictions when we at least know the estimated download bytes. assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); 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 6304270f9a76..305569edd2fa 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -308,7 +308,6 @@ public final class UserManagerServiceTest { addDefaultProfileAndParent(); mUms.setBootUser(PROFILE_USER_ID); - // Boot user not switchable so return most recently in foreground. assertWithMessage("getBootUser") .that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID); @@ -523,6 +522,24 @@ public final class UserManagerServiceTest { .isFalse(); } + @Test + public void testCreateUserWithLongName_TruncatesName() { + UserInfo user = mUms.createUserWithThrow(generateLongString(), USER_TYPE_FULL_SECONDARY, 0); + assertThat(user.name.length()).isEqualTo(500); + UserInfo user1 = mUms.createUserWithThrow("Test", USER_TYPE_FULL_SECONDARY, 0); + assertThat(user1.name.length()).isEqualTo(4); + } + + private String generateLongString() { + String partialString = "Test Name Test Name Test Name Test Name Test Name Test Name Test " + + "Name Test Name Test Name Test Name "; //String of length 100 + StringBuilder resultString = new StringBuilder(); + for (int i = 0; i < 660; i++) { + resultString.append(partialString); + } + return resultString.toString(); + } + private void removeNonSystemUsers() { for (UserInfo user : mUms.getUsers(true)) { if (!user.getUserHandle().isSystem()) { diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index 8ab45070a017..18a4f0068909 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -16,7 +16,7 @@ android_test { "coretests-aidl", "platformprotosnano", "junit", - "truth-prebuilt", + "truth", "androidx.test.runner", "androidx.test.ext.junit", "androidx.test.ext.truth", diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 20d8a5d1efeb..2ece8c74420c 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -2,6 +2,13 @@ // Build FrameworksServicesTests package //######################################################################## +java_defaults { + name: "FrameworksServicesTests-jni-defaults", + jni_libs: [ + "libservicestestjni", + ], +} + package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import @@ -13,6 +20,9 @@ package { android_test { name: "FrameworksServicesTests", + defaults: [ + "FrameworksServicesTests-jni-defaults", + ], // Include all test java files. srcs: [ @@ -51,7 +61,7 @@ android_test { "mockito-target-minus-junit4", "platform-test-annotations", "ShortcutManagerTestUtils", - "truth-prebuilt", + "truth", "testables", "androidx.test.uiautomator_uiautomator", "platformprotosnano", @@ -62,7 +72,7 @@ android_test { // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", - "truth-prebuilt", + "truth", "junit", "junit-params", "ActivityContext", diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp new file mode 100644 index 000000000000..174beb81d3eb --- /dev/null +++ b/services/tests/servicestests/jni/Android.bp @@ -0,0 +1,58 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_library_shared { + name: "libservicestestjni", + + defaults: ["android.hardware.graphics.common-ndk_shared"], + + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], + + srcs: [ + ":lib_cachedAppOptimizer_native", + ":lib_gameManagerService_native", + ":lib_oomConnection_native", + "onload.cpp", + ], + + include_dirs: [ + "frameworks/base/libs", + "frameworks/native/services", + "frameworks/native/libs/math/include", + "frameworks/native/libs/ui/include", + "system/memory/libmeminfo/include", + ], + + shared_libs: [ + "libandroid", + "libandroid_runtime", + "libbase", + "libbinder", + "libgralloctypes", + "libgui", + "libhidlbase", + "liblog", + "libmeminfo", + "libmemevents", + "libnativehelper", + "libprocessgroup", + "libutils", + "libcutils", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", + "android.hardware.graphics.common@1.2", + "android.hardware.graphics.mapper@4.0", + "android.hidl.token@1.0-utils", + ], +}
\ No newline at end of file diff --git a/services/tests/servicestests/jni/onload.cpp b/services/tests/servicestests/jni/onload.cpp new file mode 100644 index 000000000000..f160b3d97367 --- /dev/null +++ b/services/tests/servicestests/jni/onload.cpp @@ -0,0 +1,48 @@ +/* + * 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. + */ + +/* + * this is a mini native libaray for cached app optimizer tests to run properly. It + * loads all the native methods necessary. + */ +#include <nativehelper/JNIHelp.h> +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +namespace android { +int register_android_server_am_CachedAppOptimizer(JNIEnv* env); +int register_android_server_app_GameManagerService(JNIEnv* env); +int register_android_server_am_OomConnection(JNIEnv* env); +}; + +using namespace android; + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) +{ + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + ALOGE("GetEnv failed!"); + return result; + } + ALOG_ASSERT(env, "Could not retrieve the env!"); + register_android_server_am_CachedAppOptimizer(env); + register_android_server_app_GameManagerService(env); + register_android_server_am_OomConnection(env); + return JNI_VERSION_1_4; +} diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml index 0115db63e56b..ef19ba11fb6d 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml @@ -40,6 +40,7 @@ mediaSharedWithParent='true' credentialShareableWithParent='false' showInSettings='23' + hideInSettingsInQuietMode='true' inheritDevicePolicy='450' deleteAppWithParent='false' alwaysVisible='true' diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java index 8a057df7e836..0988eeab5913 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java @@ -366,6 +366,7 @@ public class FlashNotificationsControllerTest { private void assumeCameraTorchAvailable() { assumeTrue(mCameraManager != null); assumeTrue(!mCameraInfoMap.isEmpty()); + assumeTrue(mCameraInfoMap.values().stream().anyMatch(info -> info.mIsValid)); } private void simulateCallSequence() throws InterruptedException { diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java index acdfee9af557..c0051c6c9e17 100644 --- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java @@ -172,4 +172,9 @@ public class AnrHelperTest { anyString(), any(), any(), any(), anyBoolean(), any(), eq(mAuxExecutorService), anyBoolean(), anyBoolean(), any()); } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("servicestestjni"); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java index 9fdbdda38c75..70527ce2ad32 100644 --- a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java @@ -228,6 +228,7 @@ public class AnrTimerTest { TestHandler mTestHandler; TestInjector(int skip, boolean immediate) { + super(mHandler); mTracker = new TestTracker(skip); mImmediate = immediate; } @@ -249,9 +250,16 @@ public class AnrTimerTest { return mTestHandler; } + @Override AnrTimer.CpuTracker getTracker() { return mTracker; } + + /** For test purposes, always enable the feature. */ + @Override + boolean getFeatureEnabled() { + return true; + } } // Tests @@ -261,7 +269,6 @@ public class AnrTimerTest { // 4. Start a couple of timers. Verify max active timers. Discard one and verify the active // count drops by 1. Accept one and verify the active count drops by 1. - @Test public void testSimpleTimeout() throws Exception { // Create an immediate TestHandler. 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 61b30a024478..e8cbcf9a6874 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 @@ -233,6 +233,7 @@ public class VirtualDeviceManagerServiceTest { private VirtualDeviceManagerService mVdms; private VirtualDeviceManagerInternal mLocalService; private VirtualDeviceManagerService.VirtualDeviceManagerImpl mVdm; + private VirtualDeviceManagerService.VirtualDeviceManagerNativeImpl mVdmNative; private VirtualDeviceLog mVirtualDeviceLog; @Mock private InputController.NativeWrapper mNativeWrapperMock; @@ -340,6 +341,7 @@ public class VirtualDeviceManagerServiceTest { mSetFlagsRule.disableFlags(Flags.FLAG_DYNAMIC_POLICY); mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS); mSetFlagsRule.disableFlags(Flags.FLAG_VDM_CUSTOM_HOME); + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_NATIVE_VDM); doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt()); doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt()); @@ -384,6 +386,7 @@ public class VirtualDeviceManagerServiceTest { mVdms = new VirtualDeviceManagerService(mContext); mLocalService = mVdms.getLocalServiceInstance(); mVdm = mVdms.new VirtualDeviceManagerImpl(); + mVdmNative = mVdms.new VirtualDeviceManagerNativeImpl(); mVirtualDeviceLog = new VirtualDeviceLog(mContext); mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1); mSensorController = mDeviceImpl.getSensorControllerForTest(); @@ -440,24 +443,32 @@ public class VirtualDeviceManagerServiceTest { public void getDevicePolicy_invalidDeviceId_returnsDefault() { assertThat(mVdm.getDevicePolicy(DEVICE_ID_INVALID, POLICY_TYPE_SENSORS)) .isEqualTo(DEVICE_POLICY_DEFAULT); + assertThat(mVdmNative.getDevicePolicy(DEVICE_ID_INVALID, POLICY_TYPE_SENSORS)) + .isEqualTo(DEVICE_POLICY_DEFAULT); } @Test public void getDevicePolicy_defaultDeviceId_returnsDefault() { assertThat(mVdm.getDevicePolicy(DEVICE_ID_DEFAULT, POLICY_TYPE_SENSORS)) .isEqualTo(DEVICE_POLICY_DEFAULT); + assertThat(mVdmNative.getDevicePolicy(DEVICE_ID_DEFAULT, POLICY_TYPE_SENSORS)) + .isEqualTo(DEVICE_POLICY_DEFAULT); } @Test public void getDevicePolicy_nonExistentDeviceId_returnsDefault() { assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS)) .isEqualTo(DEVICE_POLICY_DEFAULT); + assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS)) + .isEqualTo(DEVICE_POLICY_DEFAULT); } @Test public void getDevicePolicy_unspecifiedPolicy_returnsDefault() { assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) .isEqualTo(DEVICE_POLICY_DEFAULT); + assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) + .isEqualTo(DEVICE_POLICY_DEFAULT); } @Test @@ -472,6 +483,8 @@ public class VirtualDeviceManagerServiceTest { assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) .isEqualTo(DEVICE_POLICY_CUSTOM); + assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) + .isEqualTo(DEVICE_POLICY_CUSTOM); } @Test @@ -567,8 +580,8 @@ public class VirtualDeviceManagerServiceTest { @Test public void getDeviceIdsForUid_noRunningApps_returnsNull() { - Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1); - assertThat(deviceIds).isEmpty(); + assertThat(mLocalService.getDeviceIdsForUid(UID_1)).isEmpty(); + assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).isEmpty(); } @Test @@ -577,8 +590,8 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged( Sets.newArraySet(UID_2)); - Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1); - assertThat(deviceIds).isEmpty(); + assertThat(mLocalService.getDeviceIdsForUid(UID_1)).isEmpty(); + assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).isEmpty(); } @Test @@ -587,8 +600,9 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged( Sets.newArraySet(UID_1)); - Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1); - assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId()); + int deviceId = mDeviceImpl.getDeviceId(); + assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId); + assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId); } @Test @@ -598,8 +612,9 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged( Sets.newArraySet(UID_1, UID_2)); - Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1); - assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId()); + int deviceId = mDeviceImpl.getDeviceId(); + assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId); + assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId); } @Test @@ -611,8 +626,9 @@ public class VirtualDeviceManagerServiceTest { secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged( Sets.newArraySet(UID_1)); - Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1); - assertThat(deviceIds).containsExactly(secondDevice.getDeviceId()); + int deviceId = secondDevice.getDeviceId(); + assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId); + assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId); } @Test @@ -628,8 +644,9 @@ public class VirtualDeviceManagerServiceTest { secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged( Sets.newArraySet(UID_1, UID_2)); - Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1); - assertThat(deviceIds).containsExactly( + assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly( + mDeviceImpl.getDeviceId(), secondDevice.getDeviceId()); + assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly( mDeviceImpl.getDeviceId(), secondDevice.getDeviceId()); } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 7d735632b675..7dcfc88e998c 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -284,6 +284,7 @@ public final class DeviceStateManagerServiceTest { assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier()); } + @FlakyTest(bugId = 297949293) @Test public void getDeviceStateInfo_baseStateAndCommittedStateNotSet() throws RemoteException { // Create a provider and a service without an initial base state. 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 184c976b86bf..3de167e72ba0 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 @@ -336,7 +336,8 @@ public final class UpdatableFontDirTest { return new FontConfig(Collections.emptyList(), Collections.emptyList(), Collections.singletonList(new FontConfig.NamedFamilyList( - Collections.singletonList(family), "sans-serif")), 0, 1); + Collections.singletonList(family), "sans-serif")), + Collections.emptyList(), 0, 1); }; UpdatableFontDir dirForPreparation = new UpdatableFontDir( @@ -499,7 +500,8 @@ public final class UpdatableFontDirTest { Collections.emptyList(), Collections.emptyList(), Collections.singletonList(new FontConfig.NamedFamilyList( - Collections.singletonList(family), "sans-serif")), 0, 1); + Collections.singletonList(family), "sans-serif")), + Collections.emptyList(), 0, 1); }); dir.loadFontFileMap(); @@ -651,7 +653,8 @@ public final class UpdatableFontDirTest { FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE); return new FontConfig(Collections.emptyList(), Collections.emptyList(), Collections.singletonList(new FontConfig.NamedFamilyList( - Collections.singletonList(family), "sans-serif")), 0, 1); + Collections.singletonList(family), "sans-serif")), + Collections.emptyList(), 0, 1); }); dir.loadFontFileMap(); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 9f75cf8d552e..253592c9a07d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -43,6 +43,7 @@ import android.annotation.UserIdInt; import android.app.PropertyInvalidatedCache; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; +import android.multiuser.Flags; import android.os.Looper; import android.os.Parcel; import android.os.UserHandle; @@ -124,18 +125,34 @@ public class UserManagerServiceUserInfoTest { mUserManagerService.putUserInfo(data.info); - // Set a global and user restriction so they get written out to the user file. + //Local restrictions are written to the user specific files and global restrictions + // are written to the SYSTEM user file. setUserRestrictions(data.info.id, globalRestriction, localRestriction, true); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos); mUserManagerService.writeUserLP(data, out); - byte[] bytes = baos.toByteArray(); + byte[] secondaryUserBytes = baos.toByteArray(); + baos.reset(); + + byte[] systemUserBytes = new byte[0]; + if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) { + UserData systemUserData = new UserData(); + systemUserData.info = mUserManagerService.getUserInfo(UserHandle.USER_SYSTEM); + mUserManagerService.writeUserLP(systemUserData, baos); + systemUserBytes = baos.toByteArray(); + } // Clear the restrictions to see if they are properly read in from the user file. setUserRestrictions(data.info.id, globalRestriction, localRestriction, false); - mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes)); + //read the secondary and SYSTEM user file to fetch local/global device policy restrictions. + mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(secondaryUserBytes)); + if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) { + mUserManagerService.readUserLP(UserHandle.USER_SYSTEM, + new ByteArrayInputStream(systemUserBytes)); + } + assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction)); assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(localRestriction)); } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java index a54bc914f686..c684a7bbf884 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java @@ -60,6 +60,7 @@ public class UserManagerServiceUserPropertiesTest { .setShowInLauncher(21) .setStartWithParent(false) .setShowInSettings(45) + .setHideInSettingsInQuietMode(false) .setInheritDevicePolicy(67) .setUseParentsContacts(false) .setCrossProfileIntentFilterAccessControl(10) @@ -72,6 +73,7 @@ public class UserManagerServiceUserPropertiesTest { final UserProperties actualProps = new UserProperties(defaultProps); actualProps.setShowInLauncher(14); actualProps.setShowInSettings(32); + actualProps.setHideInSettingsInQuietMode(true); actualProps.setInheritDevicePolicy(51); actualProps.setUseParentsContacts(true); actualProps.setCrossProfileIntentFilterAccessControl(20); @@ -228,6 +230,8 @@ public class UserManagerServiceUserPropertiesTest { assertThat(expected.getShowInLauncher()).isEqualTo(actual.getShowInLauncher()); assertThat(expected.getStartWithParent()).isEqualTo(actual.getStartWithParent()); assertThat(expected.getShowInSettings()).isEqualTo(actual.getShowInSettings()); + assertThat(expected.getHideInSettingsInQuietMode()) + .isEqualTo(actual.getHideInSettingsInQuietMode()); assertThat(expected.getInheritDevicePolicy()).isEqualTo(actual.getInheritDevicePolicy()); assertThat(expected.getUseParentsContacts()).isEqualTo(actual.getUseParentsContacts()); assertThat(expected.getCrossProfileIntentFilterAccessControl()) diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index e3579b4b9ef6..20270a817831 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -90,6 +90,7 @@ public class UserManagerServiceUserTypeTest { .setMediaSharedWithParent(true) .setCredentialShareableWithParent(false) .setShowInSettings(900) + .setHideInSettingsInQuietMode(true) .setInheritDevicePolicy(340) .setDeleteAppWithParent(true) .setAlwaysVisible(true); @@ -160,6 +161,7 @@ public class UserManagerServiceUserTypeTest { assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent()); assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent()); assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings()); + assertTrue(type.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode()); assertEquals(340, type.getDefaultUserPropertiesReference() .getInheritDevicePolicy()); assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent()); @@ -217,6 +219,7 @@ public class UserManagerServiceUserTypeTest { assertFalse(props.isCredentialShareableWithParent()); assertFalse(props.getDeleteAppWithParent()); assertFalse(props.getAlwaysVisible()); + assertFalse(props.getHideInSettingsInQuietMode()); assertFalse(type.hasBadge()); } @@ -304,6 +307,7 @@ public class UserManagerServiceUserTypeTest { .setMediaSharedWithParent(false) .setCredentialShareableWithParent(true) .setShowInSettings(20) + .setHideInSettingsInQuietMode(false) .setInheritDevicePolicy(21) .setDeleteAppWithParent(true) .setAlwaysVisible(false); @@ -344,6 +348,7 @@ public class UserManagerServiceUserTypeTest { assertTrue(aospType.getDefaultUserPropertiesReference() .isCredentialShareableWithParent()); assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings()); + assertFalse(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode()); assertEquals(21, aospType.getDefaultUserPropertiesReference() .getInheritDevicePolicy()); assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent()); @@ -390,6 +395,7 @@ public class UserManagerServiceUserTypeTest { assertFalse(aospType.getDefaultUserPropertiesReference() .isCredentialShareableWithParent()); assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings()); + assertTrue(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode()); assertEquals(450, aospType.getDefaultUserPropertiesReference() .getInheritDevicePolicy()); assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent()); diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 2dacda0af7f4..8891413dd964 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -31,6 +31,7 @@ android_test { "androidx.test.rules", "hamcrest-library", "mockito-target-inline-minus-junit4", + "platform-compat-test-rules", "platform-test-annotations", "platformprotosnano", "statsdprotolite", @@ -38,10 +39,12 @@ android_test { "hamcrest-library", "servicestests-utils", "testables", - "truth-prebuilt", + "truth", // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", + "flag-junit", + "notification_flags_lib", ], libs: [ diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 974238427587..42ad73a23f0e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -32,6 +32,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -147,6 +148,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { private static final int CUSTOM_LIGHT_ON = 10000; private static final int CUSTOM_LIGHT_OFF = 10000; private static final int MAX_VIBRATION_DELAY = 1000; + private static final float DEFAULT_VOLUME = 1.0f; @Before public void setUp() throws Exception { @@ -397,19 +399,22 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { // private void verifyNeverBeep() throws RemoteException { - verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any()); + verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any(), anyFloat()); } private void verifyBeepUnlooped() throws RemoteException { - verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any()); + verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any(), + eq(DEFAULT_VOLUME)); } private void verifyBeepLooped() throws RemoteException { - verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any()); + verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any(), + eq(DEFAULT_VOLUME)); } private void verifyBeep(int times) throws RemoteException { - verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any()); + verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any(), + eq(DEFAULT_VOLUME)); } private void verifyNeverStopAudio() throws RemoteException { @@ -905,7 +910,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { verifyDelayedVibrate( mService.getVibratorHelper().createFallbackVibration(/* insistent= */ false)); verify(mRingtonePlayer, never()).playAsync - (anyObject(), anyObject(), anyBoolean(), anyObject()); + (anyObject(), anyObject(), anyBoolean(), anyObject(), anyFloat()); assertTrue(r.isInterruptive()); assertNotEquals(-1, r.getLastAudiblyAlertedMs()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java index 81867df74abd..9bd938f2e0a7 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java @@ -57,6 +57,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.graphics.Color; import android.graphics.drawable.Icon; import android.media.AudioAttributes; @@ -66,9 +67,11 @@ import android.os.Handler; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; @@ -87,8 +90,11 @@ import com.android.server.UiServiceTestCase; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; import com.android.server.pm.PackageManagerService; + +import java.util.List; import java.util.Objects; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -102,6 +108,8 @@ import org.mockito.verification.VerificationMode; @RunWith(AndroidJUnit4.class) @SuppressLint("GuardedBy") public class NotificationAttentionHelperTest extends UiServiceTestCase { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock AudioManager mAudioManager; @Mock Vibrator mVibrator; @@ -115,6 +123,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { IAccessibilityManager mAccessibilityService; @Mock KeyguardManager mKeyguardManager; + @Mock + private UserManager mUserManager; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @@ -134,6 +144,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { private AccessibilityManager mAccessibilityManager; private static final NotificationAttentionHelper.Signals DEFAULT_SIGNALS = new NotificationAttentionHelper.Signals(false, 0); + private static final NotificationAttentionHelper.Signals WORK_PROFILE_SIGNALS = + new NotificationAttentionHelper.Signals(true, 0); private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1); private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0); @@ -151,6 +163,7 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { private static final int CUSTOM_LIGHT_ON = 10000; private static final int CUSTOM_LIGHT_OFF = 10000; private static final int MAX_VIBRATION_DELAY = 1000; + private static final float DEFAULT_VOLUME = 1.0f; @Before public void setUp() throws Exception { @@ -178,6 +191,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { // TODO (b/291907312): remove feature flag mTestFlagResolver.setFlagOverride(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR, true); + // Disable feature flags by default. Tests should enable as needed. + mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_EXPIRE_BITMAPS); mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger, mNotificationInstanceIdSequence)); @@ -189,9 +204,9 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { private void initAttentionHelper(TestableFlagResolver flagResolver) { mAttentionHelper = new NotificationAttentionHelper(getContext(), mock(LightsManager.class), - mAccessibilityManager, getContext().getPackageManager(), mUsageStats, + mAccessibilityManager, getContext().getPackageManager(), mUserManager, mUsageStats, mService.mNotificationManagerPrivate, mock(ZenModeHelper.class), flagResolver); - mAttentionHelper.setVibratorHelper(new VibratorHelper(getContext())); + mAttentionHelper.setVibratorHelper(spy(new VibratorHelper(getContext()))); mAttentionHelper.setAudioManager(mAudioManager); mAttentionHelper.setSystemReady(true); mAttentionHelper.setLights(mLight); @@ -282,6 +297,11 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { true /* noisy */, true /* buzzy*/, false /* lights */); } + private NotificationRecord getBuzzyBeepyNotification(UserHandle userHandle) { + return getNotificationRecord(mId, false /* insistent */, false /* once */, + true /* noisy */, true /* buzzy*/, false /* lights */, userHandle); + } + private NotificationRecord getLightsNotification() { return getNotificationRecord(mId, false /* insistent */, false /* once */, false /* noisy */, false /* buzzy*/, true /* lights */); @@ -312,7 +332,13 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, boolean noisy, boolean buzzy, boolean lights) { return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy, - lights, null, Notification.GROUP_ALERT_ALL, false); + lights, null, Notification.GROUP_ALERT_ALL, false, mUser); + } + + private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, + boolean noisy, boolean buzzy, boolean lights, UserHandle userHandle) { + return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy, + lights, null, Notification.GROUP_ALERT_ALL, false, userHandle); } private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, @@ -320,25 +346,25 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { boolean noisy, boolean buzzy, boolean lights) { return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true, - null, Notification.GROUP_ALERT_ALL, true); + null, Notification.GROUP_ALERT_ALL, true, mUser); } private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) { return getNotificationRecord(mId, false, false, true, false, false, true, true, true, - groupKey, groupAlertBehavior, false); + groupKey, groupAlertBehavior, false, mUser); } private NotificationRecord getLightsNotificationRecord(String groupKey, int groupAlertBehavior) { return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, - true, true, groupKey, groupAlertBehavior, false); + true, true, groupKey, groupAlertBehavior, false, mUser); } private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration, boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior, - boolean isLeanback) { + boolean isLeanback, UserHandle userHandle) { final Builder builder = new Builder(getContext()) .setContentTitle("foo") @@ -399,7 +425,7 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { .thenReturn(isLeanback); StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, - mPid, n, mUser, null, System.currentTimeMillis()); + mPid, n, userHandle, null, System.currentTimeMillis()); NotificationRecord r = new NotificationRecord(context, sbn, mChannel); mService.addNotification(r); return r; @@ -410,19 +436,26 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { // private void verifyNeverBeep() throws RemoteException { - verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any()); + verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any(), anyFloat()); } private void verifyBeepUnlooped() throws RemoteException { - verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any()); + verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any(), + eq(DEFAULT_VOLUME)); } private void verifyBeepLooped() throws RemoteException { - verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any()); + verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any(), + eq(DEFAULT_VOLUME)); } private void verifyBeep(int times) throws RemoteException { - verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any()); + verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any(), + eq(DEFAULT_VOLUME)); + } + + private void verifyBeepVolume(float volume) throws RemoteException { + verify(mRingtonePlayer, times(1)).playAsync(any(), any(), anyBoolean(), any(), eq(volume)); } private void verifyNeverStopAudio() throws RemoteException { @@ -921,7 +954,7 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { mAttentionHelper.getVibratorHelper().createFallbackVibration( /* insistent= */ false)); verify(mRingtonePlayer, never()).playAsync(anyObject(), anyObject(), anyBoolean(), - anyObject()); + anyObject(), anyFloat()); assertTrue(r.isInterruptive()); assertNotEquals(-1, r.getLastAudiblyAlertedMs()); } @@ -1948,6 +1981,259 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { assertEquals(-1, r.getLastAudiblyAlertedMs()); } + // TODO b/270456865: Only one of the two strategies will be released. + // The other one need to be removed + @Test + public void testBeepVolume_politeNotif() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1"); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + initAttentionHelper(flagResolver); + + NotificationRecord r = getBeepyNotification(); + + // set up internal state + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + Mockito.reset(mRingtonePlayer); + + // update should beep at 50% volume + r.isUpdate = true; + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyBeepVolume(0.5f); + + // 2nd update should beep at 0% volume + Mockito.reset(mRingtonePlayer); + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyBeepVolume(0.0f); + + verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt()); + assertNotEquals(-1, r.getLastAudiblyAlertedMs()); + } + + // TODO b/270456865: Only one of the two strategies will be released. + // The other one need to be removed + @Test + public void testBeepVolume_politeNotif_Strategy2() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2"); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + initAttentionHelper(flagResolver); + + NotificationRecord r = getBeepyNotification(); + + // set up internal state + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + Mockito.reset(mRingtonePlayer); + + // update should beep at 0% volume + r.isUpdate = true; + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyBeepVolume(0.0f); + + // 2nd update should beep at 50% volume + Mockito.reset(mRingtonePlayer); + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyBeepVolume(0.5f); + + verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt()); + assertNotEquals(-1, r.getLastAudiblyAlertedMs()); + } + + @Test + public void testVibrationIntensity_politeNotif() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1"); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + initAttentionHelper(flagResolver); + + NotificationRecord r = getBuzzyBeepyNotification(); + + // set up internal state + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + + VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper(); + Mockito.reset(vibratorHelper); + + // update should buzz at 50% intensity + r.isUpdate = true; + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + + verify(vibratorHelper, times(1)).scale(any(), eq(0.5f)); + Mockito.reset(vibratorHelper); + + // 2nd update should buzz at 0% intensity + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verify(vibratorHelper, times(1)).scale(any(), eq(0.0f)); + } + + @Test + public void testVibrationIntensity_politeNotif_Strategy2() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2"); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + initAttentionHelper(flagResolver); + + NotificationRecord r = getBuzzyBeepyNotification(); + + // set up internal state + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + + VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper(); + Mockito.reset(vibratorHelper); + + // update should buzz at 0% intensity + r.isUpdate = true; + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + + verify(vibratorHelper, times(1)).scale(any(), eq(0.0f)); + Mockito.reset(vibratorHelper); + + // 2nd update should buzz at 50% intensity + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verify(vibratorHelper, times(1)).scale(any(), eq(0.5f)); + } + + @Test + public void testBuzzOnlyOnScreenUnlock_politeNotif() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + + // When NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED setting is enabled + Settings.System.putInt(getContext().getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, 1); + + initAttentionHelper(flagResolver); + // And screen is unlocked + mAttentionHelper.setUserPresent(true); + + NotificationRecord r = getBuzzyBeepyNotification(); + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + + // The notification attention should only buzz + verifyNeverBeep(); + verifyVibrate(); + assertNotEquals(-1, r.getLastAudiblyAlertedMs()); + } + + @Test + public void testBeepVolume_politeNotif_disabled() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + + // When NOTIFICATION_COOLDOWN_ENABLED setting is disabled + Settings.System.putInt(getContext().getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 0); + + initAttentionHelper(flagResolver); + + NotificationRecord r = getBeepyNotification(); + + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + Mockito.reset(mRingtonePlayer); + + // update should beep at 100% volume + r.isUpdate = true; + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyBeepVolume(1.0f); + + // 2nd update should beep at 100% volume + Mockito.reset(mRingtonePlayer); + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyBeepVolume(1.0f); + + assertNotEquals(-1, r.getLastAudiblyAlertedMs()); + } + + @Test + public void testBeepVolume_politeNotif_workProfile() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1"); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + + final int workProfileUserId = mUser.getIdentifier() + 1; + + // Enable notifications cooldown for work profile + Settings.System.putIntForUser(getContext().getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 1, workProfileUserId); + + when(mUserManager.getProfiles(mUser.getIdentifier())).thenReturn( + List.of(new UserInfo(workProfileUserId, "work_profile", null, + UserInfo.FLAG_PROFILE | UserInfo.FLAG_MANAGED_PROFILE, + UserInfo.getDefaultUserType(UserInfo.FLAG_MANAGED_PROFILE)))); + + initAttentionHelper(flagResolver); + + final NotificationRecord r = getBuzzyBeepyNotification(UserHandle.of(workProfileUserId)); + + // set up internal state + mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS); + Mockito.reset(mRingtonePlayer); + + // update should beep at 50% volume + r.isUpdate = true; + mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS); + verifyBeepVolume(0.5f); + + // 2nd update should beep at 0% volume + Mockito.reset(mRingtonePlayer); + mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS); + verifyBeepVolume(0.0f); + + verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt()); + assertNotEquals(-1, r.getLastAudiblyAlertedMs()); + } + + @Test + public void testBeepVolume_politeNotif_workProfile_disabled() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS); + TestableFlagResolver flagResolver = new TestableFlagResolver(); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1"); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50); + flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0); + + final int workProfileUserId = mUser.getIdentifier() + 1; + + // Disable notifications cooldown for work profile + Settings.System.putIntForUser(getContext().getContentResolver(), + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 0, workProfileUserId); + + when(mUserManager.getProfiles(mUser.getIdentifier())).thenReturn( + List.of(new UserInfo(workProfileUserId, "work_profile", null, + UserInfo.FLAG_PROFILE | UserInfo.FLAG_MANAGED_PROFILE, + UserInfo.getDefaultUserType(UserInfo.FLAG_MANAGED_PROFILE)))); + + initAttentionHelper(flagResolver); + + final NotificationRecord r = getBuzzyBeepyNotification(UserHandle.of(workProfileUserId)); + + mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS); + Mockito.reset(mRingtonePlayer); + + // update should beep at 100% volume + r.isUpdate = true; + mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS); + verifyBeepVolume(1.0f); + + // 2nd update should beep at 100% volume + Mockito.reset(mRingtonePlayer); + mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS); + verifyBeepVolume(1.0f); + + assertNotEquals(-1, r.getLastAudiblyAlertedMs()); + } + static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> { private final int mRepeatIndex; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java index 312057ee922d..348d1bfd44df 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java @@ -44,6 +44,12 @@ import org.mockito.Captor; import org.mockito.Mock; import java.lang.reflect.Field; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.time.ZoneId; @RunWith(AndroidTestingRunner.class) public class NotificationBitmapJobServiceTest extends UiServiceTestCase { @@ -103,17 +109,39 @@ public class NotificationBitmapJobServiceTest extends UiServiceTestCase { @Test public void testGetTimeUntilRemoval_beforeToday2am_returnTimeUntilToday2am() { - final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 1, - /* today2AM= */ 2, /* tomorrow2AM= */ 26); + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime now = Instant.now().atZone(zoneId); + LocalDate today = now.toLocalDate(); - assertThat(timeUntilRemoval).isEqualTo(1); + LocalTime oneAM = LocalTime.of(/* hour= */ 1, /* minute= */ 0); + LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0); + + ZonedDateTime today1AM = ZonedDateTime.of(today, oneAM, zoneId); + ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId); + ZonedDateTime tomorrow2AM = today2AM.plusDays(1); + + final long msUntilRemoval = mJobService.getTimeUntilRemoval( + /* now= */ today1AM, today2AM, tomorrow2AM); + + assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(1).toMillis()); } @Test public void testGetTimeUntilRemoval_afterToday2am_returnTimeUntilTomorrow2am() { - final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 3, - /* today2AM= */ 2, /* tomorrow2AM= */ 26); + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime now = Instant.now().atZone(zoneId); + LocalDate today = now.toLocalDate(); + + LocalTime threeAM = LocalTime.of(/* hour= */ 3, /* minute= */ 0); + LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0); + + ZonedDateTime today3AM = ZonedDateTime.of(today, threeAM, zoneId); + ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId); + ZonedDateTime tomorrow2AM = today2AM.plusDays(1); + + final long msUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ today3AM, + today2AM, tomorrow2AM); - assertThat(timeUntilRemoval).isEqualTo(23); + assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(23).toMillis()); } }
\ No newline at end of file diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java index d758e71c62a2..3499a12f5954 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java @@ -71,10 +71,10 @@ public class NotificationHistoryJobServiceTest extends UiServiceTestCase { @Before public void setUp() throws Exception { mJobService = new NotificationHistoryJobService(); + mJobService.attachBaseContext(mContext); + mJobService.onCreate(); + mJobService.onBind(/* intent= */ null); // Create JobServiceEngine within JobService. - final Field field = JobService.class.getDeclaredField("mEngine"); - field.setAccessible(true); - field.set(mJobService, mock(JobServiceEngine.class)); mContext.addMockSystemService(JobScheduler.class, mMockJobScheduler); // add NotificationManagerInternal to LocalServices diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 7a55143ba13f..53ca704b6d86 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -23,9 +23,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -49,12 +47,10 @@ import android.graphics.drawable.Icon; import android.os.Binder; import android.os.Build; import android.os.IBinder; -import android.os.Parcel; import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; -import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.NotificationRankingUpdate; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; @@ -158,81 +154,6 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } } - // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking. - @Test - public void testRankingUpdate_parcel() { - NotificationRankingUpdate nru = generateUpdate(); - Parcel parcel = Parcel.obtain(); - nru.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel); - assertEquals(nru, nru1); - } - - // Tests parceling of RankingMap and RankingMap.equals - @Test - public void testRankingMap_parcel() { - RankingMap rmap = generateUpdate().getRankingMap(); - Parcel parcel = Parcel.obtain(); - rmap.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - RankingMap rmap1 = RankingMap.CREATOR.createFromParcel(parcel); - - detailedAssertEquals(rmap, rmap1); - assertEquals(rmap, rmap1); - } - - // Tests parceling of Ranking and Ranking.equals - @Test - public void testRanking_parcel() { - Ranking ranking = generateUpdate().getRankingMap().getRawRankingObject(mKeys[0]); - Parcel parcel = Parcel.obtain(); - ranking.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - Ranking ranking1 = new Ranking(parcel); - detailedAssertEquals("rankings differ: ", ranking, ranking1); - assertEquals(ranking, ranking1); - } - - // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking. - @Test - public void testRankingUpdate_equals() { - NotificationRankingUpdate nru = generateUpdate(); - NotificationRankingUpdate nru2 = generateUpdate(); - detailedAssertEquals(nru, nru2); - assertEquals(nru, nru2); - Ranking tweak = nru2.getRankingMap().getRawRankingObject(mKeys[0]); - tweak.populate( - tweak.getKey(), - tweak.getRank(), - !tweak.matchesInterruptionFilter(), // note the inversion here! - tweak.getLockscreenVisibilityOverride(), - tweak.getSuppressedVisualEffects(), - tweak.getImportance(), - tweak.getImportanceExplanation(), - tweak.getOverrideGroupKey(), - tweak.getChannel(), - (ArrayList) tweak.getAdditionalPeople(), - (ArrayList) tweak.getSnoozeCriteria(), - tweak.canShowBadge(), - tweak.getUserSentiment(), - tweak.isSuspended(), - tweak.getLastAudiblyAlertedMillis(), - tweak.isNoisy(), - (ArrayList) tweak.getSmartActions(), - (ArrayList) tweak.getSmartReplies(), - tweak.canBubble(), - tweak.isTextChanged(), - tweak.isConversation(), - tweak.getConversationShortcutInfo(), - tweak.getRankingAdjustment(), - tweak.isBubble(), - tweak.getProposedImportance(), - tweak.hasSensitiveContent() - ); - assertNotEquals(nru, nru2); - } - @Test public void testLegacyIcons_preM() { TestListenerService service = new TestListenerService(); @@ -275,7 +196,6 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertNull(n.largeIcon); } - // Test data private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"}; @@ -461,48 +381,6 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } } - private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) { - assertEquals(a.getRankingMap(), b.getRankingMap()); - } - - private void detailedAssertEquals(String comment, Ranking a, Ranking b) { - assertEquals(comment, a.getKey(), b.getKey()); - assertEquals(comment, a.getRank(), b.getRank()); - assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter()); - assertEquals(comment, a.getLockscreenVisibilityOverride(), b.getLockscreenVisibilityOverride()); - assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects()); - assertEquals(comment, a.getImportance(), b.getImportance()); - assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation()); - assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey()); - assertEquals(comment, a.getChannel(), b.getChannel()); - assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople()); - assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria()); - assertEquals(comment, a.canShowBadge(), b.canShowBadge()); - assertEquals(comment, a.getUserSentiment(), b.getUserSentiment()); - assertEquals(comment, a.isSuspended(), b.isSuspended()); - assertEquals(comment, a.getLastAudiblyAlertedMillis(), b.getLastAudiblyAlertedMillis()); - assertEquals(comment, a.isNoisy(), b.isNoisy()); - assertEquals(comment, a.getSmartReplies(), b.getSmartReplies()); - assertEquals(comment, a.canBubble(), b.canBubble()); - assertEquals(comment, a.isConversation(), b.isConversation()); - assertEquals(comment, a.getConversationShortcutInfo().getId(), - b.getConversationShortcutInfo().getId()); - assertActionsEqual(a.getSmartActions(), b.getSmartActions()); - assertEquals(a.getProposedImportance(), b.getProposedImportance()); - assertEquals(a.hasSensitiveContent(), b.hasSensitiveContent()); - } - - private void detailedAssertEquals(RankingMap a, RankingMap b) { - Ranking arank = new Ranking(); - Ranking brank = new Ranking(); - assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys()); - for (String key : a.getOrderedKeys()) { - a.getRanking(key, arank); - b.getRanking(key, brank); - detailedAssertEquals("ranking for key <" + key + ">", arank, brank); - } - } - public static class TestListenerService extends NotificationListenerService { private final IBinder binder = new LocalBinder(); public int targetSdk = 0; 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 9543a2de1e13..3d4b4a62e5ac 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -91,7 +91,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER; -import static com.android.server.notification.NotificationManagerService.BITMAP_EXPIRATION_TIME_MS; +import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION; import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; @@ -164,6 +164,7 @@ import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.AssociationInfo; import android.companion.ICompanionDeviceManager; +import android.compat.testing.PlatformCompatChangeRule; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; @@ -270,11 +271,15 @@ import com.android.server.wm.WindowManagerInternal; import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; @@ -317,6 +322,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private final int mUid = Binder.getCallingUid(); private final @UserIdInt int mUserId = UserHandle.getUserId(mUid); + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + private TestableNotificationManagerService mService; private INotificationManager mBinderService; private NotificationManagerInternal mInternalService; @@ -679,7 +687,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mPackageIntentReceiver = broadcastReceivers.get(i); } if (filter.hasAction(Intent.ACTION_USER_SWITCHED)) { - mUserSwitchIntentReceiver = broadcastReceivers.get(i); + // There may be multiple receivers, get the NMS one + if (broadcastReceivers.get(i).toString().contains( + NotificationManagerService.class.getName())) { + mUserSwitchIntentReceiver = broadcastReceivers.get(i); + } } } assertNotNull("package intent receiver should exist", mPackageIntentReceiver); @@ -4828,6 +4840,62 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableCompatChanges({NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION}) + public void testMediaStyle_enforceNoClearFlagEnabled() throws RemoteException { + Notification.MediaStyle style = new Notification.MediaStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, "testMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isEqualTo(FLAG_NO_CLEAR); + } + + @Test + @EnableCompatChanges({NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION}) + public void testCustomMediaStyle_enforceNoClearFlagEnabled() throws RemoteException { + Notification.DecoratedMediaCustomViewStyle style = + new Notification.DecoratedMediaCustomViewStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, + "testCustomMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isEqualTo(FLAG_NO_CLEAR); + } + + @Test + @DisableCompatChanges(NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION) + public void testMediaStyle_enforceNoClearFlagDisabled() throws RemoteException { + Notification.MediaStyle style = new Notification.MediaStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, "testMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isNotEqualTo(FLAG_NO_CLEAR); + } + + @Test + @DisableCompatChanges(NotificationManagerService.ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION) + public void testCustomMediaStyle_enforceNoClearFlagDisabled() throws RemoteException { + Notification.DecoratedMediaCustomViewStyle style = + new Notification.DecoratedMediaCustomViewStyle(); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setStyle(style); + + NotificationRecord posted = createAndPostNotification(nb, + "testCustomMediaStyleSetNoClearFlag"); + + assertThat(posted.getFlags() & FLAG_NO_CLEAR).isNotEqualTo(FLAG_NO_CLEAR); + } + + @Test public void testMediaStyleRemote_hasPermission() throws RemoteException { String deviceName = "device"; mContext.getTestablePermissions().setPermission( @@ -4838,17 +4906,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.getId()) .setStyle(style); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testMediaStyleRemoteHasPermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testMediaStyleRemoteHasPermission"); Bundle extras = posted.getNotification().extras; assertTrue(extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); @@ -4866,17 +4925,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.getId()) .setStyle(style); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testMediaStyleRemoteNoPermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testMediaStyleRemoteNoPermission"); assertFalse(posted.getNotification().extras .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); @@ -4899,17 +4949,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.getId()) .setStyle(style); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testCustomMediaStyleRemoteNoPermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testCustomMediaStyleRemoteNoPermission"); assertFalse(posted.getNotification().extras .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); @@ -4929,16 +4970,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .addExtras(extras); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testSubstituteAppNamePermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testSubstituteAppNameHasPermission"); assertTrue(posted.getNotification().extras .containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)); @@ -4955,16 +4989,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .addExtras(extras); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, - "testSubstituteAppNamePermission", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - NotificationRecord posted = mService.findNotificationLocked( - PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + NotificationRecord posted = createAndPostNotification(nb, + "testSubstituteAppNameNoPermission"); assertFalse(posted.getNotification().extras .containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)); @@ -11358,7 +11385,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { long timePostedMs = System.currentTimeMillis(); if (isExpired) { - timePostedMs -= BITMAP_EXPIRATION_TIME_MS; + timePostedMs -= BITMAP_DURATION.toMillis(); } StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, notification, UserHandle.getUserHandleForUid(mUid), null, timePostedMs); @@ -12656,6 +12683,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(service, times(1)).setDNDMigrationDone(user.id); } + private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName) + throws RemoteException { + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + return mService.findNotificationLocked( + PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + } + private static <T extends Parcelable> T parcelAndUnparcel(T source, Parcelable.Creator<T> creator) { Parcel parcel = Parcel.obtain(); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java index a91bd2b55f76..000355598281 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java @@ -16,6 +16,8 @@ package com.android.server.vibrator; +import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY; +import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF; import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK; import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK; @@ -24,8 +26,13 @@ import static android.view.HapticFeedbackConstants.CLOCK_TICK; import static android.view.HapticFeedbackConstants.CONTEXT_CLICK; import static android.view.HapticFeedbackConstants.SAFE_MODE_ENABLED; import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE; +import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS; +import static android.view.HapticFeedbackConstants.SCROLL_LIMIT; +import static android.view.HapticFeedbackConstants.SCROLL_TICK; + import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.when; @@ -37,6 +44,7 @@ import android.os.VibrationEffect; import android.os.VibratorInfo; import android.util.AtomicFile; import android.util.SparseArray; +import android.view.flags.FeatureFlags; import androidx.test.InstrumentationRegistry; @@ -59,23 +67,25 @@ public class HapticFeedbackVibrationProviderTest { private static final VibrationEffect PRIMITIVE_CLICK_EFFECT = VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK, 0.3497f).compose(); + private static final int[] SCROLL_FEEDBACK_CONSTANTS = + new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK}; private Context mContext = InstrumentationRegistry.getContext(); private VibratorInfo mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO; @Mock private Resources mResourcesMock; + @Mock private FeatureFlags mViewFeatureFlags; @Test public void testNonExistentCustomization_useDefault() throws Exception { // No customization file is set. - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) .isEqualTo(VibrationEffect.get(EFFECT_TICK)); // The customization file specifies no customization. setupCustomizationFile("<haptic-feedback-constants></haptic-feedback-constants>"); - hapticProvider = new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + hapticProvider = createProviderWithDefaultCustomizations(); assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) .isEqualTo(VibrationEffect.get(EFFECT_TICK)); @@ -84,8 +94,7 @@ public class HapticFeedbackVibrationProviderTest { @Test public void testExceptionParsingCustomizations_useDefault() throws Exception { setupCustomizationFile("<bad-xml></bad-xml>"); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) .isEqualTo(VibrationEffect.get(EFFECT_TICK)); @@ -97,8 +106,7 @@ public class HapticFeedbackVibrationProviderTest { SparseArray<VibrationEffect> customizations = new SparseArray<>(); customizations.put(CONTEXT_CLICK, PRIMITIVE_CLICK_EFFECT); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); // The override for `CONTEXT_CLICK` is used. assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) @@ -118,8 +126,7 @@ public class HapticFeedbackVibrationProviderTest { + "</haptic-feedback-constants>"; setupCustomizationFile(xml); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); // The override for `CONTEXT_CLICK` is not used because the vibration is not supported. assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) @@ -137,15 +144,12 @@ public class HapticFeedbackVibrationProviderTest { customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT); // Test with a customization available for `TEXT_HANDLE_MOVE`. - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull(); // Test with no customization available for `TEXT_HANDLE_MOVE`. - hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull(); } @@ -158,16 +162,13 @@ public class HapticFeedbackVibrationProviderTest { customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT); // Test with a customization available for `TEXT_HANDLE_MOVE`. - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)) .isEqualTo(PRIMITIVE_CLICK_EFFECT); // Test with no customization available for `TEXT_HANDLE_MOVE`. - hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)) .isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK)); @@ -181,15 +182,13 @@ public class HapticFeedbackVibrationProviderTest { SparseArray<VibrationEffect> customizations = new SparseArray<>(); customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)) .isEqualTo(PRIMITIVE_CLICK_EFFECT); mockSafeModeEnabledVibration(null); - hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)) .isEqualTo(PRIMITIVE_CLICK_EFFECT); @@ -199,9 +198,7 @@ public class HapticFeedbackVibrationProviderTest { public void testNoValidCustomizationPresentForSafeModeEnabled_resourceBasedVibrationUsed() throws Exception { mockSafeModeEnabledVibration(10, 20, 30, 40); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)) .isEqualTo(VibrationEffect.createWaveform(new long[] {10, 20, 30, 40}, -1)); @@ -211,35 +208,65 @@ public class HapticFeedbackVibrationProviderTest { public void testNoValidCustomizationAndResourcePresentForSafeModeEnabled_noVibrationUsed() throws Exception { mockSafeModeEnabledVibration(null); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)).isNull(); } @Test public void testVibrationAttribute_forNotBypassingIntensitySettings() { - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false); - assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .isEqualTo(0); + assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isFalse(); } @Test public void testVibrationAttribute_forByassingIntensitySettings() { - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true); - assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .isNotEqualTo(0); + assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isTrue(); + } + + @Test + public void testVibrationAttribute_scrollFeedback_scrollApiFlagOn_bypassInterruptPolicy() { + when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); + + for (int effectId : SCROLL_FEEDBACK_CONSTANTS) { + VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( + effectId, /* bypassVibrationIntensitySetting= */ false); + assertWithMessage("Expected FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId) + .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isTrue(); + } + } + + @Test + public void testVibrationAttribute_scrollFeedback_scrollApiFlagOff_noBypassInterruptPolicy() { + when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(false); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); + + for (int effectId : SCROLL_FEEDBACK_CONSTANTS) { + VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( + effectId, /* bypassVibrationIntensitySetting= */ false); + assertWithMessage("Expected no FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId) + .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isFalse(); + } + } + + private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() { + return createProvider(/* customizations= */ null); + } + + private HapticFeedbackVibrationProvider createProvider( + SparseArray<VibrationEffect> customizations) { + return new HapticFeedbackVibrationProvider( + mResourcesMock, mVibratorInfo, customizations, mViewFeatureFlags); } private void mockVibratorPrimitiveSupport(int... supportedPrimitives) { diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 0eec9cd93f3b..40e0e84dca59 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -46,6 +47,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.res.Resources; import android.hardware.input.IInputManager; @@ -87,6 +89,7 @@ import android.util.SparseBooleanArray; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.InputDevice; +import android.view.flags.FeatureFlags; import androidx.test.InstrumentationRegistry; import androidx.test.filters.FlakyTest; @@ -172,6 +175,8 @@ public class VibratorManagerServiceTest { private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock; @Mock private AudioManager mAudioManagerMock; + @Mock + private FeatureFlags mViewFeatureFlags; private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); @@ -321,7 +326,8 @@ public class VibratorManagerServiceTest { HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider( Resources resources, VibratorInfo vibratorInfo) { return new HapticFeedbackVibrationProvider( - resources, vibratorInfo, mHapticFeedbackVibrationMap); + resources, vibratorInfo, mHapticFeedbackVibrationMap, + mViewFeatureFlags); } }); return mService; @@ -649,6 +655,42 @@ public class VibratorManagerServiceTest { } @Test + public void vibrate_withoutBypassFlagsPermissions_bypassFlagsNotApplied() throws Exception { + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(false); + } + + @Test + public void vibrate_withSecureSettingsPermission_bypassFlagsApplied() throws Exception { + grantPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(true); + } + + @Test + public void vibrate_withModifyPhoneStatePermission_bypassFlagsApplied() throws Exception { + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(true); + } + + @Test + public void vibrate_withModifyAudioRoutingPermission_bypassFlagsApplied() throws Exception { + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + grantPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(true); + } + + @Test public void vibrate_withRingtone_usesRingerModeSettings() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); @@ -1166,7 +1208,7 @@ public class VibratorManagerServiceTest { } @Test - public void vibrate_withTriggerCallback_finishesVibration() throws Exception { + public void vibrate_withriggerCallback_finishesVibration() throws Exception { mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE); mockVibrators(1, 2); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); @@ -1303,10 +1345,18 @@ public class VibratorManagerServiceTest { } @Test - public void performHapticFeedback_doesNotRequirePermission() throws Exception { + public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception { + // Deny permissions that would have been required for regular vibrations, and check that + // the vibration proceed as expected to verify that haptic feedback does not need these + // permissions. denyPermission(android.Manifest.permission.VIBRATE); + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + // Flag override to enable the scroll feedack constants to bypass interruption policies. + when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true); mHapticFeedbackVibrationMap.put( - HapticFeedbackConstants.KEYBOARD_TAP, + HapticFeedbackConstants.SCROLL_TICK, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); @@ -1315,13 +1365,16 @@ public class VibratorManagerServiceTest { HalVibration vibration = performHapticFeedbackAndWaitUntilFinished( - service, HapticFeedbackConstants.KEYBOARD_TAP, /* always= */ true); + service, HapticFeedbackConstants.SCROLL_TICK, /* always= */ true); List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments(); assertEquals(1, playedSegments.size()); PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0); assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId()); - assertEquals(VibrationAttributes.USAGE_TOUCH, vibration.callerInfo.attrs.getUsage()); + VibrationAttributes attrs = vibration.callerInfo.attrs; + assertEquals(VibrationAttributes.USAGE_HARDWARE_FEEDBACK, attrs.getUsage()); + assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)); + assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)); } @Test @@ -2261,6 +2314,31 @@ public class VibratorManagerServiceTest { assertNull(metrics.halUnsupportedEffectsUsed); } + private void assertCanVibrateWithBypassFlags(boolean expectedCanApplyBypassFlags) + throws Exception { + mockVibrators(1); + mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + VibratorManagerService service = createSystemReadyService(); + + HalVibration vibration = vibrateAndWaitUntilFinished( + service, + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), + new VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_TOUCH) + .setFlags( + VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF + | VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) + .build()); + + VibrationAttributes attrs = vibration.callerInfo.attrs; + assertEquals( + expectedCanApplyBypassFlags, + attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)); + assertEquals( + expectedCanApplyBypassFlags, + attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)); + } + private VibrationEffectSegment expectedPrebaked(int effectId) { return expectedPrebaked(effectId, VibrationEffect.EFFECT_STRENGTH_MEDIUM); } @@ -2327,12 +2405,13 @@ public class VibratorManagerServiceTest { return vib; } - private void vibrateAndWaitUntilFinished(VibratorManagerService service, VibrationEffect effect, - VibrationAttributes attrs) throws InterruptedException { - vibrateAndWaitUntilFinished(service, CombinedVibration.createParallel(effect), attrs); + private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service, + VibrationEffect effect, VibrationAttributes attrs) throws InterruptedException { + return vibrateAndWaitUntilFinished( + service, CombinedVibration.createParallel(effect), attrs); } - private void vibrateAndWaitUntilFinished(VibratorManagerService service, + private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service, CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException { HalVibration vib = service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME, @@ -2340,6 +2419,8 @@ public class VibratorManagerServiceTest { if (vib != null) { vib.waitForEnd(); } + + return vib; } private void vibrate(VibratorManagerService service, VibrationEffect effect, @@ -2368,7 +2449,15 @@ public class VibratorManagerServiceTest { return predicateResult; } + private void grantPermission(String permission) { + when(mContextSpy.checkCallingOrSelfPermission(permission)) + .thenReturn(PackageManager.PERMISSION_GRANTED); + doNothing().when(mContextSpy).enforceCallingOrSelfPermission(eq(permission), anyString()); + } + private void denyPermission(String permission) { + when(mContextSpy.checkCallingOrSelfPermission(permission)) + .thenReturn(PackageManager.PERMISSION_DENIED); doThrow(new SecurityException()).when(mContextSpy) .enforceCallingOrSelfPermission(eq(permission), anyString()); } diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp index e704ebf32270..744cb63f72b3 100644 --- a/services/tests/voiceinteractiontests/Android.bp +++ b/services/tests/voiceinteractiontests/Android.bp @@ -43,7 +43,7 @@ android_test { "services.soundtrigger", "servicestests-core-utils", "servicestests-utils-mockito-extended", - "truth-prebuilt", + "truth", ], libs: [ diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index c2812a14a3eb..af39b2f027ee 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -57,12 +57,14 @@ android_test { "platform-test-annotations", "servicestests-utils", "testng", - "truth-prebuilt", + "truth", "testables", "hamcrest-library", "platform-compat-test-rules", "CtsSurfaceValidatorLib", "service-sdksandbox.impl", + "com.android.window.flags.window-aconfig-java", + "flag-junit", ], libs: [ diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 42e3383987d6..762e23c8e288 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -47,6 +47,8 @@ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/> + <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/> + <uses-permission android:name="android.permission.MONITOR_INPUT"/> <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" @@ -104,6 +106,11 @@ android:showWhenLocked="true" android:turnScreenOn="true" /> + <activity android:name="android.app.Activity" + android:exported="true" + android:showWhenLocked="true" + android:turnScreenOn="true" /> + <activity android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity" android:exported="true"> diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java index 0a7bb00ce1c2..71098aa5e883 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java @@ -20,6 +20,7 @@ import static com.android.server.policy.PhoneWindowManager.DOUBLE_TAP_HOME_RECEN import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL; +import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL; import android.platform.test.annotations.Presubmit; import android.view.KeyEvent; @@ -284,6 +285,16 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_H, META_ON}}; } + @Keep + private static Object[][] shortPressOnSettingsTestArguments() { + // testName, testKeys, shortPressOnSettingsBehavior, expectedLogEvent, expectedKey, + // expectedModifierState + return new Object[][]{ + {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS}, + SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL, + KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_SETTINGS, 0}}; + } + @Before public void setUp() { setUpPhoneWindowManager(/*supportSettingsUpdate*/ true); @@ -294,6 +305,7 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { mPhoneWindowManager.overrideEnableBugReportTrigger(true); mPhoneWindowManager.overrideStatusBarManagerInternal(); mPhoneWindowManager.overrideStartActivity(); + mPhoneWindowManager.overrideSendBroadcast(); mPhoneWindowManager.overrideUserSetupComplete(); mPhoneWindowManager.setupAssistForLaunch(); mPhoneWindowManager.overrideTogglePanel(); @@ -330,4 +342,15 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent, expectedKey, expectedModifierState, "Failed while executing " + testName); } + + @Test + @Parameters(method = "shortPressOnSettingsTestArguments") + public void testShortPressOnSettings(String testName, int[] testKeys, + int shortPressOnSettingsBehavior, KeyboardLogEvent expectedLogEvent, int expectedKey, + int expectedModifierState) { + mPhoneWindowManager.overrideShortPressOnSettingsBehavior(shortPressOnSettingsBehavior); + sendKeyCombination(testKeys, 0 /* duration */); + mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent, + expectedKey, expectedModifierState, "Failed while executing " + testName); + } } diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index ef28ffa7da8f..2244dbe8af98 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -375,6 +375,10 @@ class TestPhoneWindowManager { mPhoneWindowManager.mDoubleTapOnHomeBehavior = behavior; } + void overrideShortPressOnSettingsBehavior(int behavior) { + mPhoneWindowManager.mShortPressOnSettingsBehavior = behavior; + } + void overrideCanStartDreaming(boolean canDream) { doReturn(canDream).when(mDreamManagerInternal).canStartDreaming(anyBoolean()); } @@ -484,6 +488,10 @@ class TestPhoneWindowManager { doNothing().when(mContext).startActivityAsUser(any(), any(), any()); } + void overrideSendBroadcast() { + doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + } + void overrideUserSetupComplete() { doReturn(true).when(mPhoneWindowManager).isUserSetupComplete(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 3bc6450ae591..c241033c69d3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -549,10 +549,12 @@ public class RootWindowContainerTests extends WindowTestsBase { // Let's pretend that the app has crashed. firstActivity.app.setThread(null); - mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); + final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities( + firstActivity.app, "test"); // Verify that the root task was removed. assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount()); + assertEquals(rootTask, finishedTask); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java index c1d51474b1cd..8119fd486a87 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java @@ -138,11 +138,11 @@ public class SurfaceControlViewHostTests { IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken()); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window, - mScvh1.getFocusGrantToken(), true); + mScvh1.getInputTransferToken(), true); assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true)); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window, - mScvh2.getFocusGrantToken(), true); + mScvh2.getInputTransferToken(), true); assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index 91256ee8a1c4..4a335944228a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -216,7 +216,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { try { final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); mWm.mTaskSnapshotController.createSnapshot(mAppWindow.mActivityRecord.getTask(), - 1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */, builder); + 1f /* scaleFraction */, new Rect() /* crop */, builder); } catch (NullPointerException e) { fail("There should be no exception when calling createTaskSnapshot"); } @@ -238,7 +238,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { spyOn(builder); mWm.mTaskSnapshotController.createSnapshot( mAppWindow.mActivityRecord.getTask(), 1f /* scaleFraction */, - PixelFormat.UNKNOWN, null /* outTaskSize */, builder); + new Rect() /* crop */, builder); // Verify the builder should includes IME surface. verify(builder).setHasImeSurface(eq(true)); builder.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)); @@ -261,7 +261,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot( - mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); + mAppWindow.mActivityRecord.getTask(), builder) != null; assertTrue(success); // The pixel format should be selected automatically. @@ -270,7 +270,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { // Snapshot should not be taken while the rotation of activity and task are different. doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform(); success = mWm.mTaskSnapshotController.prepareTaskSnapshot( - mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); + mAppWindow.mActivityRecord.getTask(), builder) != null; assertFalse(success); } 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 241f7d8937f2..474720f68731 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1442,7 +1442,7 @@ public class TransitionTests extends WindowTestsBase { // normally. mWm.mSyncEngine.abort(openTransition.getSyncId()); - verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2), eq(false)); + verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2)); controller.finishTransition(openTransition); @@ -1482,7 +1482,7 @@ public class TransitionTests extends WindowTestsBase { // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be // called until finish). - verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1), eq(false)); + verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1)); enteringAnimReports.clear(); doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), @@ -1515,7 +1515,7 @@ public class TransitionTests extends WindowTestsBase { assertFalse(activity1.isVisible()); assertFalse(activity1.app.hasActivityInVisibleTask()); - verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1), eq(false)); + verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1)); assertTrue(enteringAnimReports.contains(activity2)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java new file mode 100644 index 000000000000..ac498397eb39 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java @@ -0,0 +1,217 @@ +/* + * 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.wm; + +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.app.Instrumentation; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.server.wm.BuildUtils; +import android.server.wm.CtsWindowInfoUtils; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.window.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@Presubmit +public class TrustedOverlayTests { + private static final String TAG = "TrustedOverlayTests"; + private static final long TIMEOUT_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Rule + public TestName mName = new TestName(); + + @Rule + public final ActivityScenarioRule<Activity> mActivityRule = new ActivityScenarioRule<>( + Activity.class); + + private Instrumentation mInstrumentation; + private Activity mActivity; + + @Before + public void setup() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mActivityRule.getScenario().onActivity(activity -> { + mActivity = activity; + }); + } + + @RequiresFlagsDisabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY) + @Test + public void setTrustedOverlayInputWindow() throws InterruptedException { + testTrustedOverlayChildHelper(false); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY) + public void setTrustedOverlayChildLayer() throws InterruptedException { + testTrustedOverlayChildHelper(true); + } + + /** + * b/300659960 where setting spy window and trusted overlay were not happening in the same + * transaction causing the system to crash. This ensures there are no synchronization issues + * setting both spy window and trusted overlay. + */ + @Test + public void setSpyWindowDoesntCrash() throws InterruptedException { + IBinder[] tokens = new IBinder[1]; + CountDownLatch hostTokenReady = new CountDownLatch(1); + mInstrumentation.runOnMainSync(() -> { + WindowManager.LayoutParams params = mActivity.getWindow().getAttributes(); + params.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + params.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; + mActivity.getWindow().setAttributes(params); + + View rootView = mActivity.getWindow().getDecorView(); + if (rootView.isAttachedToWindow()) { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } else { + rootView.getViewTreeObserver().addOnWindowAttachListener( + new ViewTreeObserver.OnWindowAttachListener() { + @Override + public void onWindowAttached() { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } + + @Override + public void onWindowDetached() { + } + }); + } + }); + + assertTrue("Failed to wait for host to get added", + hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS)); + + boolean[] foundTrusted = new boolean[1]; + CtsWindowInfoUtils.waitForWindowInfos( + windowInfos -> { + for (var windowInfo : windowInfos) { + if (windowInfo.windowToken == tokens[0] && windowInfo.isTrustedOverlay) { + foundTrusted[0] = true; + return true; + } + } + return false; + }, TIMEOUT_S, TimeUnit.SECONDS); + + if (!foundTrusted[0]) { + CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName()); + } + + assertTrue("Failed to find window or was not marked trusted", foundTrusted[0]); + } + + private void testTrustedOverlayChildHelper(boolean expectedTrustedChild) + throws InterruptedException { + IBinder[] tokens = new IBinder[2]; + CountDownLatch hostTokenReady = new CountDownLatch(1); + mInstrumentation.runOnMainSync(() -> { + mActivity.getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY); + View rootView = mActivity.getWindow().getDecorView(); + if (rootView.isAttachedToWindow()) { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } else { + rootView.getViewTreeObserver().addOnWindowAttachListener( + new ViewTreeObserver.OnWindowAttachListener() { + @Override + public void onWindowAttached() { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } + + @Override + public void onWindowDetached() { + } + }); + } + }); + + assertTrue("Failed to wait for host to get added", + hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS)); + + mInstrumentation.runOnMainSync(() -> { + WindowManager wm = mActivity.getSystemService(WindowManager.class); + + View childView = new View(mActivity) { + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + tokens[1] = getWindowToken(); + } + }; + WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.token = tokens[0]; + params.type = TYPE_APPLICATION_PANEL; + wm.addView(childView, params); + }); + + boolean[] foundTrusted = new boolean[2]; + + CtsWindowInfoUtils.waitForWindowInfos( + windowInfos -> { + for (var windowInfo : windowInfos) { + if (windowInfo.windowToken == tokens[0] + && windowInfo.isTrustedOverlay) { + foundTrusted[0] = true; + } else if (windowInfo.windowToken == tokens[1] + && windowInfo.isTrustedOverlay) { + foundTrusted[1] = true; + } + } + return foundTrusted[0] && foundTrusted[1]; + }, TIMEOUT_S, TimeUnit.SECONDS); + + if (!foundTrusted[0] || !foundTrusted[1]) { + CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName()); + } + + assertTrue("Failed to find parent window or was not marked trusted", foundTrusted[0]); + assertEquals("Failed to find child window or was not marked trusted", expectedTrustedChild, + foundTrusted[1]); + } +} diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 67ae0b21e209..f3bf026ddc6e 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -106,6 +106,7 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; @@ -201,7 +202,8 @@ public class UsageStatsService extends SystemService implements static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 9; private final Object mLock = new Object(); - Handler mHandler; + private Handler mHandler; + private Handler mIoHandler; AppOpsManager mAppOps; UserManager mUserManager; PackageManager mPackageManager; @@ -233,7 +235,7 @@ public class UsageStatsService extends SystemService implements private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>(); final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray(); final SparseArray<ActivityData> mVisibleActivities = new SparseArray(); - @GuardedBy("mLock") + @GuardedBy("mLaunchTimeAlarmQueues") // Don't hold the main lock private final SparseArray<LaunchTimeAlarmQueue> mLaunchTimeAlarmQueues = new SparseArray<>(); @GuardedBy("mUsageEventListeners") // Don't hold the main lock when calling out private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners = @@ -279,6 +281,38 @@ public class UsageStatsService extends SystemService implements } } + private final Handler.Callback mIoHandlerCallback = (msg) -> { + switch (msg.what) { + case MSG_UID_STATE_CHANGED: { + final int uid = msg.arg1; + final int procState = msg.arg2; + + final int newCounter = (procState <= ActivityManager.PROCESS_STATE_TOP) ? 0 : 1; + synchronized (mUidToKernelCounter) { + final int oldCounter = mUidToKernelCounter.get(uid, 0); + if (newCounter != oldCounter) { + mUidToKernelCounter.put(uid, newCounter); + try { + FileUtils.stringToFile(KERNEL_COUNTER_FILE, uid + " " + newCounter); + } catch (IOException e) { + Slog.w(TAG, "Failed to update counter set: " + e); + } + } + } + return true; + } + case MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK: { + final int userId = msg.arg1; + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, + "usageStatsHandleEstimatedLaunchTimesOnUser(" + userId + ")"); + handleEstimatedLaunchTimesOnUserUnlock(userId); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + return true; + } + } + return false; + }; + private final Injector mInjector; public UsageStatsService(Context context) { @@ -299,6 +333,7 @@ public class UsageStatsService extends SystemService implements mPackageManager = getContext().getPackageManager(); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mHandler = new H(BackgroundThread.get().getLooper()); + mIoHandler = new Handler(IoThread.get().getLooper(), mIoHandlerCallback); mAppStandby = mInjector.getAppStandbyController(getContext()); mResponseStatsTracker = new BroadcastResponseStatsTracker(mAppStandby, getContext()); @@ -424,6 +459,9 @@ public class UsageStatsService extends SystemService implements } mUserUnlockedStates.remove(userId); mUserState.put(userId, null); // release the service (mainly for GC) + } + + synchronized (mLaunchTimeAlarmQueues) { LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId); if (alarmQueue != null) { alarmQueue.removeAllAlarms(); @@ -456,9 +494,12 @@ public class UsageStatsService extends SystemService implements Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "loadPendingEvents"); loadPendingEventsLocked(userId, pendingEvents); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - final LinkedList<Event> eventsInMem = mReportedEvents.get(userId); - if (eventsInMem != null) { - pendingEvents.addAll(eventsInMem); + synchronized (mReportedEvents) { + final LinkedList<Event> eventsInMem = mReportedEvents.get(userId); + if (eventsInMem != null) { + pendingEvents.addAll(eventsInMem); + mReportedEvents.remove(userId); + } } boolean needToFlush = !pendingEvents.isEmpty(); @@ -476,11 +517,12 @@ public class UsageStatsService extends SystemService implements } reportEvent(unlockEvent, userId); - mHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK, userId, 0).sendToTarget(); + mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK, + userId, 0).sendToTarget(); - // Remove all the stats stored in memory and in system DE. - mReportedEvents.remove(userId); + // Remove all the stats stored in system DE. deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats")); + // Force a flush to disk for the current user to ensure important events are persisted. // Note: there is a very very small chance that the system crashes between deleting // the stats above from DE and persisting them to CE here in which case we will lose @@ -599,7 +641,7 @@ public class UsageStatsService extends SystemService implements private final IUidObserver mUidObserver = new UidObserver() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { - mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget(); + mIoHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget(); } @Override @@ -671,16 +713,18 @@ public class UsageStatsService extends SystemService implements callingPid, callingUid) == PackageManager.PERMISSION_GRANTED); } - private static void deleteRecursively(File f) { - File[] files = f.listFiles(); - if (files != null) { - for (File subFile : files) { - deleteRecursively(subFile); + private static void deleteRecursively(final File path) { + if (path.isDirectory()) { + final File[] files = path.listFiles(); + if (files != null) { + for (File subFile : files) { + deleteRecursively(subFile); + } } } - if (f.exists() && !f.delete()) { - Slog.e(TAG, "Failed to delete " + f); + if (path.exists() && !path.delete()) { + Slog.e(TAG, "Failed to delete " + path); } } @@ -873,6 +917,7 @@ public class UsageStatsService extends SystemService implements } } + @GuardedBy({"mLock", "mReportedEvents"}) private void persistPendingEventsLocked(int userId) { final LinkedList<Event> pendingEvents = mReportedEvents.get(userId); if (pendingEvents == null || pendingEvents.isEmpty()) { @@ -976,7 +1021,7 @@ public class UsageStatsService extends SystemService implements + UserUsageStatsService.eventToString(event.mEventType); Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, traceTag); } - synchronized (mLock) { + synchronized (mReportedEvents) { LinkedList<Event> events = mReportedEvents.get(userId); if (events == null) { events = new LinkedList<>(); @@ -1241,6 +1286,9 @@ public class UsageStatsService extends SystemService implements Slog.i(TAG, "Removing user " + userId + " and all data."); mUserState.remove(userId); mAppTimeLimit.onUserRemoved(userId); + } + + synchronized (mLaunchTimeAlarmQueues) { final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId); if (alarmQueue != null) { alarmQueue.removeAllAlarms(); @@ -1271,6 +1319,13 @@ public class UsageStatsService extends SystemService implements } } + synchronized (mLaunchTimeAlarmQueues) { + final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId); + if (alarmQueue != null) { + alarmQueue.removeAlarmForKey(packageName); + } + } + final int tokenRemoved; synchronized (mLock) { final long timeRemoved = System.currentTimeMillis(); @@ -1279,10 +1334,7 @@ public class UsageStatsService extends SystemService implements // when the user service is initialized and package manager is queried. return; } - final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId); - if (alarmQueue != null) { - alarmQueue.removeAlarmForKey(packageName); - } + final UserUsageStatsService userService = mUserState.get(userId); if (userService == null) { return; @@ -1492,60 +1544,63 @@ public class UsageStatsService extends SystemService implements estimatedLaunchTime = calculateEstimatedPackageLaunchTime(userId, packageName); mAppStandby.setEstimatedLaunchTime(packageName, userId, estimatedLaunchTime); - synchronized (mLock) { - LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId); - if (alarmQueue == null) { - alarmQueue = new LaunchTimeAlarmQueue( - userId, getContext(), BackgroundThread.get().getLooper()); - mLaunchTimeAlarmQueues.put(userId, alarmQueue); - } - alarmQueue.addAlarm(packageName, - SystemClock.elapsedRealtime() + (estimatedLaunchTime - now)); - } + getOrCreateLaunchTimeAlarmQueue(userId).addAlarm(packageName, + SystemClock.elapsedRealtime() + (estimatedLaunchTime - now)); } return estimatedLaunchTime; } + private LaunchTimeAlarmQueue getOrCreateLaunchTimeAlarmQueue(int userId) { + synchronized (mLaunchTimeAlarmQueues) { + LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId); + if (alarmQueue == null) { + alarmQueue = new LaunchTimeAlarmQueue( + userId, getContext(), BackgroundThread.get().getLooper()); + mLaunchTimeAlarmQueues.put(userId, alarmQueue); + } + + return alarmQueue; + } + } + @CurrentTimeMillisLong private long calculateEstimatedPackageLaunchTime(int userId, String packageName) { - synchronized (mLock) { - final long endTime = System.currentTimeMillis(); - final long beginTime = endTime - ONE_WEEK; - final long unknownTime = endTime + UNKNOWN_LAUNCH_TIME_DELAY_MS; - final UsageEvents events = queryEarliestEventsForPackage( - userId, beginTime, endTime, packageName, Event.ACTIVITY_RESUMED); - if (events == null) { - if (DEBUG) { - Slog.d(TAG, "No events for " + userId + ":" + packageName); - } - return unknownTime; + final long endTime = System.currentTimeMillis(); + final long beginTime = endTime - ONE_WEEK; + final long unknownTime = endTime + UNKNOWN_LAUNCH_TIME_DELAY_MS; + final UsageEvents events = queryEarliestEventsForPackage( + userId, beginTime, endTime, packageName, Event.ACTIVITY_RESUMED); + if (events == null) { + if (DEBUG) { + Slog.d(TAG, "No events for " + userId + ":" + packageName); } - final UsageEvents.Event event = new UsageEvents.Event(); - final boolean hasMoreThan24HoursOfHistory; - if (events.getNextEvent(event)) { - hasMoreThan24HoursOfHistory = endTime - event.getTimeStamp() > ONE_DAY; - if (DEBUG) { - Slog.d(TAG, userId + ":" + packageName + " history > 24 hours=" - + hasMoreThan24HoursOfHistory); - } - } else { - if (DEBUG) { - Slog.d(TAG, userId + ":" + packageName + " has no events"); - } - return unknownTime; - } - do { - if (event.getEventType() == Event.ACTIVITY_RESUMED) { - final long timestamp = event.getTimeStamp(); - final long nextLaunch = - calculateNextLaunchTime(hasMoreThan24HoursOfHistory, timestamp); - if (nextLaunch > endTime) { - return nextLaunch; - } - } - } while (events.getNextEvent(event)); return unknownTime; } + final UsageEvents.Event event = new UsageEvents.Event(); + final boolean hasMoreThan24HoursOfHistory; + if (events.getNextEvent(event)) { + hasMoreThan24HoursOfHistory = endTime - event.getTimeStamp() > ONE_DAY; + if (DEBUG) { + Slog.d(TAG, userId + ":" + packageName + " history > 24 hours=" + + hasMoreThan24HoursOfHistory); + } + } else { + if (DEBUG) { + Slog.d(TAG, userId + ":" + packageName + " has no events"); + } + return unknownTime; + } + do { + if (event.getEventType() == Event.ACTIVITY_RESUMED) { + final long timestamp = event.getTimeStamp(); + final long nextLaunch = + calculateNextLaunchTime(hasMoreThan24HoursOfHistory, timestamp); + if (nextLaunch > endTime) { + return nextLaunch; + } + } + } while (events.getNextEvent(event)); + return unknownTime; } @CurrentTimeMillisLong @@ -1566,62 +1621,55 @@ public class UsageStatsService extends SystemService implements } private void handleEstimatedLaunchTimesOnUserUnlock(int userId) { - synchronized (mLock) { - final long nowElapsed = SystemClock.elapsedRealtime(); - final long now = System.currentTimeMillis(); - final long beginTime = now - ONE_WEEK; - final UsageEvents events = queryEarliestAppEvents( - userId, beginTime, now, Event.ACTIVITY_RESUMED); - if (events == null) { - return; - } - final ArrayMap<String, Boolean> hasMoreThan24HoursOfHistory = new ArrayMap<>(); - final UsageEvents.Event event = new UsageEvents.Event(); - LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId); - if (alarmQueue == null) { - alarmQueue = new LaunchTimeAlarmQueue( - userId, getContext(), BackgroundThread.get().getLooper()); - mLaunchTimeAlarmQueues.put(userId, alarmQueue); - } - boolean changedTimes = false; - for (boolean unprocessedEvent = events.getNextEvent(event); unprocessedEvent; - unprocessedEvent = events.getNextEvent(event)) { - final String packageName = event.getPackageName(); - if (!hasMoreThan24HoursOfHistory.containsKey(packageName)) { - boolean hasHistory = now - event.getTimeStamp() > ONE_DAY; - if (DEBUG) { - Slog.d(TAG, - userId + ":" + packageName + " history > 24 hours=" + hasHistory); - } - hasMoreThan24HoursOfHistory.put(packageName, hasHistory); + final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = System.currentTimeMillis(); + final long beginTime = now - ONE_WEEK; + final UsageEvents events = queryEarliestAppEvents( + userId, beginTime, now, Event.ACTIVITY_RESUMED); + if (events == null) { + return; + } + final ArrayMap<String, Boolean> hasMoreThan24HoursOfHistory = new ArrayMap<>(); + final UsageEvents.Event event = new UsageEvents.Event(); + boolean changedTimes = false; + final LaunchTimeAlarmQueue alarmQueue = getOrCreateLaunchTimeAlarmQueue(userId); + for (boolean unprocessedEvent = events.getNextEvent(event); unprocessedEvent; + unprocessedEvent = events.getNextEvent(event)) { + final String packageName = event.getPackageName(); + if (!hasMoreThan24HoursOfHistory.containsKey(packageName)) { + boolean hasHistory = now - event.getTimeStamp() > ONE_DAY; + if (DEBUG) { + Slog.d(TAG, + userId + ":" + packageName + " history > 24 hours=" + hasHistory); } - if (event.getEventType() == Event.ACTIVITY_RESUMED) { - long estimatedLaunchTime = - mAppStandby.getEstimatedLaunchTime(packageName, userId); - if (estimatedLaunchTime < now || estimatedLaunchTime == Long.MAX_VALUE) { - //noinspection ConstantConditions - estimatedLaunchTime = calculateNextLaunchTime( - hasMoreThan24HoursOfHistory.get(packageName), event.getTimeStamp()); - mAppStandby.setEstimatedLaunchTime( - packageName, userId, estimatedLaunchTime); - } - if (estimatedLaunchTime < now + ONE_WEEK) { - // Before a user is unlocked, we don't know when the app will be launched, - // so we give callers the UNKNOWN time. Now that we have a better estimate, - // we should notify them of the change. - if (DEBUG) { - Slog.d(TAG, "User " + userId + " unlock resulting in" - + " estimated launch time change for " + packageName); - } - changedTimes |= stageChangedEstimatedLaunchTime(userId, packageName); + hasMoreThan24HoursOfHistory.put(packageName, hasHistory); + } + if (event.getEventType() == Event.ACTIVITY_RESUMED) { + long estimatedLaunchTime = + mAppStandby.getEstimatedLaunchTime(packageName, userId); + if (estimatedLaunchTime < now || estimatedLaunchTime == Long.MAX_VALUE) { + //noinspection ConstantConditions + estimatedLaunchTime = calculateNextLaunchTime( + hasMoreThan24HoursOfHistory.get(packageName), event.getTimeStamp()); + mAppStandby.setEstimatedLaunchTime( + packageName, userId, estimatedLaunchTime); + } + if (estimatedLaunchTime < now + ONE_WEEK) { + // Before a user is unlocked, we don't know when the app will be launched, + // so we give callers the UNKNOWN time. Now that we have a better estimate, + // we should notify them of the change. + if (DEBUG) { + Slog.d(TAG, "User " + userId + " unlock resulting in" + + " estimated launch time change for " + packageName); } - alarmQueue.addAlarm(packageName, nowElapsed + (estimatedLaunchTime - now)); + changedTimes |= stageChangedEstimatedLaunchTime(userId, packageName); } - } - if (changedTimes) { - mHandler.sendEmptyMessage(MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED); + alarmQueue.addAlarm(packageName, nowElapsed + (estimatedLaunchTime - now)); } } + if (changedTimes) { + mHandler.sendEmptyMessage(MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED); + } } private void setEstimatedLaunchTime(int userId, String packageName, @@ -1897,16 +1945,19 @@ public class UsageStatsService extends SystemService implements idpw.println(); } } else { - final LinkedList<Event> pendingEvents = mReportedEvents.get(userId); - if (pendingEvents != null && !pendingEvents.isEmpty()) { - final int eventCount = pendingEvents.size(); - idpw.println("Pending events: count=" + eventCount); - idpw.increaseIndent(); - for (int idx = 0; idx < eventCount; idx++) { - UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), true); + synchronized (mReportedEvents) { + final LinkedList<Event> pendingEvents = mReportedEvents.get(userId); + if (pendingEvents != null && !pendingEvents.isEmpty()) { + final int eventCount = pendingEvents.size(); + idpw.println("Pending events: count=" + eventCount); + idpw.increaseIndent(); + for (int idx = 0; idx < eventCount; idx++) { + UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), + true); + } + idpw.decreaseIndent(); + idpw.println(); } - idpw.decreaseIndent(); - idpw.println(); } } idpw.decreaseIndent(); @@ -1989,37 +2040,11 @@ public class UsageStatsService extends SystemService implements case MSG_PACKAGE_REMOVED: onPackageRemoved(msg.arg1, (String) msg.obj); break; - case MSG_UID_STATE_CHANGED: { - final int uid = msg.arg1; - final int procState = msg.arg2; - - final int newCounter = (procState <= ActivityManager.PROCESS_STATE_TOP) ? 0 : 1; - synchronized (mUidToKernelCounter) { - final int oldCounter = mUidToKernelCounter.get(uid, 0); - if (newCounter != oldCounter) { - mUidToKernelCounter.put(uid, newCounter); - try { - FileUtils.stringToFile(KERNEL_COUNTER_FILE, uid + " " + newCounter); - } catch (IOException e) { - Slog.w(TAG, "Failed to update counter set: " + e); - } - } - } - break; - } case MSG_ON_START: synchronized (mLock) { loadGlobalComponentUsageLocked(); } break; - case MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK: { - final int userId = msg.arg1; - Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, - "usageStatsHandleEstimatedLaunchTimesOnUser(" + userId + ")"); - handleEstimatedLaunchTimesOnUserUnlock(userId); - Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - } - break; case MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED: { removeMessages(MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 7d9b3790c1b5..def52a517913 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -942,6 +942,7 @@ public final class Call { * @return the Telecom identifier associated with this {@link Call} . This is not a stable * identifier and is not guaranteed to be unique across device reboots. */ + @FlaggedApi(Flags.FLAG_CALL_DETAILS_ID_CHANGES) public @NonNull String getId() { return mTelecomCallId; } /** {@hide} */ @@ -1894,7 +1895,7 @@ public final class Call { * Tones are both played locally for the user to hear and sent to the network to be relayed * to the remote device. * <p> - * You must ensure that any call to {@link #playDtmfTone(char}) is followed by a matching + * You must ensure that any call to {@link #playDtmfTone(char)} is followed by a matching * call to {@link #stopDtmfTone()} and that each tone is stopped before a new one is started. * The play and stop commands are relayed to the underlying * {@link android.telecom.ConnectionService} as executed; implementations may not correctly diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java index 50f2ad4561cc..24d39182add6 100644 --- a/telecomm/java/android/telecom/CallControl.java +++ b/telecomm/java/android/telecom/CallControl.java @@ -225,7 +225,7 @@ public final class CallControl { /** * Request start a call streaming session. On receiving valid request, telecom will bind to - * the {@link CallStreamingService} implemented by a general call streaming sender. So that the + * the {@code CallStreamingService} implemented by a general call streaming sender. So that the * call streaming sender can perform streaming local device audio to another remote device and * control the call during streaming. * diff --git a/telecomm/java/android/telecom/CallControlCallback.java b/telecomm/java/android/telecom/CallControlCallback.java index eac2e64aa2ab..0166022abeb8 100644 --- a/telecomm/java/android/telecom/CallControlCallback.java +++ b/telecomm/java/android/telecom/CallControlCallback.java @@ -69,7 +69,7 @@ public interface CallControlCallback { /** * Telecom is informing the client to answer an incoming call and set it to active. * - * @param videoState see {@link android.telecom.CallAttributes.CallType} for valid states + * @param videoState the video state * @param wasCompleted The {@link Consumer} to be completed. If the client can answer the call * on their end, {@link Consumer#accept(Object)} should be called with * {@link Boolean#TRUE}. diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java index d9f89d544f40..c83804fde8fe 100644 --- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java +++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java @@ -68,7 +68,7 @@ public final class PhoneAccountSuggestion implements Parcelable { /** * Creates a new instance of {@link PhoneAccountSuggestion}. This constructor is intended for - * use by apps implementing a {@link PhoneAccountSuggestionService}, and generally should not be + * use by apps implementing a {@code PhoneAccountSuggestionService}, and generally should not be * used by dialer apps other than for testing purposes. * * @param handle The {@link PhoneAccountHandle} for this suggestion. diff --git a/telecomm/java/android/telecom/StreamingCall.java b/telecomm/java/android/telecom/StreamingCall.java index 29f436d9f459..ad1b6f9e7665 100644 --- a/telecomm/java/android/telecom/StreamingCall.java +++ b/telecomm/java/android/telecom/StreamingCall.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -25,6 +26,8 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import com.android.server.telecom.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -57,6 +60,7 @@ public final class StreamingCall implements Parcelable { /** * The ID associated with this call. This is the same value as {@link CallControl#getCallId()}. */ + @FlaggedApi(Flags.FLAG_CALL_DETAILS_ID_CHANGES) public static final String EXTRA_CALL_ID = "android.telecom.extra.CALL_ID"; /** diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a5e0638fec95..63e91ad414de 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -519,8 +519,6 @@ public class CarrierConfigManager { /** * Used in the Preferred Network Types menu to determine if the 2G option is displayed. * Value defaults to false as of Android T to discourage the use of insecure 2G protocols. - * - * @see #KEY_HIDE_ENABLE_2G */ public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; @@ -5075,7 +5073,6 @@ public class CarrierConfigManager { * MMTEL and RCS. * <p> * The default value for this configuration is {@code false}. - * @see android.telephony.ims.SipDelegateManager */ public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = KEY_PREFIX + "ims_single_registration_required_bool"; @@ -5650,8 +5647,8 @@ public class CarrierConfigManager { * <li>{@link #KEY_CAPABILITY_TYPE_SMS_INT_ARRAY}</li> * <li>{@link #KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY}</li> * </ul> - * <p> The values are defined in - * {@link ImsRegistrationImplBase.ImsRegistrationTech} + * <p> The values are defined as {@code REGISTRATION_TECH_*} constants in + * {@link android.telephony.ims.stub.ImsRegistrationImplBase}. * * changing mmtel_requires_provisioning_bundle requires changes to * carrier_volte_provisioning_required_bool and vice versa @@ -5663,12 +5660,12 @@ public class CarrierConfigManager { /** * List of different RAT technologies on which Provisioning for Voice calling (IR.92) * is supported. - * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE * <p>Possible values are, - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR} + * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE */ public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY = KEY_PREFIX + "capability_type_voice_int_array"; @@ -5676,12 +5673,12 @@ public class CarrierConfigManager { /** * List of different RAT technologies on which Provisioning for Video Telephony (IR.94) * is supported. - * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO * <p>Possible values are, - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR} + * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO */ public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY = KEY_PREFIX + "capability_type_video_int_array"; @@ -5689,24 +5686,24 @@ public class CarrierConfigManager { /** * List of different RAT technologies on which Provisioning for XCAP over Ut for * supplementary services. (IR.92) is supported. - * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT * <p>Possible values are, - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR} + * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT */ public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY = KEY_PREFIX + "capability_type_ut_int_array"; /** * List of different RAT technologies on which Provisioning for SMS (IR.92) is supported. - * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS * <p>Possible values are, - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR} + * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS */ public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY = KEY_PREFIX + "capability_type_sms_int_array"; @@ -5714,12 +5711,12 @@ public class CarrierConfigManager { /** * List of different RAT technologies on which Provisioning for Call Composer * (section 2.4 of RCC.20) is supported. - * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER * <p>Possible values are, - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR} + * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER */ public static final String KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY = KEY_PREFIX + "capability_type_call_composer_int_array"; @@ -5734,8 +5731,8 @@ public class CarrierConfigManager { * <li>{@link #KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY}</li> * <li>{@link #KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY}</li> * </ul> - * <p> The values are defined in - * {@link ImsRegistrationImplBase.ImsRegistrationTech} + * <p> The values are defined as {@code REGISTRATION_TECH_*} constants in + * {@link android.telephony.ims.stub.ImsRegistrationImplBase}. */ public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE = KEY_PREFIX + "rcs_requires_provisioning_bundle"; @@ -5744,12 +5741,11 @@ public class CarrierConfigManager { * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS. * If not set, this RcsFeature should not service capability requests. - * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE * <p>Possible values are, - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR} */ public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY = KEY_PREFIX + "capability_type_options_uce_int_array"; @@ -5759,12 +5755,11 @@ public class CarrierConfigManager { * framework. If set, the RcsFeature should support capability exchange using a presence * server. If not set, this RcsFeature should not publish capabilities or service capability * requests using presence. - * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE * <p>Possible values are, - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM} - * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR} */ public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY = KEY_PREFIX + "capability_type_presence_uce_int_array"; @@ -9541,9 +9536,9 @@ public class CarrierConfigManager { * Used to trade privacy/security against potentially reduced carrier coverage for some * carriers. * - * @deprecated Future versions of Android will disallow carriers from hiding this toggle - * because disabling 2g is a security feature that users should always have access to at - * their discretion. + * @removed This config option is no longer supported as it was hiding a security feature + * from users. Setting this option will not change the behavior of the Settings menu starting + * in Android V. */ @Deprecated public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool"; @@ -9642,7 +9637,7 @@ public class CarrierConfigManager { /** * A list of premium capabilities the carrier supports. Applications can prompt users to * purchase these premium capabilities from their carrier for a performance boost. - * Valid values are any of {@link TelephonyManager.PremiumCapability}. + * Valid values are any of {@link TelephonyManager}'s {@code PREMIUM_CAPABILITY_*} constants. * * This is empty by default, indicating that no premium capabilities are supported. * diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index 631013fc485e..cb4a6e7479cf 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -625,7 +625,7 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** - * @return The access network technology {@link NetworkType}. + * @return The access network technology network type.. */ public @NetworkType int getAccessNetworkTechnology() { return mAccessNetworkTechnology; diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index de2e9527e7b8..6ebf3bea7b6d 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -259,7 +259,7 @@ public class SubscriptionInfo implements Parcelable { /** * Whether this subscription is used for communicating with non-terrestrial networks. */ - private final boolean mIsNtn; + private final boolean mIsOnlyNonTerrestrialNetwork; /** * @hide @@ -385,7 +385,7 @@ public class SubscriptionInfo implements Parcelable { this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled; this.mPortIndex = portIndex; this.mUsageSetting = usageSetting; - this.mIsNtn = false; + this.mIsOnlyNonTerrestrialNetwork = false; } /** @@ -424,7 +424,7 @@ public class SubscriptionInfo implements Parcelable { this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled; this.mPortIndex = builder.mPortIndex; this.mUsageSetting = builder.mUsageSetting; - this.mIsNtn = builder.mIsNtn; + this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork; } /** @@ -878,8 +878,8 @@ public class SubscriptionInfo implements Parcelable { * otherwise. */ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public boolean isNtn() { - return mIsNtn; + public boolean isOnlyNonTerrestrialNetwork() { + return mIsOnlyNonTerrestrialNetwork; } @NonNull @@ -918,7 +918,7 @@ public class SubscriptionInfo implements Parcelable { UiccAccessRule.CREATOR)) .setUiccApplicationsEnabled(source.readBoolean()) .setUsageSetting(source.readInt()) - .setNtn(source.readBoolean()) + .setOnlyNonTerrestrialNetwork(source.readBoolean()) .build(); } @@ -960,7 +960,7 @@ public class SubscriptionInfo implements Parcelable { dest.writeTypedArray(mCarrierConfigAccessRules, flags); dest.writeBoolean(mAreUiccApplicationsEnabled); dest.writeInt(mUsageSetting); - dest.writeBoolean(mIsNtn); + dest.writeBoolean(mIsOnlyNonTerrestrialNetwork); } @Override @@ -1023,7 +1023,7 @@ public class SubscriptionInfo implements Parcelable { + " mType=" + SubscriptionManager.subscriptionTypeToString(mType) + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting) - + " ntn=" + mIsNtn + + " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork + "]"; } @@ -1049,7 +1049,7 @@ public class SubscriptionInfo implements Parcelable { that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules, that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid) && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner) - && mIsNtn == that.mIsNtn; + && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork; } @Override @@ -1058,7 +1058,7 @@ public class SubscriptionInfo implements Parcelable { mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded, mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass, mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId, - mIsGroupDisabled, mIsNtn); + mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork); result = 31 * result + Arrays.hashCode(mEhplmns); result = 31 * result + Arrays.hashCode(mHplmns); result = 31 * result + Arrays.hashCode(mNativeAccessRules); @@ -1260,7 +1260,7 @@ public class SubscriptionInfo implements Parcelable { /** * {@code true} if it is a non-terrestrial network subscription, {@code false} otherwise. */ - private boolean mIsNtn = false; + private boolean mIsOnlyNonTerrestrialNetwork = false; /** * Default constructor. @@ -1304,7 +1304,7 @@ public class SubscriptionInfo implements Parcelable { mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled; mPortIndex = info.mPortIndex; mUsageSetting = info.mUsageSetting; - mIsNtn = info.mIsNtn; + mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork; } /** @@ -1692,12 +1692,13 @@ public class SubscriptionInfo implements Parcelable { /** * Set whether the subscription is exclusively used for non-terrestrial networks or not. * - * @param isNtn {@code true} if the subscription is for NTN, {@code false} otherwise. + * @param isOnlyNonTerrestrialNetwork {@code true} if the subscription is for NTN, + * {@code false} otherwise. * @return The builder. */ @NonNull - public Builder setNtn(boolean isNtn) { - mIsNtn = isNtn; + public Builder setOnlyNonTerrestrialNetwork(boolean isOnlyNonTerrestrialNetwork) { + mIsOnlyNonTerrestrialNetwork = isOnlyNonTerrestrialNetwork; return this; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index fa5fd875a024..8e90fe7ea975 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1364,7 +1364,7 @@ public class SubscriptionManager { public static class OnSubscriptionsChangedListener { /** - * After {@link Build.VERSION_CODES.Q}, it is no longer necessary to instantiate a + * After {@link Build.VERSION_CODES#Q}, it is no longer necessary to instantiate a * Handler inside of the OnSubscriptionsChangedListener in all cases, so it will only * be done for callers that do not supply an Executor. */ @@ -1388,13 +1388,14 @@ public class SubscriptionManager { /** * Create an OnSubscriptionsChangedListener. * - * For callers targeting {@link Build.VERSION_CODES.P} or earlier, this can only be called + * For callers targeting {@link Build.VERSION_CODES#P} or earlier, this can only be called * on a thread that already has a prepared Looper. Callers targeting Q or later should * subsequently use {@link SubscriptionManager#addOnSubscriptionsChangedListener( * Executor, OnSubscriptionsChangedListener)}. * - * On OS versions prior to {@link Build.VERSION_CODES.V} callers should assume that this - * call will fail if invoked on a thread that does not already have a prepared looper. + * On OS versions prior to {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} callers should + * assume that this call will fail if invoked on a thread that does not already have a + * prepared looper. */ public OnSubscriptionsChangedListener() { mCreatorLooper = Looper.myLooper(); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 234ca918a144..548fa97db599 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -13132,8 +13132,8 @@ public class TelephonyManager { * </ul> * * @param executor The executor on which the result listener will be called. - * @param resultListener {@link Consumer} that will be called with the result fetched - * from the radio of type {@link CarrierRestrictionStatus} + * @param resultListener {@link Consumer} that will be called with the carrier restriction + * status result fetched from the radio * @throws SecurityException if the caller does not have the required permission/privileges or * if the caller is not pre-registered. */ @@ -13465,7 +13465,7 @@ public class TelephonyManager { public static final int DATA_ENABLED_REASON_THERMAL = 3; /** - * To indicate data was enabled or disabled due to {@link MobileDataPolicy} overrides. + * To indicate data was enabled or disabled due to mobile data policy overrides. * Note that this is not a valid reason for {@link #setDataEnabledForReason(int, boolean)} and * is only used to indicate that data enabled was changed due to an override. */ @@ -14571,7 +14571,7 @@ public class TelephonyManager { * @param needValidation whether validation is needed before switch happens. * @param executor The executor of where the callback will execute. * @param callback Callback will be triggered once it succeeds or failed. - * See {@link TelephonyManager.SetOpportunisticSubscriptionResult} + * See the {@code SET_OPPORTUNISTIC_SUB_*} constants * for more details. Pass null if don't care about the result. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) @@ -17516,7 +17516,7 @@ public class TelephonyManager { public @interface PurchasePremiumCapabilityResult {} /** - * Returns the purchase result {@link PurchasePremiumCapabilityResult} as a String. + * Returns the purchase result as a String. * * @param result The purchase premium capability result. * @return The purchase result as a String. @@ -17570,7 +17570,6 @@ public class TelephonyManager { * @param capability The premium capability to purchase. * @param executor The callback executor for the response. * @param callback The result of the purchase request. - * One of {@link PurchasePremiumCapabilityResult}. * @throws SecurityException if the caller does not hold permissions * READ_BASIC_PHONE_STATE or INTERNET. * @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid. diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 26c17a461cf2..e9af486219f7 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -532,7 +532,7 @@ public class ApnSetting implements Parcelable { /** * Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought * up by this APN setting. Note this value will only be used when MTU size is not provided - * in {@link DataCallResponse#getMtuV4()} during network bring up. + * in {@code DataCallResponse#getMtuV4()} during network bring up. * * @return the MTU size in bytes of the route. */ @@ -542,7 +542,7 @@ public class ApnSetting implements Parcelable { /** * Returns the MTU size of the IPv6 mobile interface to which the APN connected. Note this value - * will only be used when MTU size is not provided in {@link DataCallResponse#getMtuV6()} + * will only be used when MTU size is not provided in {@code DataCallResponse#getMtuV6()} * during network bring up. * * @return the MTU size in bytes of the route. @@ -1787,7 +1787,7 @@ public class ApnSetting implements Parcelable { /** * Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought * up by this APN setting. Note this value will only be used when MTU size is not provided - * in {@link DataCallResponse#getMtuV4()} during network bring up. + * in {@code DataCallResponse#getMtuV4()} during network bring up. * * @param mtuV4 the MTU size in bytes of the route. */ @@ -1799,7 +1799,7 @@ public class ApnSetting implements Parcelable { /** * Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv6 routes brought * up by this APN setting. Note this value will only be used when MTU size is not provided - * in {@link DataCallResponse#getMtuV6()} during network bring up. + * in {@code DataCallResponse#getMtuV6()} during network bring up. * * @param mtuV6 the MTU size in bytes of the route. */ diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index ed4627631dd5..b9a7d439114a 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -249,7 +249,7 @@ public class EuiccManager { * * <p>{@link #EXTRA_USE_QR_SCANNER} not set or set to false: The LPA should try to get an * activation code from the carrier app by binding to the carrier app service implementing - * {@link android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE}. + * {@code android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE}. * <p>{@link #EXTRA_USE_QR_SCANNER} set to true: The LPA should launch a QR scanner for the user * to scan an eSIM profile QR code. * diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index caee4e2ca277..2172d7de0dd7 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -561,7 +561,7 @@ public class ImsMmTelManager implements RegistrationManager { * @param c The MmTel {@link CapabilityCallback} to be registered. * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) * @throws ImsException if the subscription associated with this callback is valid, but - * the {@link ImsService} associated with the subscription is not available. This can happen if + * the {@code ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. */ diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 4439e5c35d83..2b49bcd4e928 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -247,7 +247,7 @@ public class ImsRcsManager { * @param c The {@link RegistrationManager.RegistrationCallback} to be added. * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback) * @throws ImsException if the subscription associated with this callback is valid, but - * the {@link ImsService} associated with the subscription is not available. This can happen if + * the {@code ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. */ diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 37a6a7ebf953..1c5d1e940030 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -1469,9 +1469,8 @@ public class ProvisioningManager { /** * Get the provisioning status for the IMS MmTel capability specified. * - * If provisioning is not required for the queried - * {@link MmTelFeature.MmTelCapabilities.MmTelCapability} and - * {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will + * If provisioning is not required for the queried {@code capability} and + * {@code tech} combination specified, this method will * always return {@code true}. * * <p> Requires Permission: @@ -1503,7 +1502,7 @@ public class ProvisioningManager { * Get the provisioning status for the IMS RCS capability specified. * * If provisioning is not required for the queried - * {@link ImsRcsManager.RcsImsCapabilityFlag} or if the device does not support IMS + * {@code capability} or if the device does not support IMS * this method will always return {@code true}. * * @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL @@ -1533,7 +1532,7 @@ public class ProvisioningManager { * Get the provisioning status for the IMS RCS capability specified. * * If provisioning is not required for the queried - * {@link ImsRcsManager.RcsImsCapabilityFlag} or if the device does not support IMS + * {@code capability} or if the device does not support IMS * this method will always return {@code true}. * * <p> Requires Permission: diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index 873ce6064cca..b528866371cb 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -31,7 +31,6 @@ import android.os.Bundle; import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ims.aidl.IImsRegistrationCallback; -import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; @@ -42,7 +41,7 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * Manages IMS Service registration state for associated {@link ImsFeature}s. + * Manages IMS Service registration state for associated {@code ImsFeature}s. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) public interface RegistrationManager { @@ -394,7 +393,7 @@ public interface RegistrationManager { * @param c The {@link RegistrationCallback} to be added. * @see #unregisterImsRegistrationCallback(RegistrationCallback) * @throws ImsException if the subscription associated with this callback is valid, but - * the {@link ImsService} associated with the subscription is not available. This can happen if + * the {@code ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. */ diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 8dc2de8178c4..c61f80152144 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -1628,7 +1628,7 @@ public final class SatelliteManager { * @param subId The subscription ID of the carrier. * @param enableSatellite {@code true} to enable the satellite and {@code false} to disable. * @param executor The executor on which the error code listener will be called. - * @param resultListener Listener for the {@link SatelliteError} result of the operation. + * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. @@ -1662,7 +1662,7 @@ public final class SatelliteManager { * will return a {@code boolean} with value {@code true} if the satellite * is enabled and {@code false} otherwise. * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} - * will return a {@link SatelliteException} with the {@link SatelliteError}. + * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. @@ -1688,7 +1688,7 @@ public final class SatelliteManager { * @param subId The subscription ID of the carrier. * @param reason Reason for disallowing satellite communication. * @param executor The executor on which the error code listener will be called. - * @param resultListener Listener for the {@link SatelliteError} result of the operation. + * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. @@ -1731,7 +1731,7 @@ public final class SatelliteManager { * @param subId The subscription ID of the carrier. * @param reason Reason for disallowing satellite communication. * @param executor The executor on which the error code listener will be called. - * @param resultListener Listener for the {@link SatelliteError} result of the operation. + * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl index 0fcd0d622d36..7fda550c599c 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl @@ -32,15 +32,15 @@ oneway interface ISatellite { * * @param listener The callback interface to handle satellite service indications. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void setSatelliteListener(in ISatelliteListener listener); @@ -53,15 +53,15 @@ oneway interface ISatellite { * disabling listening mode. * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestSatelliteListeningEnabled(in boolean enable, in int timeout, in IIntegerConsumer resultCallback); @@ -84,15 +84,15 @@ oneway interface ISatellite { * @param enableDemoMode True to enable demo mode and false to disable. * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestSatelliteEnabled(in boolean enableSatellite, in boolean enableDemoMode, in IIntegerConsumer resultCallback); @@ -101,39 +101,42 @@ oneway interface ISatellite { * Request to get whether the satellite modem is enabled. * * @param resultCallback The callback to receive the error code result of the operation. - * This must only be sent when the error is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether the satellite modem is enabled. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether the satellite modem is enabled. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - void requestIsSatelliteEnabled(in IIntegerConsumer resultCallback, in IBooleanConsumer callback); + void requestIsSatelliteEnabled(in IIntegerConsumer resultCallback, + in IBooleanConsumer callback); /** * Request to get whether the satellite service is supported on the device. * * @param resultCallback The callback to receive the error code result of the operation. - * This must only be sent when the error is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether the satellite service is supported on the device. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether the satellite service is supported on the device. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestIsSatelliteSupported(in IIntegerConsumer resultCallback, in IBooleanConsumer callback); @@ -142,19 +145,20 @@ oneway interface ISatellite { * Request to get the SatelliteCapabilities of the satellite service. * * @param resultCallback The callback to receive the error code result of the operation. - * This must only be sent when the error is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * the SatelliteCapabilities of the satellite service. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive the SatelliteCapabilities of the satellite service. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestSatelliteCapabilities(in IIntegerConsumer resultCallback, in ISatelliteCapabilitiesConsumer callback); @@ -166,15 +170,15 @@ oneway interface ISatellite { * * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void startSendingSatellitePointingInfo(in IIntegerConsumer resultCallback); @@ -184,15 +188,15 @@ oneway interface ISatellite { * * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void stopSendingSatellitePointingInfo(in IIntegerConsumer resultCallback); @@ -206,18 +210,18 @@ oneway interface ISatellite { * @param provisionData Data from the provisioning app that can be used by provisioning server * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:REQUEST_ABORTED - * SatelliteError:NETWORK_TIMEOUT + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT */ void provisionSatelliteService(in String token, in byte[] provisionData, in IIntegerConsumer resultCallback); @@ -230,18 +234,18 @@ oneway interface ISatellite { * @param token The token of the device/subscription to be deprovisioned. * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:REQUEST_ABORTED - * SatelliteError:NETWORK_TIMEOUT + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT */ void deprovisionSatelliteService(in String token, in IIntegerConsumer resultCallback); @@ -249,19 +253,20 @@ oneway interface ISatellite { * Request to get whether this device is provisioned with a satellite provider. * * @param resultCallback The callback to receive the error code result of the operation. - * This must only be sent when the error is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether this device is provisioned with a satellite provider. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether this device is provisioned with a satellite provider. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestIsSatelliteProvisioned(in IIntegerConsumer resultCallback, in IBooleanConsumer callback); @@ -273,20 +278,20 @@ oneway interface ISatellite { * * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:SATELLITE_ACCESS_BARRED - * SatelliteError:NETWORK_TIMEOUT - * SatelliteError:SATELLITE_NOT_REACHABLE - * SatelliteError:NOT_AUTHORIZED + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT + * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE + * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED */ void pollPendingSatelliteDatagrams(in IIntegerConsumer resultCallback); @@ -297,21 +302,21 @@ oneway interface ISatellite { * @param isEmergency Whether this is an emergency datagram. * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:REQUEST_ABORTED - * SatelliteError:SATELLITE_ACCESS_BARRED - * SatelliteError:NETWORK_TIMEOUT - * SatelliteError:SATELLITE_NOT_REACHABLE - * SatelliteError:NOT_AUTHORIZED + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED + * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT + * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE + * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED */ void sendSatelliteDatagram(in SatelliteDatagram datagram, in boolean isEmergency, in IIntegerConsumer resultCallback); @@ -322,19 +327,20 @@ oneway interface ISatellite { * ISatelliteListener#onSatelliteModemStateChanged. * * @param resultCallback The callback to receive the error code result of the operation. - * This must only be sent when the error is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * the current satellite modem state. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive the current satellite modem state. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestSatelliteModemState(in IIntegerConsumer resultCallback, in IIntegerConsumer callback); @@ -343,19 +349,20 @@ oneway interface ISatellite { * Request to get whether satellite communication is allowed for the current location. * * @param resultCallback The callback to receive the error code result of the operation. - * This must only be sent when the error is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether satellite communication is allowed for the current location. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether satellite communication is allowed for the current location. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestIsSatelliteCommunicationAllowedForCurrentLocation( in IIntegerConsumer resultCallback, in IBooleanConsumer callback); @@ -366,19 +373,20 @@ oneway interface ISatellite { * This will return 0 if the satellite is currently visible. * * @param resultCallback The callback to receive the error code result of the operation. - * This must only be sent when the error is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * the time after which the satellite will be visible. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive the time after which the satellite will be visible. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ void requestTimeForNextSatelliteVisibility(in IIntegerConsumer resultCallback, in IIntegerConsumer callback); @@ -399,14 +407,14 @@ oneway interface ISatellite { * attach to them. * @param resultCallback The callback to receive the error code result of the operation. * - * Valid error codes returned: - * SatelliteError:NONE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:MODEM_ERR - * SatelliteError:NO_RESOURCES - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED + * Valid result codes returned: + * SatelliteResult:NONE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:MODEM_ERR + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED */ void setSatellitePlmn(int simSlot, in List<String> carrierPlmnList, in List<String> allSatellitePlmnList, in IIntegerConsumer resultCallback); @@ -420,12 +428,12 @@ oneway interface ISatellite { * @param serial Serial number of request. * @param enable {@code true} to enable satellite, {@code false} to disable satellite. * - * Valid errors returned: - * SatelliteError:NONE - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:MODEM_ERR - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED */ void setSatelliteEnabledForCarrier(int simSlot, boolean satelliteEnabled, in IIntegerConsumer callback); @@ -437,12 +445,12 @@ oneway interface ISatellite { * this information to determine the relevant carrier. * @param serial Serial number of request. * - * Valid errors returned: - * SatelliteError:NONE - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:MODEM_ERR - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED */ void requestIsSatelliteEnabledForCarrier(int simSlot, in IIntegerConsumer resultCallback, in IBooleanConsumer callback); diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java index a9c09c94d57c..4cee01e8bd39 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java +++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java @@ -72,172 +72,172 @@ public class SatelliteImplBase extends SatelliteService { @Override public void requestSatelliteListeningEnabled(boolean enable, int timeout, - IIntegerConsumer errorCallback) throws RemoteException { + IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestSatelliteListeningEnabled(enable, timeout, errorCallback), + .requestSatelliteListeningEnabled(enable, timeout, resultCallback), "requestSatelliteListeningEnabled"); } @Override public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled, - IIntegerConsumer errorCallback) throws RemoteException { + IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .enableCellularModemWhileSatelliteModeIsOn(enabled, errorCallback), + .enableCellularModemWhileSatelliteModeIsOn(enabled, resultCallback), "enableCellularModemWhileSatelliteModeIsOn"); } @Override public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, - IIntegerConsumer errorCallback) throws RemoteException { + IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this .requestSatelliteEnabled( - enableSatellite, enableDemoMode, errorCallback), + enableSatellite, enableDemoMode, resultCallback), "requestSatelliteEnabled"); } @Override - public void requestIsSatelliteEnabled(IIntegerConsumer errorCallback, + public void requestIsSatelliteEnabled(IIntegerConsumer resultCallback, IBooleanConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestIsSatelliteEnabled(errorCallback, callback), + .requestIsSatelliteEnabled(resultCallback, callback), "requestIsSatelliteEnabled"); } @Override - public void requestIsSatelliteSupported(IIntegerConsumer errorCallback, + public void requestIsSatelliteSupported(IIntegerConsumer resultCallback, IBooleanConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestIsSatelliteSupported(errorCallback, callback), + .requestIsSatelliteSupported(resultCallback, callback), "requestIsSatelliteSupported"); } @Override - public void requestSatelliteCapabilities(IIntegerConsumer errorCallback, + public void requestSatelliteCapabilities(IIntegerConsumer resultCallback, ISatelliteCapabilitiesConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestSatelliteCapabilities(errorCallback, callback), + .requestSatelliteCapabilities(resultCallback, callback), "requestSatelliteCapabilities"); } @Override - public void startSendingSatellitePointingInfo(IIntegerConsumer errorCallback) + public void startSendingSatellitePointingInfo(IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( - () -> SatelliteImplBase.this.startSendingSatellitePointingInfo(errorCallback), + () -> SatelliteImplBase.this.startSendingSatellitePointingInfo(resultCallback), "startSendingSatellitePointingInfo"); } @Override - public void stopSendingSatellitePointingInfo(IIntegerConsumer errorCallback) + public void stopSendingSatellitePointingInfo(IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( - () -> SatelliteImplBase.this.stopSendingSatellitePointingInfo(errorCallback), + () -> SatelliteImplBase.this.stopSendingSatellitePointingInfo(resultCallback), "stopSendingSatellitePointingInfo"); } @Override public void provisionSatelliteService(String token, byte[] provisionData, - IIntegerConsumer errorCallback) throws RemoteException { + IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .provisionSatelliteService(token, provisionData, errorCallback), + .provisionSatelliteService(token, provisionData, resultCallback), "provisionSatelliteService"); } @Override - public void deprovisionSatelliteService(String token, IIntegerConsumer errorCallback) + public void deprovisionSatelliteService(String token, IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( - () -> SatelliteImplBase.this.deprovisionSatelliteService(token, errorCallback), + () -> SatelliteImplBase.this.deprovisionSatelliteService(token, resultCallback), "deprovisionSatelliteService"); } @Override - public void requestIsSatelliteProvisioned(IIntegerConsumer errorCallback, + public void requestIsSatelliteProvisioned(IIntegerConsumer resultCallback, IBooleanConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestIsSatelliteProvisioned(errorCallback, callback), + .requestIsSatelliteProvisioned(resultCallback, callback), "requestIsSatelliteProvisioned"); } @Override - public void pollPendingSatelliteDatagrams(IIntegerConsumer errorCallback) + public void pollPendingSatelliteDatagrams(IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( - () -> SatelliteImplBase.this.pollPendingSatelliteDatagrams(errorCallback), + () -> SatelliteImplBase.this.pollPendingSatelliteDatagrams(resultCallback), "pollPendingSatelliteDatagrams"); } @Override public void sendSatelliteDatagram(SatelliteDatagram datagram, boolean isEmergency, - IIntegerConsumer errorCallback) throws RemoteException { + IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .sendSatelliteDatagram(datagram, isEmergency, errorCallback), + .sendSatelliteDatagram(datagram, isEmergency, resultCallback), "sendSatelliteDatagram"); } @Override - public void requestSatelliteModemState(IIntegerConsumer errorCallback, + public void requestSatelliteModemState(IIntegerConsumer resultCallback, IIntegerConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestSatelliteModemState(errorCallback, callback), + .requestSatelliteModemState(resultCallback, callback), "requestSatelliteModemState"); } @Override public void requestIsSatelliteCommunicationAllowedForCurrentLocation( - IIntegerConsumer errorCallback, IBooleanConsumer callback) + IIntegerConsumer resultCallback, IBooleanConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this .requestIsSatelliteCommunicationAllowedForCurrentLocation( - errorCallback, callback), + resultCallback, callback), "requestIsSatelliteCommunicationAllowedForCurrentLocation"); } @Override - public void requestTimeForNextSatelliteVisibility(IIntegerConsumer errorCallback, + public void requestTimeForNextSatelliteVisibility(IIntegerConsumer resultCallback, IIntegerConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestTimeForNextSatelliteVisibility(errorCallback, callback), + .requestTimeForNextSatelliteVisibility(resultCallback, callback), "requestTimeForNextSatelliteVisibility"); } @Override public void setSatellitePlmn(int simSlot, List<String> carrierPlmnList, - List<String> devicePlmnList, IIntegerConsumer errorCallback) + List<String> devicePlmnList, IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this.setSatellitePlmn( - simSlot, carrierPlmnList, devicePlmnList, errorCallback), + simSlot, carrierPlmnList, devicePlmnList, resultCallback), "setSatellitePlmn"); } @Override public void setSatelliteEnabledForCarrier(int simSlot, boolean enableSatellite, - IIntegerConsumer errorCallback) throws RemoteException { + IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( - () -> SatelliteImplBase.this - .setSatelliteEnabledForCarrier(simSlot, enableSatellite, errorCallback), + () -> SatelliteImplBase.this.setSatelliteEnabledForCarrier( + simSlot, enableSatellite, resultCallback), "setSatelliteEnabledForCarrier"); } @Override - public void requestIsSatelliteEnabledForCarrier(int simSlot, IIntegerConsumer errorCallback, - IBooleanConsumer callback) throws RemoteException { + public void requestIsSatelliteEnabledForCarrier(int simSlot, + IIntegerConsumer resultCallback, IBooleanConsumer callback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestIsSatelliteEnabledForCarrier(simSlot, errorCallback, callback), + .requestIsSatelliteEnabledForCarrier(simSlot, resultCallback, callback), "requestIsSatelliteEnabledForCarrier"); } @@ -260,15 +260,15 @@ public class SatelliteImplBase extends SatelliteService { * * @param listener The callback interface to handle satellite service indications. * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ public void setSatelliteListener(@NonNull ISatelliteListener listener) { // stub implementation @@ -281,20 +281,20 @@ public class SatelliteImplBase extends SatelliteService { * @param enable True to enable satellite listening mode and false to disable. * @param timeout How long the satellite modem should wait for the next incoming page before * disabling listening mode. - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ public void requestSatelliteListeningEnabled(boolean enable, int timeout, - @NonNull IIntegerConsumer errorCallback) { + @NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -302,10 +302,10 @@ public class SatelliteImplBase extends SatelliteService { * Allow cellular modem scanning while satellite mode is on. * @param enabled {@code true} to enable cellular modem while satellite mode is on * and {@code false} to disable - * @param errorCallback The callback to receive the error code result of the operation. + * @param resultCallback The callback to receive the error code result of the operation. */ public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled, - @NonNull IIntegerConsumer errorCallback) { + @NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -316,42 +316,43 @@ public class SatelliteImplBase extends SatelliteService { * * @param enableSatellite True to enable the satellite modem and false to disable. * @param enableDemoMode True to enable demo mode and false to disable. - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, - @NonNull IIntegerConsumer errorCallback) { + @NonNull IIntegerConsumer resultCallback) { // stub implementation } /** * Request to get whether the satellite modem is enabled. * - * @param errorCallback The callback to receive the error code result of the operation. - * This must only be sent when the result is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether the satellite modem is enabled. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether the satellite modem is enabled. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void requestIsSatelliteEnabled(@NonNull IIntegerConsumer errorCallback, + public void requestIsSatelliteEnabled(@NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) { // stub implementation } @@ -359,22 +360,23 @@ public class SatelliteImplBase extends SatelliteService { /** * Request to get whether the satellite service is supported on the device. * - * @param errorCallback The callback to receive the error code result of the operation. - * This must only be sent when the result is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether the satellite service is supported on the device. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether the satellite service is supported on the device. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void requestIsSatelliteSupported(@NonNull IIntegerConsumer errorCallback, + public void requestIsSatelliteSupported(@NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) { // stub implementation } @@ -382,22 +384,23 @@ public class SatelliteImplBase extends SatelliteService { /** * Request to get the SatelliteCapabilities of the satellite service. * - * @param errorCallback The callback to receive the error code result of the operation. - * This must only be sent when the result is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * the SatelliteCapabilities of the satellite service. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive the SatelliteCapabilities of the satellite service. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void requestSatelliteCapabilities(@NonNull IIntegerConsumer errorCallback, + public void requestSatelliteCapabilities(@NonNull IIntegerConsumer resultCallback, @NonNull ISatelliteCapabilitiesConsumer callback) { // stub implementation } @@ -407,19 +410,19 @@ public class SatelliteImplBase extends SatelliteService { * The satellite service should report the satellite pointing info via * ISatelliteListener#onSatellitePositionChanged as the user device/satellite moves. * - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void startSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) { + public void startSendingSatellitePointingInfo(@NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -427,19 +430,19 @@ public class SatelliteImplBase extends SatelliteService { * User stopped pointing to the satellite. * The satellite service should stop reporting satellite pointing info to the framework. * - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void stopSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) { + public void stopSendingSatellitePointingInfo(@NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -452,23 +455,23 @@ public class SatelliteImplBase extends SatelliteService { * gateway. * @param provisionData Data from the provisioning app that can be used by provisioning * server - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:REQUEST_ABORTED - * SatelliteError:NETWORK_TIMEOUT + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT */ public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData, - @NonNull IIntegerConsumer errorCallback) { + @NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -478,45 +481,46 @@ public class SatelliteImplBase extends SatelliteService { * Once deprovisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report false. * * @param token The token of the device/subscription to be deprovisioned. - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:REQUEST_ABORTED - * SatelliteError:NETWORK_TIMEOUT + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT */ public void deprovisionSatelliteService(@NonNull String token, - @NonNull IIntegerConsumer errorCallback) { + @NonNull IIntegerConsumer resultCallback) { // stub implementation } /** * Request to get whether this device is provisioned with a satellite provider. * - * @param errorCallback The callback to receive the error code result of the operation. - * This must only be sent when the result is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether this device is provisioned with a satellite provider. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether this device is provisioned with a satellite provider. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void requestIsSatelliteProvisioned(@NonNull IIntegerConsumer errorCallback, + public void requestIsSatelliteProvisioned(@NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) { // stub implementation } @@ -526,24 +530,24 @@ public class SatelliteImplBase extends SatelliteService { * The satellite service should check if there are any pending datagrams to be received over * satellite and report them via ISatelliteListener#onSatelliteDatagramsReceived. * - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:SATELLITE_ACCESS_BARRED - * SatelliteError:NETWORK_TIMEOUT - * SatelliteError:SATELLITE_NOT_REACHABLE - * SatelliteError:NOT_AUTHORIZED + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT + * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE + * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED */ - public void pollPendingSatelliteDatagrams(@NonNull IIntegerConsumer errorCallback) { + public void pollPendingSatelliteDatagrams(@NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -552,26 +556,26 @@ public class SatelliteImplBase extends SatelliteService { * * @param datagram Datagram to send in byte format. * @param isEmergency Whether this is an emergency datagram. - * @param errorCallback The callback to receive the error code result of the operation. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:NETWORK_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES - * SatelliteError:REQUEST_ABORTED - * SatelliteError:SATELLITE_ACCESS_BARRED - * SatelliteError:NETWORK_TIMEOUT - * SatelliteError:SATELLITE_NOT_REACHABLE - * SatelliteError:NOT_AUTHORIZED + * @param resultCallback The callback to receive the error code result of the operation. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED + * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED + * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT + * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE + * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED */ public void sendSatelliteDatagram(@NonNull SatelliteDatagram datagram, boolean isEmergency, - @NonNull IIntegerConsumer errorCallback) { + @NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -580,22 +584,23 @@ public class SatelliteImplBase extends SatelliteService { * The satellite service should report the current satellite modem state via * ISatelliteListener#onSatelliteModemStateChanged. * - * @param errorCallback The callback to receive the error code result of the operation. - * This must only be sent when the result is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * the current satellite modem state. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive the current satellite modem state. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void requestSatelliteModemState(@NonNull IIntegerConsumer errorCallback, + public void requestSatelliteModemState(@NonNull IIntegerConsumer resultCallback, @NonNull IIntegerConsumer callback) { // stub implementation } @@ -603,23 +608,24 @@ public class SatelliteImplBase extends SatelliteService { /** * Request to get whether satellite communication is allowed for the current location. * - * @param errorCallback The callback to receive the error code result of the operation. - * This must only be sent when the result is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * whether satellite communication is allowed for the current location. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive whether satellite communication is allowed for the current location. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ public void requestIsSatelliteCommunicationAllowedForCurrentLocation( - @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) { + @NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) { // stub implementation } @@ -628,22 +634,23 @@ public class SatelliteImplBase extends SatelliteService { * representing the duration in seconds after which the satellite will be visible. * This will return 0 if the satellite is currently visible. * - * @param errorCallback The callback to receive the error code result of the operation. - * This must only be sent when the result is not SatelliteError#ERROR_NONE. - * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive - * the time after which the satellite will be visible. - * - * Valid error codes returned: - * SatelliteError:ERROR_NONE - * SatelliteError:SERVICE_ERROR - * SatelliteError:MODEM_ERROR - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED - * SatelliteError:NO_RESOURCES + * @param resultCallback The callback to receive the error code result of the operation. + * This must only be sent when the result is not + * SatelliteResult#SATELLITE_RESULT_SUCCESS. + * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to + * receive the time after which the satellite will be visible. + * + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES */ - public void requestTimeForNextSatelliteVisibility(@NonNull IIntegerConsumer errorCallback, + public void requestTimeForNextSatelliteVisibility(@NonNull IIntegerConsumer resultCallback, @NonNull IIntegerConsumer callback) { // stub implementation } @@ -658,25 +665,25 @@ public class SatelliteImplBase extends SatelliteService { * * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be * applied. The modem will use this information to determine the relevant carrier. - * @param errorCallback The callback to receive the error code result of the operation. + * @param resultCallback The callback to receive the error code result of the operation. * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks * supported by user subscription. * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite * PLMNs that are not supported by the carrier and make sure not to * attach to them. * - * Valid error codes returned: - * SatelliteError:NONE - * SatelliteError:INVALID_ARGUMENTS - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:MODEM_ERR - * SatelliteError:NO_RESOURCES - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED + * Valid result codes returned: + * SatelliteResult:NONE + * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:MODEM_ERR + * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED */ public void setSatellitePlmn(@NonNull int simLogicalSlotIndex, @NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList, - @NonNull IIntegerConsumer errorCallback) { + @NonNull IIntegerConsumer resultCallback) { // stub implementation } @@ -689,12 +696,12 @@ public class SatelliteImplBase extends SatelliteService { * @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite. * @param callback {@code true} to enable satellite, {@code false} to disable satellite. * - * Valid errors returned: - * SatelliteError:NONE - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:MODEM_ERR - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED */ public void setSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex, @NonNull boolean satelliteEnabled, @NonNull IIntegerConsumer callback) { @@ -707,18 +714,18 @@ public class SatelliteImplBase extends SatelliteService { * * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be * applied. The modem will use this information to determine the relevant carrier. - * @param errorCallback The callback to receive the error code result of the operation. + * @param resultCallback The callback to receive the error code result of the operation. * @param callback {@code true} to satellite enabled, {@code false} to satellite disabled. * - * Valid errors returned: - * SatelliteError:NONE - * SatelliteError:INVALID_MODEM_STATE - * SatelliteError:MODEM_ERR - * SatelliteError:RADIO_NOT_AVAILABLE - * SatelliteError:REQUEST_NOT_SUPPORTED + * Valid result codes returned: + * SatelliteResult:SATELLITE_RESULT_SUCCESS + * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE + * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR + * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE + * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED */ public void requestIsSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex, - @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) { + @NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) { // stub implementation } } diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp index 5233a5b8654e..c2a70151fa13 100644 --- a/tests/BatteryStatsPerfTest/Android.bp +++ b/tests/BatteryStatsPerfTest/Android.bp @@ -27,7 +27,7 @@ android_test { static_libs: [ "androidx.test.rules", "apct-perftests-utils", - "truth-prebuilt", + "truth", ], platform_apis: true, certificate: "platform", diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp index 615990f22e3c..38cb9869f165 100644 --- a/tests/BinaryTransparencyHostTest/Android.bp +++ b/tests/BinaryTransparencyHostTest/Android.bp @@ -30,7 +30,7 @@ java_test_host { "compatibility-host-util", ], static_libs: [ - "truth-prebuilt", + "truth", ], data: [ ":BinaryTransparencyTestApp", diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp index c4faf7f4fb11..1fb73e2c0967 100644 --- a/tests/BlobStoreTestUtils/Android.bp +++ b/tests/BlobStoreTestUtils/Android.bp @@ -22,12 +22,12 @@ package { } java_library { - name: "BlobStoreTestUtils", - srcs: ["src/**/*.java"], - static_libs: [ - "truth-prebuilt", - "androidx.test.uiautomator_uiautomator", - "androidx.test.ext.junit", - ], - sdk_version: "test_current", + name: "BlobStoreTestUtils", + srcs: ["src/**/*.java"], + static_libs: [ + "truth", + "androidx.test.uiautomator_uiautomator", + "androidx.test.ext.junit", + ], + sdk_version: "test_current", } diff --git a/tests/ChoreographerTests/Android.bp b/tests/ChoreographerTests/Android.bp index ca3026705c63..5d49120ee702 100644 --- a/tests/ChoreographerTests/Android.bp +++ b/tests/ChoreographerTests/Android.bp @@ -34,7 +34,7 @@ android_test { "androidx.test.rules", "compatibility-device-util-axt", "com.google.android.material_material", - "truth-prebuilt", + "truth", ], jni_libs: [ "libchoreographertests_jni", diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp index 680952157fdc..96e4a9ea4300 100644 --- a/tests/CtsSurfaceControlTestsStaging/Android.bp +++ b/tests/CtsSurfaceControlTestsStaging/Android.bp @@ -37,7 +37,7 @@ android_test { "compatibility-device-util-axt", "com.google.android.material_material", "SurfaceFlingerProperties", - "truth-prebuilt", + "truth", ], resource_dirs: ["src/main/res"], certificate: "platform", diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp index 448d46fe5e4e..3f2c80831565 100644 --- a/tests/DynamicCodeLoggerIntegrationTests/Android.bp +++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp @@ -47,7 +47,7 @@ android_test { static_libs: [ "androidx.test.rules", - "truth-prebuilt", + "truth", ], compile_multilib: "both", diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index a2ae56eaeadc..82aa85d55e4c 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -236,7 +236,7 @@ java_library { static_libs: [ "flickerlib", "flickerlib-helpers", - "truth-prebuilt", + "truth", "app-helpers-core", ], } @@ -255,7 +255,7 @@ java_library { "flickerlib", "flickerlib-apphelpers", "flickerlib-helpers", - "truth-prebuilt", + "truth", "app-helpers-core", "wm-flicker-window-extensions", ], diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt index c30786f4e1c4..da51eff24dc1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -17,6 +17,7 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.content.Intent import android.media.session.MediaController import android.media.session.MediaSessionManager import android.tools.common.datatypes.Rect @@ -267,6 +268,11 @@ open class PipAppHelper(instrumentation: Instrumentation) : fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper) + fun changeAspectRatio() { + val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO") + context.sendBroadcast(intent) + } + fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) { clickObject(ENTER_PIP_BUTTON_ID) diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java index cdb1d42bd4f2..12eaad108fc6 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java @@ -82,6 +82,8 @@ public class PipActivity extends Activity { "com.android.wm.shell.flicker.testapp.SWITCH_OFF"; private static final String ACTION_SWITCH_ON = "com.android.wm.shell.flicker.testapp.SWITCH_ON"; private static final String ACTION_CLEAR = "com.android.wm.shell.flicker.testapp.CLEAR"; + private static final String ACTION_ASPECT_RATIO = + "com.android.wm.shell.flicker.testapp.ASPECT_RATIO"; private final PictureInPictureParams.Builder mPipParamsBuilder = new PictureInPictureParams.Builder() @@ -109,6 +111,9 @@ public class PipActivity extends Activity { case ACTION_CLEAR: mPipParamsBuilder.setActions(Collections.emptyList()); break; + case ACTION_ASPECT_RATIO: + mPipParamsBuilder.setAspectRatio(RATIO_TALL); + break; case ACTION_NO_OP: return; default: @@ -190,6 +195,7 @@ public class PipActivity extends Activity { filter.addAction(ACTION_CLEAR); filter.addAction(ACTION_SET_REQUESTED_ORIENTATION); filter.addAction(ACTION_ENTER_PIP); + filter.addAction(ACTION_ASPECT_RATIO); registerReceiver(mBroadcastReceiver, filter); handleIntentExtra(getIntent()); diff --git a/tests/FsVerityTest/TEST_MAPPING b/tests/FsVerityTest/TEST_MAPPING index 39944bed3f60..7d59d7765183 100644 --- a/tests/FsVerityTest/TEST_MAPPING +++ b/tests/FsVerityTest/TEST_MAPPING @@ -1,12 +1,7 @@ { - "postsubmit": [ + "presubmit": [ { "name": "FsVerityTest" - }, - // nextgen test only runs during postsubmit. - { - "name": "FsVerityTest", - "keywords": ["nextgen"] } ] } diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 365e00e2b652..cf2d5d69552f 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -9,6 +9,10 @@ package { android_test { name: "InputTests", + defaults: [ + // For ExtendedMockito dependencies. + "modules-utils-testable-device-config-defaults", + ], srcs: [ "src/**/*.java", "src/**/*.kt", @@ -35,7 +39,7 @@ android_test { "services.core.unboosted", "testables", "testng", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index b64775103ab2..fa86e9c4ec0a 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -32,11 +32,16 @@ import android.os.Bundle import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.provider.Settings +import android.util.proto.ProtoOutputStream import android.view.InputDevice import android.view.inputmethod.InputMethodInfo import android.view.inputmethod.InputMethodSubtype import androidx.test.core.R import androidx.test.core.app.ApplicationProvider +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.internal.os.KeyboardConfiguredProto +import com.android.internal.util.FrameworkStatsLog +import com.android.modules.utils.testing.ExtendedMockitoRule import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals @@ -46,9 +51,9 @@ import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito -import org.mockito.junit.MockitoJUnit import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.IOException @@ -96,6 +101,9 @@ class KeyboardLayoutManagerTests { private const val ENGLISH_US_LAYOUT_NAME = "keyboard_layout_english_us" private const val ENGLISH_UK_LAYOUT_NAME = "keyboard_layout_english_uk" private const val VENDOR_SPECIFIC_LAYOUT_NAME = "keyboard_layout_vendorId:1,productId:1" + const val LAYOUT_TYPE_QWERTZ = 2 + const val LAYOUT_TYPE_QWERTY = 1 + const val LAYOUT_TYPE_DEFAULT = 0 } private val ENGLISH_US_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_US_LAYOUT_NAME) @@ -103,8 +111,10 @@ class KeyboardLayoutManagerTests { private val VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR = createLayoutDescriptor(VENDOR_SPECIFIC_LAYOUT_NAME) - @get:Rule - val rule = MockitoJUnit.rule()!! + @JvmField + @Rule + val extendedMockitoRule = ExtendedMockitoRule.Builder(this) + .mockStatic(FrameworkStatsLog::class.java).build()!! @Mock private lateinit var iInputManager: IInputManager @@ -145,7 +155,9 @@ class KeyboardLayoutManagerTests { override fun finishWrite(fos: FileOutputStream?, success: Boolean) {} }) testLooper = TestLooper() - keyboardLayoutManager = KeyboardLayoutManager(context, native, dataStore, testLooper.looper) + keyboardLayoutManager = Mockito.spy( + KeyboardLayoutManager(context, native, dataStore, testLooper.looper) + ) setupInputDevices() setupBroadcastReceiver() setupIme() @@ -827,6 +839,100 @@ class KeyboardLayoutManagerTests { } } + @Test + fun testConfigurationLogged_onInputDeviceAdded_VirtualKeyboardBasedSelection() { + val imeInfos = listOf( + KeyboardLayoutManager.ImeInfo(0, imeInfo, + createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"))) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(keyboardDevice.vendorId), + ArgumentMatchers.eq(keyboardDevice.productId), + ArgumentMatchers.eq(createByteArray( + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT, + "German", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, + "de-Latn", + LAYOUT_TYPE_QWERTZ)) + ) + } + } + } + + @Test + fun testConfigurationLogged_onInputDeviceAdded_DeviceBasedSelection() { + val imeInfos = listOf( + KeyboardLayoutManager.ImeInfo(0, imeInfo, + createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"))) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId), + ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId), + ArgumentMatchers.eq(createByteArray( + "en", + LAYOUT_TYPE_QWERTY, + "English (US)", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE, + "de-Latn", + LAYOUT_TYPE_QWERTZ)) + ) + } + } + } + + @Test + fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() { + val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype())) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(keyboardDevice.vendorId), + ArgumentMatchers.eq(keyboardDevice.productId), + ArgumentMatchers.eq(createByteArray( + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT, + "Default", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT, + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT)) + ) + } + } + } + + @Test + fun testConfigurationNotLogged_onInputDeviceChanged() { + val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype())) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) + ExtendedMockito.verify({ + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any(ByteArray::class.java) + ) + }, Mockito.times(0)) + } + } + private fun assertCorrectLayout( device: InputDevice, imeSubtype: InputMethodSubtype, @@ -842,18 +948,60 @@ class KeyboardLayoutManagerTests { } private fun createImeSubtype(): InputMethodSubtype = - InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++).build() + createImeSubtypeForLanguageTagAndLayoutType(null, null) private fun createImeSubtypeForLanguageTag(languageTag: String): InputMethodSubtype = - InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++) - .setLanguageTag(languageTag).build() + createImeSubtypeForLanguageTagAndLayoutType(languageTag, null) private fun createImeSubtypeForLanguageTagAndLayoutType( - languageTag: String, - layoutType: String - ): InputMethodSubtype = - InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++) - .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build() + languageTag: String?, + layoutType: String? + ): InputMethodSubtype { + val builder = InputMethodSubtype.InputMethodSubtypeBuilder() + .setSubtypeId(nextImeSubtypeId++) + .setIsAuxiliary(false) + .setSubtypeMode("keyboard") + if (languageTag != null && layoutType != null) { + builder.setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType) + } else if (languageTag != null) { + builder.setLanguageTag(languageTag) + } + return builder.build() + } + + private fun createByteArray( + expectedLanguageTag: String, expectedLayoutType: Int, expectedLayoutName: String, + expectedCriteria: Int, expectedImeLanguageTag: String, expectedImeLayoutType: Int): ByteArray { + val proto = ProtoOutputStream() + val keyboardLayoutConfigToken = proto.start( + KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG, + expectedLanguageTag + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE, + expectedLayoutType + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME, + expectedLayoutName + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA, + expectedCriteria + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG, + expectedImeLanguageTag + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE, + expectedImeLayoutType + ) + proto.end(keyboardLayoutConfigToken); + return proto.bytes + } private fun hasLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean { for (kl in layoutList) { diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java index 1b98887199e3..ae7fb3b29f6c 100644 --- a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java +++ b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java @@ -14,15 +14,16 @@ * limitations under the License. */ -package com.android.server.input; +package com.android.server.input.debug; -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.anyFloat; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -31,11 +32,12 @@ 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 com.android.server.input.InputManagerService; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,76 +50,50 @@ import org.junit.runner.RunWith; public class FocusEventDebugViewTest { private FocusEventDebugView mFocusEventDebugView; - private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView; - private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView; - private float mScaledVerticalScrollFactor; + private RotaryInputValueView mRotaryInputValueView; + private RotaryInputGraphView mRotaryInputGraphView; @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); + mRotaryInputValueView = spy(new RotaryInputValueView(context)); + mRotaryInputGraphView = spy(new 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() { + public void handleRotaryInput_sendsMotionEventWhenEnabled() { mFocusEventDebugView.handleUpdateShowRotaryInput(true); - mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f)); + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L)); - assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0); + verify(mRotaryInputGraphView).addValue(0.5f, 10L); + verify(mRotaryInputValueView).updateValue(0.5f); } @Test - public void updateActivityStatus_setsAndRemovesColorFilter() { - // It should not be active initially. - assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + public void handleRotaryInput_doesNotSendMotionEventWhenDisabled() { + mFocusEventDebugView.handleUpdateShowRotaryInput(false); - mRotaryInputValueView.updateActivityStatus(true); - // It should be active after rotary input. - assertNotNull(mRotaryInputValueView.getBackground().getColorFilter()); + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L)); - mRotaryInputValueView.updateActivityStatus(false); - // It should not be active after waiting for mUpdateActivityStatusCallback. - assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + verify(mRotaryInputGraphView, never()).addValue(anyFloat(), anyLong()); + verify(mRotaryInputValueView, never()).updateValue(anyFloat()); } - private MotionEvent createRotaryMotionEvent(float scrollAxisValue) { + private MotionEvent createRotaryMotionEvent(float scrollAxisValue, long eventTime) { PointerCoords pointerCoords = new PointerCoords(); pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue); PointerProperties pointerProperties = new PointerProperties(); return MotionEvent.obtain( /* downTime */ 0, - /* eventTime */ 0, + /* eventTime */ eventTime, /* action */ MotionEvent.ACTION_SCROLL, /* pointerCount */ 1, /* pointerProperties */ new PointerProperties[] {pointerProperties}, diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java new file mode 100644 index 000000000000..af6ece414fd1 --- /dev/null +++ b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java @@ -0,0 +1,61 @@ +/* + * 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.debug; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; + +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 RotaryInputGraphViewTest + */ +@RunWith(AndroidJUnit4.class) +public class RotaryInputGraphViewTest { + + private RotaryInputGraphView mRotaryInputGraphView; + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getContext(); + + mRotaryInputGraphView = new RotaryInputGraphView(context); + } + + @Test + public void startsWithDefaultFrameCenter() { + assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01); + } + + @Test + public void addValue_translatesRotaryInputGraphViewWithHighScrollValue() { + final float scrollAxisValue = 1000f; + final long eventTime = 0; + + mRotaryInputGraphView.addValue(scrollAxisValue, eventTime); + + assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0); + } +} diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java new file mode 100644 index 000000000000..e5e3852dc318 --- /dev/null +++ b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java @@ -0,0 +1,85 @@ +/* + * 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.debug; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.content.Context; +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; + +import java.util.Locale; + +/** + * Build/Install/Run: + * atest RotaryInputValueViewTest + */ +@RunWith(AndroidJUnit4.class) +public class RotaryInputValueViewTest { + + private final Locale mDefaultLocale = Locale.getDefault(); + + private RotaryInputValueView mRotaryInputValueView; + private float mScaledVerticalScrollFactor; + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getContext(); + mScaledVerticalScrollFactor = + ViewConfiguration.get(context).getScaledVerticalScrollFactor(); + + mRotaryInputValueView = new RotaryInputValueView(context); + } + + @Test + public void startsWithDefaultValue() { + assertEquals("+0.0", mRotaryInputValueView.getText().toString()); + } + + @Test + public void updateValue_updatesTextWithScrollValue() { + final float scrollAxisValue = 1000f; + final String expectedText = String.format(mDefaultLocale, "+%.1f", + scrollAxisValue * mScaledVerticalScrollFactor); + + mRotaryInputValueView.updateValue(scrollAxisValue); + + assertEquals(expectedText, mRotaryInputValueView.getText().toString()); + } + + @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()); + } +} diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp index 84845c69fb27..58ceb3f3edf4 100644 --- a/tests/InputMethodStressTest/Android.bp +++ b/tests/InputMethodStressTest/Android.bp @@ -26,7 +26,7 @@ android_test { "compatibility-device-util-axt", "platform-test-annotations", "platform-test-rules", - "truth-prebuilt", + "truth", ], test_suites: [ "general-tests", diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp index eee486f99748..15aaa463cce7 100644 --- a/tests/InputScreenshotTest/Android.bp +++ b/tests/InputScreenshotTest/Android.bp @@ -29,7 +29,7 @@ android_test { "androidx.lifecycle_lifecycle-livedata-ktx", "androidx.lifecycle_lifecycle-runtime-compose", "androidx.navigation_navigation-compose", - "truth-prebuilt", + "truth", "androidx.compose.runtime_runtime", "androidx.test.core", "androidx.test.ext.junit", @@ -47,7 +47,7 @@ android_test { "services.core.unboosted", "testables", "testng", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp index ef45864dd93b..ddec8fa1d70a 100644 --- a/tests/Internal/Android.bp +++ b/tests/Internal/Android.bp @@ -19,7 +19,7 @@ android_test { "junit", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", "platform-test-annotations", ], java_resource_dirs: ["res"], diff --git a/tests/LocalizationTest/Android.bp b/tests/LocalizationTest/Android.bp index 4e0b0a89d972..909ca5972552 100644 --- a/tests/LocalizationTest/Android.bp +++ b/tests/LocalizationTest/Android.bp @@ -34,7 +34,7 @@ android_test { "androidx.test.ext.junit", "androidx.test.rules", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", ], jni_libs: [ // For mockito extended diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp index 254770d21818..fcacab3fb13c 100644 --- a/tests/MidiTests/Android.bp +++ b/tests/MidiTests/Android.bp @@ -31,7 +31,7 @@ android_test { "mockito-target-inline-minus-junit4", "platform-test-annotations", "services.midi", - "truth-prebuilt", + "truth", ], jni_libs: ["libdexmakerjvmtiagent"], certificate: "platform", diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp index 1e1dc8458560..e0e6c4c43b16 100644 --- a/tests/PackageWatchdog/Android.bp +++ b/tests/PackageWatchdog/Android.bp @@ -32,7 +32,7 @@ android_test { "androidx.test.rules", "services.core", "services.net", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], jni_libs: [ diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp index f0f9c4bdd721..fd992cf415cf 100644 --- a/tests/PlatformCompatGating/Android.bp +++ b/tests/PlatformCompatGating/Android.bp @@ -38,7 +38,7 @@ android_test { "androidx.test.ext.junit", "mockito-target-minus-junit4", "testng", - "truth-prebuilt", + "truth", "platform-compat-test-rules", ], } diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp index 5f91f9d0e505..f6a41c2f44b7 100644 --- a/tests/PlatformCompatGating/test-rules/Android.bp +++ b/tests/PlatformCompatGating/test-rules/Android.bp @@ -29,7 +29,7 @@ java_library { static_libs: [ "junit", "androidx.test.core", - "truth-prebuilt", - "core-compat-test-rules" + "truth", + "core-compat-test-rules", ], } diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp index 38313f85c31d..055d6258d1ac 100644 --- a/tests/SurfaceViewBufferTests/Android.bp +++ b/tests/SurfaceViewBufferTests/Android.bp @@ -45,7 +45,7 @@ android_test { "kotlinx-coroutines-android", "flickerlib", "flickerlib-trace_processor_shell", - "truth-prebuilt", + "truth", "cts-wm-util", "CtsSurfaceValidatorLib", ], diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp index bf12f423f145..d2ade34148e2 100644 --- a/tests/TaskOrganizerTest/Android.bp +++ b/tests/TaskOrganizerTest/Android.bp @@ -43,6 +43,6 @@ android_test { "kotlinx-coroutines-android", "flickerlib", "flickerlib-trace_processor_shell", - "truth-prebuilt", + "truth", ], } diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp index 81ec265c2c29..b968e5d81148 100644 --- a/tests/TelephonyCommonTests/Android.bp +++ b/tests/TelephonyCommonTests/Android.bp @@ -32,11 +32,11 @@ android_test { static_libs: [ "mockito-target-extended", "androidx.test.rules", - "truth-prebuilt", + "truth", "platform-test-annotations", "androidx.core_core", "androidx.fragment_fragment", - "androidx.test.ext.junit" + "androidx.test.ext.junit", ], jni_libs: ["libdexmakerjvmtiagent"], diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp index c216bced81f0..4e75a1d02a41 100644 --- a/tests/TrustTests/Android.bp +++ b/tests/TrustTests/Android.bp @@ -28,7 +28,7 @@ android_test { "flag-junit", "mockito-target-minus-junit4", "servicestests-utils", - "truth-prebuilt", + "truth", ], libs: [ "android.test.runner", diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp index 9bfcc18ee301..ddb3649a8320 100644 --- a/tests/UpdatableSystemFontTest/Android.bp +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -30,7 +30,7 @@ android_test { "androidx.test.uiautomator_uiautomator", "compatibility-device-util-axt", "platform-test-annotations", - "truth-prebuilt", + "truth", ], test_suites: [ "general-tests", diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp index 97fbf5b32035..c02d8e96abb0 100644 --- a/tests/UsbManagerTests/Android.bp +++ b/tests/UsbManagerTests/Android.bp @@ -31,7 +31,7 @@ android_test { "androidx.test.rules", "mockito-target-inline-minus-junit4", "platform-test-annotations", - "truth-prebuilt", + "truth", "UsbManagerTestLib", ], jni_libs: ["libdexmakerjvmtiagent"], diff --git a/tests/UsbManagerTests/lib/Android.bp b/tests/UsbManagerTests/lib/Android.bp index 994484cd63bf..4e5a70fef0ca 100644 --- a/tests/UsbManagerTests/lib/Android.bp +++ b/tests/UsbManagerTests/lib/Android.bp @@ -34,7 +34,7 @@ android_library { "services.core", "services.net", "services.usb", - "truth-prebuilt", + "truth", "androidx.core_core", ], libs: [ diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp index c60c519ec4d4..92c271165ad7 100644 --- a/tests/UsbTests/Android.bp +++ b/tests/UsbTests/Android.bp @@ -34,7 +34,7 @@ android_test { "services.core", "services.net", "services.usb", - "truth-prebuilt", + "truth", "UsbManagerTestLib", ], jni_libs: [ diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp index 01d34e4ff645..39037f22fdcb 100644 --- a/tests/componentalias/Android.bp +++ b/tests/componentalias/Android.bp @@ -25,7 +25,7 @@ java_defaults { "androidx.test.rules", "compatibility-device-util-axt", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", ], libs: ["android.test.base"], srcs: [ diff --git a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java index 6a6ab00e33ff..a43e1b091d41 100644 --- a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java +++ b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java @@ -21,15 +21,39 @@ import java.util.Map; public class TestableFlagResolver implements SystemUiSystemPropertiesFlags.FlagResolver { private Map<String, Boolean> mOverrides = new HashMap<>(); + private Map<String, Integer> mOverridesInt = new HashMap<>(); + private Map<String, String> mOverridesString = new HashMap<>(); @Override public boolean isEnabled(SystemUiSystemPropertiesFlags.Flag flag) { return mOverrides.getOrDefault(flag.mSysPropKey, flag.mDefaultValue); } + @Override + public int getIntValue(SystemUiSystemPropertiesFlags.Flag flag) { + return mOverridesInt.getOrDefault(flag.mSysPropKey, flag.mDefaultIntValue); + } + + @Override + public String getStringValue(SystemUiSystemPropertiesFlags.Flag flag) { + return mOverridesString.getOrDefault(flag.mSysPropKey, flag.mDefaultStringValue); + } + public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag, boolean isEnabled) { mOverrides.put(flag.mSysPropKey, isEnabled); return this; } + + public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag, + int value) { + mOverridesInt.put(flag.mSysPropKey, value); + return this; + } + + public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag, + String value) { + mOverridesString.put(flag.mSysPropKey, value); + return this; + } } diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 7323b0f4c14f..977b2768e702 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -220,6 +220,7 @@ genrule { name: "aapt2-protos", tools: [":soong_zip"], srcs: [ + "ApkInfo.proto", "Configuration.proto", "ResourcesInternal.proto", "ResourceMetadata.proto", diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp index 87da09a7b054..8c644cf83339 100644 --- a/tools/aapt2/java/AnnotationProcessor.cpp +++ b/tools/aapt2/java/AnnotationProcessor.cpp @@ -49,16 +49,19 @@ struct AnnotationRule { kDeprecated = 0x01, kSystemApi = 0x02, kTestApi = 0x04, + kFlaggedApi = 0x08, }; StringPiece doc_str; uint32_t bit_mask; StringPiece annotation; + bool preserve_params; }; -static std::array<AnnotationRule, 2> sAnnotationRules = {{ - {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi"}, - {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi"}, +static std::array<AnnotationRule, 3> sAnnotationRules = {{ + {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi", true}, + {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi", false}, + {"@FlaggedApi", AnnotationRule::kFlaggedApi, "@android.annotation.FlaggedApi", true}, }}; void AnnotationProcessor::AppendCommentLine(std::string comment) { @@ -73,12 +76,11 @@ void AnnotationProcessor::AppendCommentLine(std::string comment) { std::string::size_type idx = comment.find(rule.doc_str.data()); if (idx != std::string::npos) { // Captures all parameters associated with the specified annotation rule - // by matching the first pair of parantheses after the rule. - std::regex re(std::string(rule.doc_str) += "\\s*\\((.+)\\)"); + // by matching the first pair of parentheses after the rule. + std::regex re(std::string(rule.doc_str).append(R"(\s*\((.+)\))")); std::smatch match_result; const bool is_match = std::regex_search(comment, match_result, re); - // We currently only capture and preserve parameters for SystemApi. - if (is_match && rule.bit_mask == AnnotationRule::kSystemApi) { + if (is_match && rule.preserve_params) { annotation_parameter_map_[rule.bit_mask] = match_result[1].str(); comment.erase(comment.begin() + match_result.position(), comment.begin() + match_result.position() + match_result.length()); diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp index 6bc8902a6dcf..e98e96ba3bc3 100644 --- a/tools/aapt2/java/AnnotationProcessor_test.cpp +++ b/tools/aapt2/java/AnnotationProcessor_test.cpp @@ -76,6 +76,36 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationParamsAndRemovesFromCommen EXPECT_THAT(annotations, HasSubstr("This is a system API")); } +TEST(AnnotationProcessorTest, EmitsFlaggedApiAnnotationAndRemovesFromComment) { + AnnotationProcessor processor; + processor.AppendComment("@FlaggedApi This is a flagged API"); + + std::string annotations; + StringOutputStream out(&annotations); + Printer printer(&out); + processor.Print(&printer); + out.Flush(); + + EXPECT_THAT(annotations, HasSubstr("@android.annotation.FlaggedApi")); + EXPECT_THAT(annotations, Not(HasSubstr("@FlaggedApi"))); + EXPECT_THAT(annotations, HasSubstr("This is a flagged API")); +} + +TEST(AnnotationProcessorTest, EmitsFlaggedApiAnnotationParamsAndRemovesFromComment) { + AnnotationProcessor processor; + processor.AppendComment("@FlaggedApi (\"android.flags.my_flag\") This is a flagged API"); + + std::string annotations; + StringOutputStream out(&annotations); + Printer printer(&out); + processor.Print(&printer); + out.Flush(); + + EXPECT_THAT(annotations, HasSubstr("@android.annotation.FlaggedApi(\"android.flags.my_flag\")")); + EXPECT_THAT(annotations, Not(HasSubstr("@FlaggedApi"))); + EXPECT_THAT(annotations, HasSubstr("This is a flagged API")); +} + TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) { AnnotationProcessor processor; processor.AppendComment("@TestApi This is a test API"); diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt index 33010baaf894..678e6eae0be6 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt @@ -46,8 +46,11 @@ class ConstantFilter( } methodPolicy = policy - // TODO: Need to think about the realistic default behavior. - classPolicy = if (policy != FilterPolicy.Throw) policy else FilterPolicy.Remove + // If the default policy is "throw", we convert it to "keep" for classes and fields. + classPolicy = when (policy) { + FilterPolicy.Throw -> FilterPolicy.Keep + else -> policy + } fieldPolicy = classPolicy } diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt index 9c372ff68e37..c6334c40e8f4 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt @@ -17,6 +17,8 @@ package com.android.hoststubgen.filters import com.android.hoststubgen.HostStubGenErrors import com.android.hoststubgen.HostStubGenInternalException +import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC +import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME import com.android.hoststubgen.asm.isAnonymousInnerClass import com.android.hoststubgen.log import com.android.hoststubgen.asm.ClassNodes @@ -81,6 +83,16 @@ class ImplicitOutputFilter( } } + // If we throw from the static initializer, the class would be useless, so we convert it + // "keep" instead. + if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC && + fallback.policy == FilterPolicy.Throw) { + // TODO Maybe show a warning?? But that'd be too noisy with --default-throw. + return FilterPolicy.Keep.withReason( + "'throw' on static initializer is handled as 'keep'" + + " [original throw reason: ${fallback.reason}]") + } + return fallback } }
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt index 57b668954f98..ce72a8e79882 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt @@ -79,7 +79,7 @@ class ImplGeneratingAdapter( // StaticInitMerger will merge it with the existing one, if any. visitMethod( Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC, - "<clinit>", + CLASS_INITIALIZER_NAME, "()V", null, null diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp index e7fb2debfc4e..b71e5c47c70e 100644 --- a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp +++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp @@ -24,7 +24,7 @@ java_library_host { ], static_libs: [ "junit", - "truth-prebuilt", + "truth", "mockito", // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp index 05d6a43cdb0f..f9dc305a4e3e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp @@ -104,7 +104,7 @@ java_library_host { ], static_libs: [ "junit", - "truth-prebuilt", + "truth", // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ "platform-test-annotations", diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt index 43ceec42660d..0761edc34916 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -372,7 +372,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 3 + interfaces: 0, fields: 1, methods: 1, attributes: 3 public static boolean sInitialized; descriptor: Z flags: (0x0009) ACC_PUBLIC, ACC_STATIC @@ -387,17 +387,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow - - static {}; - descriptor: ()V - flags: (0x0008) ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow } SourceFile: "TinyFrameworkClassWithInitializer.java" RuntimeVisibleAnnotations: diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt index 43ceec42660d..0761edc34916 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt @@ -372,7 +372,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 3 + interfaces: 0, fields: 1, methods: 1, attributes: 3 public static boolean sInitialized; descriptor: Z flags: (0x0009) ACC_PUBLIC, ACC_STATIC @@ -387,17 +387,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow - - static {}; - descriptor: ()V - flags: (0x0008) ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow } SourceFile: "TinyFrameworkClassWithInitializer.java" RuntimeVisibleAnnotations: diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt index 079d2a84e498..8fcd2fbe7c84 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt @@ -15,3 +15,8 @@ class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy stub # Class load hook class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + + +class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer stubclass + # Testing 'throw' on a static initializer. This should be handled as 'keep'. + method <clinit> ()V throw diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh index fd486468a1a7..722905f15b41 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh @@ -16,6 +16,10 @@ source "${0%/*}"/../../common.sh +#********************************************************************************************** +#This script is broken because it relies on soong intermediate files, which seem to have moved. +#********************************************************************************************** + # This scripts run the "tiny-framework" test, but does most stuff from the command line, using # the native java and javac commands. # This is useful to @@ -57,7 +61,7 @@ framework_compile_classpaths=( test_compile_classpaths=( $SOONG_INT/external/junit/junit/android_common/combined/junit.jar - $SOONG_INT/prebuilts/tools/common/m2/truth-prebuilt/android_common/combined/truth-prebuilt.jar + $ANDROID_BUILD_TOP/out/target/common/obj/JAVA_LIBRARIES/truth-prebuilt_intermediates/classes.jar ) test_runtime_classpaths=( diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java index 53cfdf6cd412..01a690b73ba0 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java @@ -18,10 +18,14 @@ package com.android.hoststubgen.test.tinyframework; import android.hosttest.annotation.HostSideTestClassLoadHook; import android.hosttest.annotation.HostSideTestWholeClassStub; + +// Note, policy-override-tiny-framework.txt hss an override on this class. @HostSideTestClassLoadHook( "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded") @HostSideTestWholeClassStub public class TinyFrameworkClassWithInitializer { + // Note, this method has a 'throw' in the policy file, which is handled as a 'keep' (because + // it's a static initializer), so this won't show up in the stub jar. static { sInitialized = true; } diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh index 7600942c99e6..2e9cf428354a 100755 --- a/tools/hoststubgen/scripts/run-all-tests.sh +++ b/tools/hoststubgen/scripts/run-all-tests.sh @@ -33,7 +33,9 @@ run m run-ravenwood-test ${READY_TEST_MODULES[*]} ${NOT_READY_TEST_MODULES[*]} run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh run ./hoststubgen/test-framework/run-test-without-atest.sh -run ./hoststubgen/test-tiny-framework/run-test-manually.sh + +#This script is broken because it relies on soong intermediate files, which seem to have moved. +#run ./hoststubgen/test-tiny-framework/run-test-manually.sh run atest tiny-framework-dump-test run ./scripts/build-framework-hostside-jars-and-extract.sh diff --git a/tools/processors/immutability/Android.bp b/tools/processors/immutability/Android.bp index a7d69039fcb0..ecc283b0b37e 100644 --- a/tools/processors/immutability/Android.bp +++ b/tools/processors/immutability/Android.bp @@ -50,7 +50,7 @@ java_test_host { static_libs: [ "compile-testing-prebuilt", - "truth-prebuilt", + "truth", "junit", "kotlin-reflect", "ImmutabilityAnnotationProcessorHostLibrary", diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp index 7059c52ddc76..9c755b7d58c2 100644 --- a/tools/processors/intdef_mappings/Android.bp +++ b/tools/processors/intdef_mappings/Android.bp @@ -38,7 +38,7 @@ java_test_host { static_libs: [ "compile-testing-prebuilt", - "truth-prebuilt", + "truth", "junit", "guava", "libintdef-annotation-processor", diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 7a299694741a..5a0f742372d7 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -40,7 +40,7 @@ android_test { "frameworks-base-testutils", "guava", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: [ |