diff options
939 files changed, 19336 insertions, 6733 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 95b61559e839..003b7f87fa23 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}", @@ -40,6 +41,8 @@ aconfig_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 +243,24 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// 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", @@ -383,3 +404,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/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 f017a47a4013..222275f9a4e4 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -62,15 +62,6 @@ metalava_cmd = "$(location metalava)" metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " metalava_cmd += " --quiet " -genrule { - name: "current-api-xml", - tools: ["metalava"], - srcs: [":frameworks-base-api-current.txt"], - out: ["current.api"], - cmd: metalava_cmd + "signature-to-jdiff $(in) $(out)", - visibility: ["//visibility:public"], -} - combined_apis { name: "frameworks-base-api", bootclasspath: [ @@ -91,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/core/api/current.txt b/core/api/current.txt index 43aaee8dd45e..d037c31e5af7 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); @@ -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); @@ -15224,6 +15224,7 @@ package android.graphics { method public int getMaxAnisotropy(); method public void setFilterMode(int); method public void setMaxAnisotropy(@IntRange(from=1) int); + method @FlaggedApi("com.android.graphics.hwui.flags.gainmap_animations") public void setOverrideGainmap(@Nullable android.graphics.Gainmap); field public static final int FILTER_MODE_DEFAULT = 0; // 0x0 field public static final int FILTER_MODE_LINEAR = 2; // 0x2 field public static final int FILTER_MODE_NEAREST = 1; // 0x1 @@ -18538,8 +18539,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(); @@ -42981,7 +42984,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"; @@ -44903,7 +44905,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; 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 e99725968e55..a99eeb0c36d8 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3946,7 +3946,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"; @@ -12731,16 +12731,11 @@ package android.service.voice { public final class HotwordTrainingData implements android.os.Parcelable { method public int describeContents(); - method public static int getMaxTrainingDataSize(); + method public static int getMaxTrainingDataBytes(); method public int getTimeoutStage(); - method @NonNull public java.util.List<android.service.voice.HotwordTrainingAudio> getTrainingAudios(); + method @NonNull public java.util.List<android.service.voice.HotwordTrainingAudio> getTrainingAudioList(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingData> CREATOR; - field public static final int TIMEOUT_STAGE_EARLY = 2; // 0x2 - field public static final int TIMEOUT_STAGE_LATE = 4; // 0x4 - field public static final int TIMEOUT_STAGE_MIDDLE = 3; // 0x3 - field public static final int TIMEOUT_STAGE_UNKNOWN = 0; // 0x0 - field public static final int TIMEOUT_STAGE_VERY_EARLY = 1; // 0x1 } public static final class HotwordTrainingData.Builder { @@ -12748,7 +12743,7 @@ package android.service.voice { method @NonNull public android.service.voice.HotwordTrainingData.Builder addTrainingAudio(@NonNull android.service.voice.HotwordTrainingAudio); method @NonNull public android.service.voice.HotwordTrainingData build(); method @NonNull public android.service.voice.HotwordTrainingData.Builder setTimeoutStage(int); - method @NonNull public android.service.voice.HotwordTrainingData.Builder setTrainingAudios(@NonNull java.util.List<android.service.voice.HotwordTrainingAudio>); + method @NonNull public android.service.voice.HotwordTrainingData.Builder setTrainingAudioList(@NonNull java.util.List<android.service.voice.HotwordTrainingAudio>); } public interface SandboxedDetectionInitializer { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index b11a686d8f32..4fb7b6bddaac 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -19,8 +19,12 @@ ListenerLast: android.telephony.satellite.SatelliteManager#stopSatelliteTransmis Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`) ListenerLast: android.telephony.satellite.SatelliteManager#stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>) parameter #2: Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`) - - +MissingGetterMatchingBuilder: android.service.voice.HotwordTrainingData.Builder#addTrainingAudio(android.service.voice.HotwordTrainingAudio): + android.service.voice.HotwordTrainingData does not declare a `getTrainingAudios()` method matching method android.service.voice.HotwordTrainingData.Builder.addTrainingAudio(android.service.voice.HotwordTrainingAudio) +MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean): + android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean) +MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): + android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String) MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0: Missing nullability on parameter `intent` in method `onUnbind` MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0: @@ -327,8 +331,12 @@ UnflaggedApi: android.service.voice.HotwordTrainingData#describeContents(): New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.describeContents() UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataSize(): New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataSize() +UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataBytes(): + New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataBytes() UnflaggedApi: android.service.voice.HotwordTrainingData#getTimeoutStage(): New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTimeoutStage() +UnflaggedApi: android.service.voice.HotwordTrainingData#getTrainingAudioList(): + New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTrainingAudioList() UnflaggedApi: android.service.voice.HotwordTrainingData#getTrainingAudios(): New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTrainingAudios() UnflaggedApi: android.service.voice.HotwordTrainingData#writeToParcel(android.os.Parcel, int): @@ -343,6 +351,8 @@ UnflaggedApi: android.service.voice.HotwordTrainingData.Builder#build(): New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.build() UnflaggedApi: android.service.voice.HotwordTrainingData.Builder#setTimeoutStage(int): New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTimeoutStage(int) +UnflaggedApi: android.service.voice.HotwordTrainingData.Builder#setTrainingAudioList(java.util.List<android.service.voice.HotwordTrainingAudio>): + New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTrainingAudioList(java.util.List<android.service.voice.HotwordTrainingAudio>) UnflaggedApi: android.service.voice.HotwordTrainingData.Builder#setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>): New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.Builder.setTrainingAudios(java.util.List<android.service.voice.HotwordTrainingAudio>) UnflaggedApi: android.telecom.StreamingCall#EXTRA_CALL_ID: diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 40c6fa8986f7..aa48451fd24c 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; } 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/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..dd1e108b826d 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1486,13 +1486,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 +1640,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 +2252,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 +2262,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 +2380,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 +2812,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/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/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/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index baed7f9b2a38..39800f73058b 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -637,15 +637,15 @@ public final class VirtualDeviceManager { /** * Specifies a component name to be exempt from the current activity launch policy. * - * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVIY} allows activity - * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}, + * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity + * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}), * then the specified component will be blocked from launching. - * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity - * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}, then - * the specified component will be allowed to launch.</p> + * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches + * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then the + * specified component will be allowed to launch.</p> * - * <p>Note that changing the activity launch policy will not affect current set of exempt - * components and it needs to be updated separately.</p> + * <p>Note that changing the activity launch policy will clear current set of exempt + * components.</p> * * @see #removeActivityPolicyExemption * @see #setDevicePolicy @@ -660,15 +660,15 @@ public final class VirtualDeviceManager { /** * Makes the specified component name to adhere to the default activity launch policy. * - * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVIY} allows activity - * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}, + * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity + * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}), * then the specified component will be allowed to launch. - * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity - * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}, then - * the specified component will be blocked from launching.</p> + * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches + * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then the + * specified component will be blocked from launching.</p> * - * <p>Note that changing the activity launch policy will not affect current set of exempt - * components and it needs to be updated separately.</p> + * <p>Note that changing the activity launch policy will clear current set of exempt + * components.</p> * * @see #addActivityPolicyExemption * @see #setDevicePolicy 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/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..8fbe50c32881 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"; diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index ea0f049cece3..3e12a1a66791 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -13,3 +13,10 @@ flag { 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/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/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/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index a9eb672c4e4d..1307dfc2665e 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -484,7 +484,6 @@ public final class NfcAdapter { /** * A callback to be invoked when the system successfully delivers your {@link NdefMessage} * to another device. - * @see #setOnNdefPushCompleteCallback * @deprecated this feature is removed. File sharing can work using other technology like * Bluetooth. */ @@ -496,7 +495,6 @@ public final class NfcAdapter { * <p>This callback is usually made on a binder thread (not the UI thread). * * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set - * @see #setNdefPushMessageCallback */ public void onNdefPushComplete(NfcEvent event); } @@ -504,11 +502,11 @@ public final class NfcAdapter { /** * A callback to be invoked when another NFC device capable of NDEF push (Android Beam) * is within range. - * <p>Implement this interface and pass it to {@link + * <p>Implement this interface and pass it to {@code * NfcAdapter#setNdefPushMessageCallback setNdefPushMessageCallback()} in order to create an * {@link NdefMessage} at the moment that another device is within range for NFC. Using this * callback allows you to create a message with data that might vary based on the - * content currently visible to the user. Alternatively, you can call {@link + * content currently visible to the user. Alternatively, you can call {@code * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the * same data. * @deprecated this feature is removed. File sharing can work using other technology like 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..36d318008560 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5664,6 +5664,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. @@ -11576,6 +11607,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. * 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/voice/HotwordTrainingData.java b/core/java/android/service/voice/HotwordTrainingData.java index 9dca77ed7f51..31aeb9ca219e 100644 --- a/core/java/android/service/voice/HotwordTrainingData.java +++ b/core/java/android/service/voice/HotwordTrainingData.java @@ -16,7 +16,6 @@ package android.service.voice; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; @@ -34,7 +33,7 @@ import java.util.List; * Contains training data related to hotword detection service. * * <p>The constructed object's size must be within - * {@link HotwordTrainingData#getMaxTrainingDataSize()} or an + * {@link HotwordTrainingData#getMaxTrainingDataBytes()} or an * {@link IllegalArgumentException} will be thrown on construction. Size of the object is calculated * by converting object to a {@link Parcel} and using the {@link Parcel#dataSize()}. * @@ -49,63 +48,26 @@ import java.util.List; genToString = true) @SystemApi public final class HotwordTrainingData implements Parcelable { - /** Max size for hotword training data. */ - public static int getMaxTrainingDataSize() { + /** Max size for hotword training data in bytes. */ + public static int getMaxTrainingDataBytes() { return 1024 * 1024; // 1 MB; } /** The list containing hotword audio that is useful for training. */ @NonNull @DataClass.PluralOf("trainingAudio") - private final List<HotwordTrainingAudio> mTrainingAudios; + private final List<HotwordTrainingAudio> mTrainingAudioList; - private static List<HotwordTrainingAudio> defaultTrainingAudios() { + private static List<HotwordTrainingAudio> defaultTrainingAudioList() { return Collections.emptyList(); } - /** Timeout stage is unknown. */ - public static final int TIMEOUT_STAGE_UNKNOWN = 0; - - /** - * Timeout stage value that represents that the model timed out very early while detecting - * hotword. - */ - public static final int TIMEOUT_STAGE_VERY_EARLY = 1; - - /** - * Timeout stage value that represents that the model timed out early while detecting - * hotword. - */ - public static final int TIMEOUT_STAGE_EARLY = 2; - - /** - * Timeout stage value that represents that the model timed out in the middle while detecting - * hotword. - */ - public static final int TIMEOUT_STAGE_MIDDLE = 3; - - /** - * Timeout stage value that represents that the model timed out late while detecting - * hotword. - */ - public static final int TIMEOUT_STAGE_LATE = 4; - - /** @hide */ - @IntDef(prefix = {"TIMEOUT_STAGE"}, value = { - TIMEOUT_STAGE_UNKNOWN, - TIMEOUT_STAGE_VERY_EARLY, - TIMEOUT_STAGE_EARLY, - TIMEOUT_STAGE_MIDDLE, - TIMEOUT_STAGE_LATE, - }) - @interface HotwordTimeoutStage {} - - /** Stage when timeout occurred. */ - @HotwordTimeoutStage + /** App-defined stage when hotword model timed-out while running. + * <p> Returns 0 if unset. */ private final int mTimeoutStage; private static int defaultTimeoutStage() { - return TIMEOUT_STAGE_UNKNOWN; + return 0; } private void onConstructed() { @@ -115,10 +77,10 @@ public final class HotwordTrainingData implements Parcelable { int dataSizeBytes = parcel.dataSize(); parcel.recycle(); Preconditions.checkArgument( - dataSizeBytes < getMaxTrainingDataSize(), + dataSizeBytes < getMaxTrainingDataBytes(), TextUtils.formatSimple( - "Hotword training data of size %s exceeds size limit of %s!", - dataSizeBytes, getMaxTrainingDataSize())); + "Hotword training data of size %s exceeds size limit of %s bytes!", + dataSizeBytes, getMaxTrainingDataBytes())); } @@ -136,46 +98,14 @@ public final class HotwordTrainingData implements Parcelable { //@formatter:off - /** @hide */ - @IntDef(prefix = "TIMEOUT_STAGE_", value = { - TIMEOUT_STAGE_UNKNOWN, - TIMEOUT_STAGE_VERY_EARLY, - TIMEOUT_STAGE_EARLY, - TIMEOUT_STAGE_MIDDLE, - TIMEOUT_STAGE_LATE - }) - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) - @DataClass.Generated.Member - public @interface TimeoutStage {} - - /** @hide */ - @DataClass.Generated.Member - public static String timeoutStageToString(@TimeoutStage int value) { - switch (value) { - case TIMEOUT_STAGE_UNKNOWN: - return "TIMEOUT_STAGE_UNKNOWN"; - case TIMEOUT_STAGE_VERY_EARLY: - return "TIMEOUT_STAGE_VERY_EARLY"; - case TIMEOUT_STAGE_EARLY: - return "TIMEOUT_STAGE_EARLY"; - case TIMEOUT_STAGE_MIDDLE: - return "TIMEOUT_STAGE_MIDDLE"; - case TIMEOUT_STAGE_LATE: - return "TIMEOUT_STAGE_LATE"; - default: return Integer.toHexString(value); - } - } - @DataClass.Generated.Member /* package-private */ HotwordTrainingData( - @NonNull List<HotwordTrainingAudio> trainingAudios, - @HotwordTimeoutStage int timeoutStage) { - this.mTrainingAudios = trainingAudios; + @NonNull List<HotwordTrainingAudio> trainingAudioList, + int timeoutStage) { + this.mTrainingAudioList = trainingAudioList; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTrainingAudios); + NonNull.class, null, mTrainingAudioList); this.mTimeoutStage = timeoutStage; - com.android.internal.util.AnnotationValidations.validate( - HotwordTimeoutStage.class, null, mTimeoutStage); onConstructed(); } @@ -184,15 +114,16 @@ public final class HotwordTrainingData implements Parcelable { * The list containing hotword audio that is useful for training. */ @DataClass.Generated.Member - public @NonNull List<HotwordTrainingAudio> getTrainingAudios() { - return mTrainingAudios; + public @NonNull List<HotwordTrainingAudio> getTrainingAudioList() { + return mTrainingAudioList; } /** - * Stage when timeout occurred. + * App-defined stage when hotword model timed-out while running. + * <p> Returns 0 if unset. */ @DataClass.Generated.Member - public @HotwordTimeoutStage int getTimeoutStage() { + public int getTimeoutStage() { return mTimeoutStage; } @@ -203,7 +134,7 @@ public final class HotwordTrainingData implements Parcelable { // String fieldNameToString() { ... } return "HotwordTrainingData { " + - "trainingAudios = " + mTrainingAudios + ", " + + "trainingAudioList = " + mTrainingAudioList + ", " + "timeoutStage = " + mTimeoutStage + " }"; } @@ -221,7 +152,7 @@ public final class HotwordTrainingData implements Parcelable { HotwordTrainingData that = (HotwordTrainingData) o; //noinspection PointlessBooleanExpression return true - && java.util.Objects.equals(mTrainingAudios, that.mTrainingAudios) + && java.util.Objects.equals(mTrainingAudioList, that.mTrainingAudioList) && mTimeoutStage == that.mTimeoutStage; } @@ -232,7 +163,7 @@ public final class HotwordTrainingData implements Parcelable { // int fieldNameHashCode() { ... } int _hash = 1; - _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingAudios); + _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingAudioList); _hash = 31 * _hash + mTimeoutStage; return _hash; } @@ -243,7 +174,7 @@ public final class HotwordTrainingData implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - dest.writeParcelableList(mTrainingAudios, flags); + dest.writeParcelableList(mTrainingAudioList, flags); dest.writeInt(mTimeoutStage); } @@ -258,16 +189,14 @@ public final class HotwordTrainingData implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - List<HotwordTrainingAudio> trainingAudios = new ArrayList<>(); - in.readParcelableList(trainingAudios, HotwordTrainingAudio.class.getClassLoader()); + List<HotwordTrainingAudio> trainingAudioList = new ArrayList<>(); + in.readParcelableList(trainingAudioList, HotwordTrainingAudio.class.getClassLoader()); int timeoutStage = in.readInt(); - this.mTrainingAudios = trainingAudios; + this.mTrainingAudioList = trainingAudioList; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTrainingAudios); + NonNull.class, null, mTrainingAudioList); this.mTimeoutStage = timeoutStage; - com.android.internal.util.AnnotationValidations.validate( - HotwordTimeoutStage.class, null, mTimeoutStage); onConstructed(); } @@ -293,8 +222,8 @@ public final class HotwordTrainingData implements Parcelable { @DataClass.Generated.Member public static final class Builder { - private @NonNull List<HotwordTrainingAudio> mTrainingAudios; - private @HotwordTimeoutStage int mTimeoutStage; + private @NonNull List<HotwordTrainingAudio> mTrainingAudioList; + private int mTimeoutStage; private long mBuilderFieldsSet = 0L; @@ -305,26 +234,27 @@ public final class HotwordTrainingData implements Parcelable { * The list containing hotword audio that is useful for training. */ @DataClass.Generated.Member - public @NonNull Builder setTrainingAudios(@NonNull List<HotwordTrainingAudio> value) { + public @NonNull Builder setTrainingAudioList(@NonNull List<HotwordTrainingAudio> value) { checkNotUsed(); mBuilderFieldsSet |= 0x1; - mTrainingAudios = value; + mTrainingAudioList = value; return this; } - /** @see #setTrainingAudios */ + /** @see #setTrainingAudioList */ @DataClass.Generated.Member public @NonNull Builder addTrainingAudio(@NonNull HotwordTrainingAudio value) { - if (mTrainingAudios == null) setTrainingAudios(new ArrayList<>()); - mTrainingAudios.add(value); + if (mTrainingAudioList == null) setTrainingAudioList(new ArrayList<>()); + mTrainingAudioList.add(value); return this; } /** - * Stage when timeout occurred. + * App-defined stage when hotword model timed-out while running. + * <p> Returns 0 if unset. */ @DataClass.Generated.Member - public @NonNull Builder setTimeoutStage(@HotwordTimeoutStage int value) { + public @NonNull Builder setTimeoutStage(int value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; mTimeoutStage = value; @@ -337,13 +267,13 @@ public final class HotwordTrainingData implements Parcelable { mBuilderFieldsSet |= 0x4; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { - mTrainingAudios = defaultTrainingAudios(); + mTrainingAudioList = defaultTrainingAudioList(); } if ((mBuilderFieldsSet & 0x2) == 0) { mTimeoutStage = defaultTimeoutStage(); } HotwordTrainingData o = new HotwordTrainingData( - mTrainingAudios, + mTrainingAudioList, mTimeoutStage); return o; } @@ -357,10 +287,10 @@ public final class HotwordTrainingData implements Parcelable { } @DataClass.Generated( - time = 1693313864628L, + time = 1696092128091L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingData.java", - inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"trainingAudio\") java.util.List<android.service.voice.HotwordTrainingAudio> mTrainingAudios\npublic static final int TIMEOUT_STAGE_UNKNOWN\npublic static final int TIMEOUT_STAGE_VERY_EARLY\npublic static final int TIMEOUT_STAGE_EARLY\npublic static final int TIMEOUT_STAGE_MIDDLE\npublic static final int TIMEOUT_STAGE_LATE\nprivate final @android.service.voice.HotwordTrainingData.HotwordTimeoutStage int mTimeoutStage\npublic static int getMaxTrainingDataSize()\nprivate static java.util.List<android.service.voice.HotwordTrainingAudio> defaultTrainingAudios()\nprivate static int defaultTimeoutStage()\nprivate void onConstructed()\nclass HotwordTrainingData extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"trainingAudio\") java.util.List<android.service.voice.HotwordTrainingAudio> mTrainingAudioList\nprivate final int mTimeoutStage\npublic static int getMaxTrainingDataBytes()\nprivate static java.util.List<android.service.voice.HotwordTrainingAudio> defaultTrainingAudioList()\nprivate static int defaultTimeoutStage()\nprivate void onConstructed()\nclass HotwordTrainingData extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} 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/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java index 49e00d6f6328..7befbfb0f370 100644 --- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java +++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java @@ -151,7 +151,7 @@ public abstract class ExplicitHealthCheckService extends Service { */ @NonNull public abstract List<String> onGetRequestedPackages(); - private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); + private final Handler mHandler = Handler.createAsync(Looper.getMainLooper()); @Nullable private RemoteCallback mCallback; @Override 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/FontConfig.java b/core/java/android/text/FontConfig.java index cb488b08456b..c5857347fd45 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -26,6 +26,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.fonts.FontFamily.Builder.VariableFontFamilyType; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.os.Build; @@ -528,6 +529,7 @@ public final class FontConfig implements Parcelable { private final @NonNull List<Font> mFonts; private final @NonNull LocaleList mLocaleList; private final @Variant int mVariant; + private final int mVariableFontFamilyType; /** @hide */ @Retention(SOURCE) @@ -567,10 +569,11 @@ public final class FontConfig implements Parcelable { * @hide Only system server can create this instance and passed via IPC. */ public FontFamily(@NonNull List<Font> fonts, @NonNull LocaleList localeList, - @Variant int variant) { + @Variant int variant, int variableFontFamilyType) { mFonts = fonts; mLocaleList = localeList; mVariant = variant; + mVariableFontFamilyType = variableFontFamilyType; } /** @@ -621,6 +624,20 @@ public final class FontConfig implements Parcelable { return mVariant; } + /** + * Returns the font family type. + * + * @see Builder#VARIABLE_FONT_FAMILY_TYPE_NONE + * @see Builder#VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL + * @see Builder#VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY + * @see Builder#VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT + * @hide + * @return variable font family type. + */ + public @VariableFontFamilyType int getVariableFontFamilyType() { + return mVariableFontFamilyType; + } + @Override public int describeContents() { return 0; @@ -631,6 +648,7 @@ public final class FontConfig implements Parcelable { dest.writeTypedList(mFonts, flags); dest.writeString8(mLocaleList.toLanguageTags()); dest.writeInt(mVariant); + dest.writeInt(mVariableFontFamilyType); } public static final @NonNull Creator<FontFamily> CREATOR = new Creator<FontFamily>() { @@ -641,8 +659,10 @@ public final class FontConfig implements Parcelable { source.readTypedList(fonts, Font.CREATOR); String langTags = source.readString8(); int variant = source.readInt(); + int varFamilyType = source.readInt(); - return new FontFamily(fonts, LocaleList.forLanguageTags(langTags), variant); + return new FontFamily(fonts, LocaleList.forLanguageTags(langTags), variant, + varFamilyType); } @Override 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/MotionEvent.java b/core/java/android/view/MotionEvent.java index cdf5eec32fec..70e18963d921 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -2197,6 +2197,9 @@ public final class MotionEvent extends InputEvent implements Parcelable { float xOffset, float yOffset, float xPrecision, float yPrecision, long downTimeNanos, long eventTimeNanos, int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords) { + if (action == ACTION_CANCEL) { + flags |= FLAG_CANCELED; + } mNativePtr = nativeInitialize(mNativePtr, deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, classification, xOffset, yOffset, xPrecision, yPrecision, downTimeNanos, eventTimeNanos, pointerCount, pointerIds, @@ -2387,6 +2390,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED); } + private void setCanceled(boolean canceled) { + final int flags = getFlags(); + nativeSetFlags(mNativePtr, canceled ? flags | FLAG_CANCELED : flags & ~FLAG_CANCELED); + } + /** @hide */ public boolean isTargetAccessibilityFocus() { final int flags = getFlags(); @@ -3510,6 +3518,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { * Sets this event's action. */ public final void setAction(int action) { + final int actionMasked = action & ACTION_MASK; + if (actionMasked == ACTION_CANCEL) { + setCanceled(true); + } else if (actionMasked == ACTION_POINTER_UP) { + // Do nothing - we don't know what the real intent here is + } else { + setCanceled(false); + } nativeSetAction(mNativePtr, action); } @@ -4157,6 +4173,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { /** @hide */ @Override public final void cancel() { + setCanceled(true); setAction(ACTION_CANCEL); } 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 f4213510a1c1..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); } @@ -3858,7 +3879,8 @@ public final class ViewRootImpl implements ViewParent, mPendingTransitions.clear(); } - handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction); + handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction, + "view not visible"); } else if (cancelAndRedraw) { mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason() @@ -3873,7 +3895,8 @@ public final class ViewRootImpl implements ViewParent, mPendingTransitions.clear(); } if (!performDraw(mActiveSurfaceSyncGroup)) { - handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction); + handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction, + mLastPerformDrawSkippedReason); } } @@ -4665,6 +4688,10 @@ public final class ViewRootImpl implements ViewParent, return didProduceBuffer -> { if (!didProduceBuffer) { + Trace.instant(Trace.TRACE_TAG_VIEW, + "Transaction not synced due to no frame drawn-" + mTag); + Log.d(mTag, "Pending transaction will not be applied in sync with a draw " + + "because there was nothing new to draw"); mBlastBufferQueue.applyPendingTransactions(frame); } }; @@ -4687,8 +4714,7 @@ public final class ViewRootImpl implements ViewParent, return false; } - final boolean fullRedrawNeeded = - mFullRedrawNeeded || surfaceSyncGroup != null || mHasPendingTransactions; + final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null; mFullRedrawNeeded = false; mIsDrawing = true; @@ -4748,7 +4774,8 @@ public final class ViewRootImpl implements ViewParent, if (mSurfaceHolder != null && mSurface.isValid()) { usingAsyncReport = true; SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> { - handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction); + handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction, + "SurfaceHolder"); }); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); @@ -4762,7 +4789,8 @@ public final class ViewRootImpl implements ViewParent, } if (!usingAsyncReport) { - handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction); + handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction, + "no async report"); } if (mPerformContentCapture) { @@ -4772,13 +4800,19 @@ public final class ViewRootImpl implements ViewParent, } private void handleSyncRequestWhenNoAsyncDraw(SurfaceSyncGroup surfaceSyncGroup, - @Nullable Transaction pendingTransaction) { + @Nullable Transaction pendingTransaction, String logReason) { if (surfaceSyncGroup != null) { if (pendingTransaction != null) { surfaceSyncGroup.addTransaction(pendingTransaction); } surfaceSyncGroup.markSyncReady(); } else if (pendingTransaction != null) { + Trace.instant(Trace.TRACE_TAG_VIEW, + "Transaction not synced due to " + logReason + "-" + mTag); + if (DEBUG_BLAST) { + Log.d(mTag, "Pending transaction will not be applied in sync with a draw due to " + + logReason); + } pendingTransaction.apply(); } } @@ -5286,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. */ @@ -5908,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 @@ -6186,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; @@ -8993,7 +9054,8 @@ public final class ViewRootImpl implements ViewParent, mAdded = false; AnimationHandler.removeRequestor(this); } - handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction); + handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction, + "shutting down VRI"); WindowManagerGlobal.getInstance().doRemoveView(this); } @@ -11362,15 +11424,15 @@ public final class ViewRootImpl implements ViewParent, @Override public boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t) { if (mRemoved || !isHardwareEnabled()) { + Trace.instant(Trace.TRACE_TAG_VIEW, "applyTransactionOnDraw applyImmediately-" + mTag); + Log.d(mTag, "applyTransactionOnDraw: Applying transaction immediately"); t.apply(); } else { + Trace.instant(Trace.TRACE_TAG_VIEW, "applyTransactionOnDraw-" + mTag); // Copy and clear the passed in transaction for thread safety. The new transaction is // accessed on the render thread. mPendingTransaction.merge(t); mHasPendingTransactions = true; - // Schedule the traversal to ensure there's an attempt to draw a frame and apply the - // pending transactions. This is also where the registerFrameCallback will be scheduled. - scheduleTraversals(); } return true; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e64274ec3892..2f4bea0270c1 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1850,6 +1850,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 +1900,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 +1911,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; 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 6cf185a5b460..a95f2ef191f5 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -277,6 +277,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. * @@ -3443,7 +3449,7 @@ public final class AutofillManager { return false; } for (String hint : hints) { - if (hint.equals(View.AUTOFILL_HINT_CREDENTIAL_MANAGER)) { + if (Objects.equals(hint, View.AUTOFILL_HINT_CREDENTIAL_MANAGER)) { return true; } } diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index 195565c5bc09..33db6715c327 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -551,8 +551,8 @@ public abstract class TextClassifierEvent implements Parcelable { * Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}. * <p> * Supported types: - * <p>See {@link TextClassifier.EntityType} - * <p>See {@link ConversationAction.ActionType} + * <p>See {@link TextClassifier} types + * <p>See {@link ConversationAction} types * <p>See {@link ULocale#toLanguageTag()} */ @NonNull diff --git a/core/java/android/view/translation/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java index b7e13dda9ff6..52760f728205 100644 --- a/core/java/android/view/translation/TranslationCapability.java +++ b/core/java/android/view/translation/TranslationCapability.java @@ -207,7 +207,7 @@ public final class TranslationCapability implements Parcelable { /** * Translation flags for settings that are supported by the - * {@link android.service.translation.TranslationService} between the {@link TranslationSpec}s + * translation service between the {@link TranslationSpec}s * provided in this capability. */ @DataClass.Generated.Member diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java index 5aad823c374e..99544e80ed9d 100644 --- a/core/java/android/view/translation/TranslationManager.java +++ b/core/java/android/view/translation/TranslationManager.java @@ -56,7 +56,7 @@ import java.util.function.Consumer; * translation framework. * * <p>The TranslationManager manages {@link Translator}s and help bridge client calls to - * the server {@link android.service.translation.TranslationService} </p> + * the server translation service </p> */ @SystemService(Context.TRANSLATION_MANAGER_SERVICE) public final class TranslationManager { diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java index 027edc21389f..ff11ffad28f5 100644 --- a/core/java/android/view/translation/TranslationRequest.java +++ b/core/java/android/view/translation/TranslationRequest.java @@ -27,7 +27,7 @@ import java.util.Collections; import java.util.List; /** - * Translation request sent to the {@link android.service.translation.TranslationService} by the + * Translation request sent to the translation service by the * {@link android.view.translation.Translator} which contains the text to be translated. */ @DataClass(genToString = true, genHiddenConstDefs = true, genBuilder = true) diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java index b77f2e282650..3362fc007ded 100644 --- a/core/java/android/view/translation/TranslationResponse.java +++ b/core/java/android/view/translation/TranslationResponse.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; -import android.service.translation.TranslationService; import android.util.SparseArray; import com.android.internal.util.DataClass; @@ -30,17 +29,17 @@ import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** - * Response from the {@link TranslationService}, which contains the translated result. + * Response from the translation service, which contains the translated result. */ @DataClass(genBuilder = true, genToString = true, genHiddenConstDefs = true) public final class TranslationResponse implements Parcelable { /** - * The {@link TranslationService} was successful in translating. + * The translation service was successful in translating. */ public static final int TRANSLATION_STATUS_SUCCESS = 0; /** - * The {@link TranslationService} returned unknown translation result. + * The translation service returned unknown translation result. */ public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; /** diff --git a/core/java/android/view/translation/TranslationResponseValue.java b/core/java/android/view/translation/TranslationResponseValue.java index 9dff2d56322b..18a240daffa8 100644 --- a/core/java/android/view/translation/TranslationResponseValue.java +++ b/core/java/android/view/translation/TranslationResponseValue.java @@ -27,7 +27,7 @@ import com.android.internal.util.DataClass; import java.util.Objects; /** - * A translated response value from {@link android.service.translation.TranslationService}. + * A translated response value from translation service. */ @DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true, genHiddenConstDefs = true) diff --git a/core/java/android/view/translation/ViewTranslationRequest.java b/core/java/android/view/translation/ViewTranslationRequest.java index a41749a2bc50..54b8ac2102c6 100644 --- a/core/java/android/view/translation/ViewTranslationRequest.java +++ b/core/java/android/view/translation/ViewTranslationRequest.java @@ -33,7 +33,7 @@ import java.util.Set; /** * Wrapper class representing a translation request associated with a {@link android.view.View} to - * be used by {@link android.service.translation.TranslationService}. + * be used by translation service. */ @DataClass(genBuilder = false, genToString = true, genEqualsHashCode = true, genGetters = false, genHiddenConstructor = true, genHiddenConstDefs = true) diff --git a/core/java/android/view/translation/ViewTranslationResponse.java b/core/java/android/view/translation/ViewTranslationResponse.java index d993114dba0a..134ff5a6b2c3 100644 --- a/core/java/android/view/translation/ViewTranslationResponse.java +++ b/core/java/android/view/translation/ViewTranslationResponse.java @@ -33,7 +33,7 @@ import java.util.Set; /** * Wrapper class representing a translation response associated with a {@link android.view.View} to - * be used by {@link android.service.translation.TranslationService}. + * be used by translation service. */ @DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true, genGetters = false) public final class ViewTranslationResponse implements Parcelable { 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/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/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java new file mode 100644 index 000000000000..e1a2b9485e13 --- /dev/null +++ b/core/java/android/window/SystemPerformanceHinter.java @@ -0,0 +1,331 @@ +/* + * 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.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/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..298eb2f14dc6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7232,6 +7232,13 @@ android:description="@string/permdesc_fullScreenIntent" android:protectionLevel="normal|appop" /> + <!-- Required for the assistant apps targeting {@link android.os.Build.VERSION_CODES#V} + that receive voice trigger from the trusted hotword detection service. + <p>Protection level: signature|privileged|appop + @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-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/config.xml b/core/res/res/values/config.xml index 3c296de780a2..7ef81abb8f0d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1151,6 +1151,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 +5402,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..643f4b148d74 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" /> @@ -4537,7 +4538,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..81dab0833af1 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -44,19 +44,21 @@ 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", @@ -147,7 +149,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/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java index d46f7625b596..4dd5889fdf5d 100644 --- a/core/tests/coretests/src/android/graphics/FontListParserTest.java +++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java @@ -28,6 +28,7 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.fail; import android.graphics.fonts.FontCustomizationParser; +import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontStyle; import android.os.LocaleList; import android.text.FontConfig; @@ -64,7 +65,8 @@ public final class FontListParserTest { Collections.singletonList(new FontConfig.FontFamily( Arrays.asList(new FontConfig.Font(new File("test.ttf"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", null)), - LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), "sans-serif"); + LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE)), "sans-serif"); FontConfig.NamedFamilyList family = readNamedFamily(xml); assertThat(family).isEqualTo(expected); } @@ -84,7 +86,8 @@ public final class FontListParserTest { new FontConfig.Font(new File("test.ttf"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", "serif")), - LocaleList.forLanguageTags("en"), VARIANT_DEFAULT); + LocaleList.forLanguageTags("en"), VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE); FontConfig.FontFamily family = readFamily(xml); assertThat(family).isEqualTo(expected); @@ -101,7 +104,8 @@ public final class FontListParserTest { new FontConfig.Font(new File("test.ttf"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", null)), - LocaleList.forLanguageTags("en"), VARIANT_COMPACT); + LocaleList.forLanguageTags("en"), VARIANT_COMPACT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE); FontConfig.FontFamily family = readFamily(xml); assertThat(family).isEqualTo(expected); @@ -118,7 +122,8 @@ public final class FontListParserTest { new FontConfig.Font(new File("test.ttf"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", null)), - LocaleList.forLanguageTags("en"), VARIANT_ELEGANT); + LocaleList.forLanguageTags("en"), VARIANT_ELEGANT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE); FontConfig.FontFamily family = readFamily(xml); assertThat(family).isEqualTo(expected); @@ -140,7 +145,8 @@ public final class FontListParserTest { new FontStyle(100, FONT_SLANT_UPRIGHT), 0, "", null), new FontConfig.Font(new File("italic.ttf"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC), 0, "", null)), - LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), "sans-serif"); + LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE)), "sans-serif"); FontConfig.NamedFamilyList family = readNamedFamily(xml); assertThat(family).isEqualTo(expected); } @@ -166,7 +172,8 @@ public final class FontListParserTest { new FontConfig.Font(new File("test-VF.ttf"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "'wdth' 400.0,'wght' 700.0", null)), - LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), + LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE)), "sans-serif"); FontConfig.NamedFamilyList family = readNamedFamily(xml); assertThat(family).isEqualTo(expected); @@ -187,7 +194,8 @@ public final class FontListParserTest { new FontConfig.Font(new File("test.ttc"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 1, "", null)), - LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), + LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE)), "sans-serif"); FontConfig.NamedFamilyList family = readNamedFamily(xml); assertThat(family).isEqualTo(expected); @@ -206,7 +214,8 @@ public final class FontListParserTest { new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 0, "", null), new FontConfig.Font(new File("test.ttc"), null, "test", new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), 1, "", null)), - LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT)), "sans-serif"); + LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE)), "sans-serif"); FontConfig.NamedFamilyList family = readNamedFamily(xml); assertThat(family).isEqualTo(expected); } @@ -372,6 +381,20 @@ public final class FontListParserTest { .isEqualTo("emoji.ttf"); } + @Test + public void varFamilyType() throws Exception { + String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='sans-serif' varFamilyType='1'>" + + " <font>test.ttf</font>" + + " </family>" + + "</familyset>"; + FontConfig config = readFamilies(xml, true /* include non-existing font files */); + List<FontConfig.FontFamily> families = config.getFontFamilies(); + assertThat(families.size()).isEqualTo(1); // legacy one should be ignored. + assertThat(families.get(0).getVariableFontFamilyType()).isEqualTo(1); + } + private FontConfig readFamilies(String xml, boolean allowNonExisting) throws IOException, XmlPullParserException { ByteArrayInputStream buffer = new ByteArrayInputStream( 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/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml index 1e97fcee250d..02e032b86443 100644 --- a/data/fonts/font_fallback.xml +++ b/data/fonts/font_fallback.xml @@ -15,96 +15,9 @@ --> <familyset version="23"> <!-- first font is default --> - <family name="sans-serif"> - <font weight="100" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> + <family name="sans-serif" varFamilyType="2"> + <font>Roboto-Regular.ttf <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> - </font> - <font weight="100" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> </font> </family> @@ -119,96 +32,9 @@ <alias name="tahoma" to="sans-serif" /> <alias name="verdana" to="sans-serif" /> - <family name="sans-serif-condensed"> - <font weight="100" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="900" /> - </font> - <font weight="100" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> + <family name="sans-serif-condensed" varFamilyType="2"> + <font>Roboto-Regular.ttf <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="900" /> </font> </family> <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" /> @@ -246,13 +72,8 @@ <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font> </family> - <family name="cursive"> - <font weight="400" style="normal">DancingScript-Regular.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="700" style="normal">DancingScript-Regular.ttf - <axis tag="wght" stylevalue="700" /> - </font> + <family name="cursive" varFamilyType="1"> + <font>DancingScript-Regular.ttf</font> </family> <family name="sans-serif-smallcaps"> @@ -269,96 +90,9 @@ </family> <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/> - <family name="roboto-flex"> - <font weight="100" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> - </font> - <font weight="100" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> + <family name="roboto-flex" varFamilyType="2"> + <font>RobotoFlex-Regular.ttf <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> </font> </family> @@ -375,38 +109,12 @@ </font> <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> </family> - <family lang="und-Ethi"> - <font weight="400" style="normal" postScriptName="NotoSansEthiopic-Regular"> - NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansEthiopic-Regular"> + <family lang="und-Ethi" varFamilyType="1"> + <font postScriptName="NotoSansEthiopic-Regular"> NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansEthiopic-Regular"> - NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" postScriptName="NotoSansEthiopic-Regular"> - NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifEthiopic-Regular"> + NotoSerifEthiopic-VF.ttf </font> </family> <family lang="und-Hebr"> @@ -432,124 +140,33 @@ </font> <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font> </family> - <family lang="und-Armn"> - <font weight="400" style="normal" postScriptName="NotoSansArmenian-Regular"> - NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansArmenian-Regular"> - NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansArmenian-Regular"> - NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansArmenian-Regular"> + <family lang="und-Armn" varFamilyType="1"> + <font postScriptName="NotoSansArmenian-Regular"> NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifArmenian-Regular"> + NotoSerifArmenian-VF.ttf </font> </family> - <family lang="und-Geor,und-Geok"> - <font weight="400" style="normal" postScriptName="NotoSansGeorgian-Regular"> + <family lang="und-Geor,und-Geok" varFamilyType="1"> + <font postScriptName="NotoSansGeorgian-Regular"> NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" postScriptName="NotoSansGeorgian-Regular"> - NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansGeorgian-Regular"> - NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansGeorgian-Regular"> - NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifGeorgian-Regular"> + NotoSerifGeorgian-VF.ttf </font> </family> - <family lang="und-Deva" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansDevanagari-Regular"> - NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansDevanagari-Regular"> - NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansDevanagari-Regular"> + <family lang="und-Deva" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansDevanagari-Regular"> NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" postScriptName="NotoSansDevanagari-Regular"> - NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifDevanagari-Regular"> + NotoSerifDevanagari-VF.ttf </font> </family> - <family lang="und-Deva" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> - NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> + <family lang="und-Deva" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansDevanagariUI-Regular"> NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> - NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> - NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> @@ -584,316 +201,82 @@ </font> <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font> </family> - <family lang="und-Guru" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansGurmukhi-Regular"> + <family lang="und-Guru" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansGurmukhi-Regular"> NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansGurmukhi-Regular"> - NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansGurmukhi-Regular"> - NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansGurmukhi-Regular"> - NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifGurmukhi-Regular"> + NotoSerifGurmukhi-VF.ttf </font> </family> - <family lang="und-Guru" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> - NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> - NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> - NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> + <family lang="und-Guru" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansGurmukhiUI-Regular"> NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Taml" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansTamil-Regular"> - NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTamil-Regular"> + <family lang="und-Taml" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansTamil-Regular"> NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" postScriptName="NotoSansTamil-Regular"> - NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTamil-Regular"> - NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifTamil-Regular"> + NotoSerifTamil-VF.ttf </font> </family> - <family lang="und-Taml" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansTamilUI-Regular"> - NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTamilUI-Regular"> - NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTamilUI-Regular"> + <family lang="und-Taml" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansTamilUI-Regular"> NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTamilUI-Regular"> - NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Mlym" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansMalayalam-Regular"> - NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansMalayalam-Regular"> + <family lang="und-Mlym" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansMalayalam-Regular"> NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" postScriptName="NotoSansMalayalam-Regular"> - NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansMalayalam-Regular"> - NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifMalayalam-Regular"> + NotoSerifMalayalam-VF.ttf </font> </family> - <family lang="und-Mlym" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> - NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> - NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> - NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> + <family lang="und-Mlym" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansMalayalamUI-Regular"> NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Beng" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansBengali-Regular"> - NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansBengali-Regular"> + <family lang="und-Beng" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansBengali-Regular"> NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" postScriptName="NotoSansBengali-Regular"> - NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansBengali-Regular"> - NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular"> + NotoSerifBengali-VF.ttf </font> </family> - <family lang="und-Beng" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansBengaliUI-Regular"> - NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansBengaliUI-Regular"> - NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansBengaliUI-Regular"> - NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansBengaliUI-Regular"> + <family lang="und-Beng" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansBengaliUI-Regular"> NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Telu" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansTelugu-Regular"> - NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTelugu-Regular"> + <family lang="und-Telu" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansTelugu-Regular"> NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTelugu-Regular"> - NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTelugu-Regular"> - NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifTelugu-Regular"> + NotoSerifTelugu-VF.ttf </font> </family> - <family lang="und-Telu" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansTeluguUI-Regular"> - NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTeluguUI-Regular"> - NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTeluguUI-Regular"> + <family lang="und-Telu" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansTeluguUI-Regular"> NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTeluguUI-Regular"> - NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Knda" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansKannada-Regular"> + <family lang="und-Knda" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansKannada-Regular"> NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansKannada-Regular"> - NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansKannada-Regular"> - NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansKannada-Regular"> - NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifKannada-Regular"> + NotoSerifKannada-VF.ttf </font> </family> - <family lang="und-Knda" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansKannadaUI-Regular"> - NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansKannadaUI-Regular"> + <family lang="und-Knda" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansKannadaUI-Regular"> NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansKannadaUI-Regular"> - NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansKannadaUI-Regular"> - NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Orya" variant="elegant"> @@ -907,56 +290,17 @@ </font> <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> </family> - <family lang="und-Sinh" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansSinhala-Regular"> + <family lang="und-Sinh" variant="elegant" varFamilyType="1"> + <font postScriptName="NotoSansSinhala-Regular"> NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" postScriptName="NotoSansSinhala-Regular"> - NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansSinhala-Regular"> - NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansSinhala-Regular"> - NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="700"/> + <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular"> + NotoSerifSinhala-VF.ttf </font> </family> - <family lang="und-Sinh" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> - NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> - NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> - NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> + <family lang="und-Sinh" variant="compact" varFamilyType="1"> + <font postScriptName="NotoSansSinhalaUI-Regular"> NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Khmr" variant="elegant"> @@ -1054,22 +398,9 @@ <family lang="und-Ahom"> <font weight="400" style="normal">NotoSansAhom-Regular.otf</font> </family> - <family lang="und-Adlm"> - <font weight="400" style="normal" postScriptName="NotoSansAdlam-Regular"> - NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansAdlam-Regular"> + <family lang="und-Adlm" varFamilyType="1"> + <font postScriptName="NotoSansAdlam-Regular"> NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansAdlam-Regular"> - NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansAdlam-Regular"> - NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Avst"> @@ -1355,22 +686,9 @@ NotoSansTaiViet-Regular.ttf </font> </family> - <family lang="und-Tibt"> - <font weight="400" style="normal" postScriptName="NotoSerifTibetan-Regular"> + <family lang="und-Tibt" varFamilyType="1"> + <font postScriptName="NotoSerifTibetan-Regular"> NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSerifTibetan-Regular"> - NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSerifTibetan-Regular"> - NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSerifTibetan-Regular"> - NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Tfng"> @@ -1537,94 +855,29 @@ <family lang="und-Dogr"> <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> </family> - <family lang="und-Medf"> - <font weight="400" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> - NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> - NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> - NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> + <family lang="und-Medf" varFamilyType="1"> + <font postScriptName="NotoSansMedefaidrin-Regular"> NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Soyo"> - <font weight="400" style="normal" postScriptName="NotoSansSoyombo-Regular"> - NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansSoyombo-Regular"> + <family lang="und-Soyo" varFamilyType="1"> + <font postScriptName="NotoSansSoyombo-Regular"> NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansSoyombo-Regular"> - NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansSoyombo-Regular"> - NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Takr"> - <font weight="400" style="normal" postScriptName="NotoSansTakri-Regular"> + <family lang="und-Takr" varFamilyType="1"> + <font postScriptName="NotoSansTakri-Regular"> NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTakri-Regular"> - NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTakri-Regular"> - NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTakri-Regular"> - NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Hmnp"> - <font weight="400" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> - NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> - NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> - NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> + <family lang="und-Hmnp" varFamilyType="1"> + <font postScriptName="NotoSerifHmongNyiakeng-Regular"> NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> - <family lang="und-Yezi"> - <font weight="400" style="normal" postScriptName="NotoSerifYezidi-Regular"> - NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSerifYezidi-Regular"> + <family lang="und-Yezi" varFamilyType="1"> + <font postScriptName="NotoSerifYezidi-Regular"> NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSerifYezidi-Regular"> - NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSerifYezidi-Regular"> - NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="700"/> </font> </family> </familyset> diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl index 354f10a9432a..87cb942602f7 100644 --- a/data/keyboards/Vendor_0957_Product_0001.kl +++ b/data/keyboards/Vendor_0957_Product_0001.kl @@ -47,7 +47,6 @@ key usage 0x00070037 PERIOD # custom keys key usage 0x000c01BB TV_INPUT -key usage 0x000c0186 MACRO_1 WAKE key usage 0x000c0185 TV_TELETEXT key usage 0x000c0061 CAPTIONS 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/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 1ff5a3d46f8e..250362b1e1e3 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -479,7 +479,8 @@ public final class Bitmap implements Parcelable { * This configuration may be useful when using opaque bitmaps * that do not require high color fidelity. * - * <p>Use this formula to pack into 16 bits:</p> + * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer, + * use this formula to pack into 16 bits:</p> * <pre class="prettyprint"> * short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f); * </pre> @@ -516,7 +517,8 @@ public final class Bitmap implements Parcelable { * This configuration is very flexible and offers the best * quality. It should be used whenever possible. * - * <p>Use this formula to pack into 32 bits:</p> + * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer, + * use this formula to pack into 32 bits:</p> * <pre class="prettyprint"> * int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff); * </pre> @@ -531,7 +533,8 @@ public final class Bitmap implements Parcelable { * This configuration is particularly suited for wide-gamut and * HDR content. * - * <p>Use this formula to pack into 64 bits:</p> + * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer, + * use this formula to pack into 64 bits:</p> * <pre class="prettyprint"> * long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff); * </pre> @@ -556,7 +559,8 @@ public final class Bitmap implements Parcelable { * blending, such that the memory cost is the same as ARGB_8888 while enabling higher color * precision. * - * <p>Use this formula to pack into 32 bits:</p> + * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer, + * use this formula to pack into 32 bits:</p> * <pre class="prettyprint"> * int color = (A & 0x3) << 30 | (B & 0x3ff) << 20 | (G & 0x3ff) << 10 | (R & 0x3ff); * </pre> diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java index 5c065775eea2..dcfff62459ab 100644 --- a/graphics/java/android/graphics/BitmapShader.java +++ b/graphics/java/android/graphics/BitmapShader.java @@ -16,9 +16,13 @@ package android.graphics; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.graphics.hwui.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,6 +36,7 @@ public class BitmapShader extends Shader { * Prevent garbage collection. */ /*package*/ Bitmap mBitmap; + private Gainmap mOverrideGainmap; private int mTileX; private int mTileY; @@ -173,6 +178,24 @@ public class BitmapShader extends Shader { } /** + * Draws the BitmapShader with a copy of the given gainmap instead of the gainmap on the Bitmap + * the shader was constructed from + * + * @param overrideGainmap The gainmap to draw instead, null to use any gainmap on the Bitmap + */ + @FlaggedApi(Flags.FLAG_GAINMAP_ANIMATIONS) + public void setOverrideGainmap(@Nullable Gainmap overrideGainmap) { + if (!Flags.gainmapAnimations()) throw new IllegalStateException("API not available"); + + if (overrideGainmap == null) { + mOverrideGainmap = null; + } else { + mOverrideGainmap = new Gainmap(overrideGainmap, overrideGainmap.getGainmapContents()); + } + discardNativeInstance(); + } + + /** * Returns the current max anisotropic filtering value configured by * {@link #setFilterMode(int)}. If {@link #setFilterMode(int)} is invoked this returns zero. */ @@ -199,14 +222,9 @@ public class BitmapShader extends Shader { mIsDirectSampled = mRequestDirectSampling; mRequestDirectSampling = false; - - if (mMaxAniso > 0) { - return nativeCreateWithMaxAniso(nativeMatrix, mBitmap.getNativeInstance(), mTileX, - mTileY, mMaxAniso, mIsDirectSampled); - } else { - return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, mTileY, - enableLinearFilter, mIsDirectSampled); - } + return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, + mTileY, mMaxAniso, enableLinearFilter, mIsDirectSampled, + mOverrideGainmap != null ? mOverrideGainmap.mNativePtr : 0); } /** @hide */ @@ -217,9 +235,7 @@ public class BitmapShader extends Shader { } private static native long nativeCreate(long nativeMatrix, long bitmapHandle, - int shaderTileModeX, int shaderTileModeY, boolean filter, boolean isDirectSampled); - - private static native long nativeCreateWithMaxAniso(long nativeMatrix, long bitmapHandle, - int shaderTileModeX, int shaderTileModeY, int maxAniso, boolean isDirectSampled); + int shaderTileModeX, int shaderTileModeY, int maxAniso, boolean filter, + boolean isDirectSampled, long overrideGainmapHandle); } 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 674246acafef..735bc180c015 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -16,6 +16,10 @@ package android.graphics; +import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE; +import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL; +import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY; +import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT; import static android.text.FontConfig.NamedFamilyList; import android.annotation.NonNull; @@ -28,6 +32,7 @@ import android.os.Build; import android.os.LocaleList; import android.text.FontConfig; import android.util.ArraySet; +import android.util.Log; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; @@ -256,6 +261,7 @@ public class FontListParser { final String lang = parser.getAttributeValue("", "lang"); final String variant = parser.getAttributeValue(null, "variant"); final String ignore = parser.getAttributeValue(null, "ignore"); + final String varFamilyTypeStr = parser.getAttributeValue(null, "varFamilyType"); final List<FontConfig.Font> fonts = new ArrayList<>(); while (keepReading(parser)) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; @@ -278,12 +284,45 @@ public class FontListParser { intVariant = FontConfig.FontFamily.VARIANT_ELEGANT; } } + int varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE; + if (varFamilyTypeStr != null) { + varFamilyType = Integer.parseInt(varFamilyTypeStr); + if (varFamilyType <= -1 || varFamilyType > 3) { + Log.e(TAG, "Error: unexpected varFamilyType value: " + varFamilyTypeStr); + varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE; + } + + // validation but don't read font content for performance reasons. + switch (varFamilyType) { + case VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY: + if (fonts.size() != 1) { + Log.e(TAG, "Error: Single font support wght axis, but two or more fonts are" + + " included in the font family."); + varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE; + } + break; + case VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL: + if (fonts.size() != 1) { + Log.e(TAG, "Error: Single font support both ital and wght axes, but two or" + + " more fonts are included in the font family."); + varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE; + } + break; + case VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT: + if (fonts.size() != 2) { + Log.e(TAG, "Error: two fonts that support wght axis, but one or three or" + + " more fonts are included in the font family."); + varFamilyType = VARIABLE_FONT_FAMILY_TYPE_NONE; + } + } + } boolean skip = (ignore != null && (ignore.equals("true") || ignore.equals("1"))); if (skip || fonts.isEmpty()) { return null; } - return new FontConfig.FontFamily(fonts, LocaleList.forLanguageTags(lang), intVariant); + return new FontConfig.FontFamily(fonts, LocaleList.forLanguageTags(lang), intVariant, + varFamilyType); } private static void throwIfAttributeExists(String attrName, XmlPullParser parser) { diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index 5e4110590325..4c753565eb5b 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -18,7 +18,10 @@ package android.graphics.fonts; import static com.android.text.flags.Flags.FLAG_DEPRECATE_FONTS_XML; +import static java.lang.annotation.RetentionPolicy.SOURCE; + import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,6 +35,7 @@ import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; +import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.Set; @@ -184,32 +188,59 @@ public final class FontFamily { } /** + * A special variable font family type that indicates `analyzeAndResolveVariableType` could + * not be identified the variable font family type. + * * @see #buildVariableFamily() * @hide */ public static final int VARIABLE_FONT_FAMILY_TYPE_UNKNOWN = -1; /** + * A variable font family type that indicates no variable font family can be used. + * + * The font family is used as bundle of static fonts. * @see #buildVariableFamily() * @hide */ public static final int VARIABLE_FONT_FAMILY_TYPE_NONE = 0; /** + * A variable font family type that indicates single font file can be used for multiple + * weight. For the italic style, fake italic may be applied. + * * @see #buildVariableFamily() * @hide */ public static final int VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY = 1; /** + * A variable font family type that indicates single font file can be used for multiple + * weight and italic. + * * @see #buildVariableFamily() * @hide */ public static final int VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL = 2; /** + * A variable font family type that indicates two font files are included in the family: + * one can be used for upright with various weights, the other one can be used for italic + * with various weights. + * * @see #buildVariableFamily() * @hide */ public static final int VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT = 3; + /** @hide */ + @Retention(SOURCE) + @IntDef(prefix = { "VARIABLE_FONT_FAMILY_TYPE_" }, value = { + VARIABLE_FONT_FAMILY_TYPE_UNKNOWN, + VARIABLE_FONT_FAMILY_TYPE_NONE, + VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY, + VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL, + VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT + }) + public @interface VariableFontFamilyType {} + /** * The registered italic axis used for adjusting requested style. * https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_ital @@ -222,7 +253,9 @@ public final class FontFamily { */ private static final int TAG_wght = 0x77676874; // w(0x77), g(0x67), h(0x68), t(0x74) - private static int analyzeAndResolveVariableType(ArrayList<Font> fonts) { + /** @hide */ + public static @VariableFontFamilyType int analyzeAndResolveVariableType( + ArrayList<Font> fonts) { if (fonts.size() > 2) { return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN; } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 9810022abfed..d4e35b30c8d0 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -121,7 +121,8 @@ public final class SystemFonts { final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - defaultFonts, languageTags, variant, false, cache); + defaultFonts, languageTags, variant, xmlFamily.getVariableFontFamilyType(), false, + cache); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { final String name = fallbackMap.keyAt(i); @@ -138,8 +139,8 @@ public final class SystemFonts { familyListSet.familyList.add(defaultFamily); } } else { - final FontFamily family = createFontFamily(fallback, languageTags, variant, false, - cache); + final FontFamily family = createFontFamily(fallback, languageTags, variant, + xmlFamily.getVariableFontFamilyType(), false, cache); if (family != null) { familyListSet.familyList.add(family); } else if (defaultFamily != null) { @@ -155,6 +156,7 @@ public final class SystemFonts { @NonNull List<FontConfig.Font> fonts, @NonNull String languageTags, @FontConfig.FontFamily.Variant int variant, + int varFamilyType, boolean isDefaultFallback, @NonNull Map<String, ByteBuffer> cache) { if (fonts.size() == 0) { @@ -196,7 +198,7 @@ public final class SystemFonts { } } return b == null ? null : b.build(languageTags, variant, false /* isCustomFallback */, - isDefaultFallback, FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE); + isDefaultFallback, varFamilyType); } private static void appendNamedFamilyList(@NonNull FontConfig.NamedFamilyList namedFamilyList, @@ -210,6 +212,7 @@ public final class SystemFonts { final FontFamily family = createFontFamily( xmlFamily.getFontList(), xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getVariant(), + xmlFamily.getVariableFontFamilyType(), true, // named family is always default bufferCache); if (family == null) { @@ -291,6 +294,7 @@ public final class SystemFonts { int configVersion ) { try { + Log.i(TAG, "Loading font config from " + fontsXml); return FontListParser.parse(fontsXml, systemFontDir, oemXml, productFontDir, updatableFontMap, lastModifiedDate, configVersion); } catch (IOException e) { 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/bubbles/properties/ProdBubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt index 67dc642a2deb..e1dea3babbc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt @@ -25,7 +25,8 @@ object ProdBubbleProperties : BubbleProperties { private var _isBubbleBarEnabled = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false) - override val isBubbleBarEnabled = _isBubbleBarEnabled + override val isBubbleBarEnabled + get() = _isBubbleBarEnabled override fun refresh() { _isBubbleBarEnabled = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false) 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/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 26b5a5052594..63cdb4f151ff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -67,6 +67,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.InteractionJankMonitorUtils; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; @@ -484,7 +485,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** Updates divide position and split bounds base on the ratio within root bounds. */ - public void setDivideRatio(@SnapPosition int snapPosition) { + public void setDivideRatio(@PersistentSnapPosition int snapPosition) { final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget( snapPosition); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java index ff38b7e70410..e73430056c89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java @@ -26,6 +26,16 @@ import android.annotation.IntDef; /** Helper utility class of methods and constants that are available to be imported in Launcher. */ public class SplitScreenConstants { + /** + * Duration used for every split fade-in or fade-out. + */ + public static final int FADE_DURATION = 133; + + /////////////// + // IMPORTANT for the following SPLIT_POSITION and SNAP_TO constants: + // These int values must not be changed -- they are persisted to user-defined app pairs, and + // will break things if changed. + // /** * Split position isn't specified normally meaning to use what ever it is currently set to. @@ -44,11 +54,6 @@ public class SplitScreenConstants { */ public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1; - /** - * Duration used for every split fade-in or fade-out. - */ - public static final int FADE_DURATION = 133; - @IntDef(prefix = {"SPLIT_POSITION_"}, value = { SPLIT_POSITION_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT, @@ -57,38 +62,61 @@ public class SplitScreenConstants { public @interface SplitPosition { } - /** The divider doesn't snap to any target and is freely placeable. */ - public static final int SNAP_TO_NONE = 0; - - /** A snap target positioned near the screen edge for a minimized task */ - public static final int SNAP_TO_MINIMIZE = 1; - - /** If the divider reaches this value, the left/top task should be dismissed. */ - public static final int SNAP_TO_START_AND_DISMISS = 2; - /** A snap target in the first half of the screen, where the split is roughly 30-70. */ - public static final int SNAP_TO_30_70 = 3; + public static final int SNAP_TO_30_70 = 0; /** The 50-50 snap target */ - public static final int SNAP_TO_50_50 = 4; + public static final int SNAP_TO_50_50 = 1; /** A snap target in the latter half of the screen, where the split is roughly 70-30. */ - public static final int SNAP_TO_70_30 = 5; + public static final int SNAP_TO_70_30 = 2; + + /** + * These snap targets are used for split pairs in a stable, non-transient state. They may be + * persisted in Launcher when the user saves an app pair. They are a subset of + * {@link SnapPosition}. + */ + @IntDef(prefix = { "SNAP_TO_" }, value = { + SNAP_TO_30_70, + SNAP_TO_50_50, + SNAP_TO_70_30 + }) + public @interface PersistentSnapPosition {} + + /** + * Checks if the snapPosition in question is a {@link PersistentSnapPosition}. + */ + public static boolean isPersistentSnapPosition(@SnapPosition int snapPosition) { + return snapPosition == SNAP_TO_30_70 + || snapPosition == SNAP_TO_50_50 + || snapPosition == SNAP_TO_70_30; + } + + /** The divider doesn't snap to any target and is freely placeable. */ + public static final int SNAP_TO_NONE = 10; + + /** If the divider reaches this value, the left/top task should be dismissed. */ + public static final int SNAP_TO_START_AND_DISMISS = 11; /** If the divider reaches this value, the right/bottom task should be dismissed. */ - public static final int SNAP_TO_END_AND_DISMISS = 6; + public static final int SNAP_TO_END_AND_DISMISS = 12; + + /** A snap target positioned near the screen edge for a minimized task */ + public static final int SNAP_TO_MINIMIZE = 13; @IntDef(prefix = { "SNAP_TO_" }, value = { - SNAP_TO_NONE, - SNAP_TO_MINIMIZE, - SNAP_TO_START_AND_DISMISS, SNAP_TO_30_70, SNAP_TO_50_50, SNAP_TO_70_30, - SNAP_TO_END_AND_DISMISS + SNAP_TO_NONE, + SNAP_TO_START_AND_DISMISS, + SNAP_TO_END_AND_DISMISS, + SNAP_TO_MINIMIZE }) public @interface SnapPosition {} + /////////////// + public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD}; public static final int[] CONTROLLED_WINDOWING_MODES = {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}; 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 9f9854e7e244..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 @@ -199,6 +199,7 @@ public abstract class WMShellModule { @ShellMainThread Handler mainHandler, @ShellMainThread Choreographer mainChoreographer, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, @@ -213,6 +214,7 @@ public abstract class WMShellModule { mainHandler, mainChoreographer, shellInit, + shellCommandHandler, taskOrganizer, displayController, shellController, @@ -492,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/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index ccffa02a22c1..664d44910e72 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -23,6 +23,7 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.RemoteAnimationTarget.MODE_OPENING; + import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -85,7 +86,7 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -601,7 +602,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { if (options1 == null) options1 = new Bundle(); final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1); @@ -632,7 +633,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { if (options1 == null) options1 = new Bundle(); final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1); @@ -675,7 +676,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { Intent fillInIntent = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent); @@ -702,7 +703,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private void startIntentAndTask(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { Intent fillInIntent = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent); @@ -736,7 +737,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { Intent fillInIntent1 = null; Intent fillInIntent2 = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1); @@ -767,7 +769,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { Intent fillInIntent1 = null; Intent fillInIntent2 = null; @@ -1225,7 +1227,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startTasks", (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition( @@ -1236,7 +1238,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, Bundle options1, int taskId, Bundle options2, int splitPosition, - @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntentAndTaskWithLegacyTransition", (controller) -> @@ -1248,7 +1250,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startShortcutAndTaskWithLegacyTransition", (controller) -> @@ -1260,8 +1262,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, - InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startTasks", (controller) -> controller.mStageCoordinator.startTasks(taskId1, options1, taskId2, options2, splitPosition, snapPosition, remoteTransition, @@ -1271,7 +1273,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startIntentAndTask(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntentAndTask", (controller) -> controller.startIntentAndTask(pendingIntent, userId1, options1, @@ -1282,8 +1284,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, - InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask", (controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId, options2, splitPosition, snapPosition, remoteTransition, instanceId)); @@ -1294,7 +1296,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition", (controller) -> @@ -1309,8 +1311,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, - InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntents", (controller) -> controller.startIntents(pendingIntent1, userId1, shortcutInfo1, 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 4ea14f473c39..5e2c61b9d3cd 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 @@ -128,7 +128,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; -import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.common.split.SplitWindowManager; @@ -633,7 +633,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Starts 2 tasks in one transition. */ void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId2 == INVALID_TASK_ID) { @@ -661,7 +661,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Start an intent and a task to a split pair in one transition. */ void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { @@ -683,7 +683,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Starts a shortcut and a task to a split pair in one transition. */ void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { @@ -710,7 +710,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)} */ private void startWithTask(WindowContainerTransaction wct, int mainTaskId, - @Nullable Bundle mainOptions, @SnapPosition int snapPosition, + @Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { if (!mMainStage.isActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage @@ -744,7 +744,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, Intent fillInIntent2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (pendingIntent2 == null) { @@ -796,7 +796,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Starts a pair of tasks using legacy transition. */ void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, - @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); if (taskId2 == INVALID_TASK_ID) { @@ -826,7 +827,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, @Nullable PendingIntent pendingIntent2, Intent fillInIntent2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); @@ -851,7 +852,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); @@ -872,7 +873,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Starts a pair of shortcut and task using legacy transition. */ void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, @SnapPosition int snapPosition, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); @@ -934,7 +935,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startWithLegacyTransition(WindowContainerTransaction wct, @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent, @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions, - @SplitPosition int sidePosition, @SnapPosition int snapPosition, + @SplitPosition int sidePosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { startWithLegacyTransition(wct, INVALID_TASK_ID, mainPendingIntent, mainFillInIntent, mainShortcutInfo, mainOptions, sidePosition, snapPosition, adapter, instanceId); @@ -942,7 +943,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId, @Nullable Bundle mainOptions, @SplitPosition int sidePosition, - @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { startWithLegacyTransition(wct, mainTaskId, null /* mainPendingIntent */, null /* mainFillInIntent */, null /* mainShortcutInfo */, mainOptions, sidePosition, @@ -957,7 +958,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId, @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent, @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle options, - @SplitPosition int sidePosition, @SnapPosition int snapPosition, + @SplitPosition int sidePosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) { if (!isSplitScreenVisible()) { exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT); 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/util/SplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java index a68b41d6563a..3e06d2d0e797 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java @@ -19,7 +19,7 @@ import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; -import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import java.util.Objects; @@ -39,7 +39,7 @@ public class SplitBounds implements Parcelable { public final float leftTaskPercent; public final float dividerWidthPercent; public final float dividerHeightPercent; - public final @SnapPosition int snapPosition; + public final @PersistentSnapPosition int snapPosition; /** * If {@code true}, that means at the time of creation of this object, the * split-screened apps were vertically stacked. This is useful in scenarios like @@ -51,7 +51,7 @@ public class SplitBounds implements Parcelable { public final int rightBottomTaskId; public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, - int rightBottomTaskId, @SnapPosition int snapPosition) { + int rightBottomTaskId, @PersistentSnapPosition int snapPosition) { this.leftTopBounds = leftTopBounds; this.rightBottomBounds = rightBottomBounds; this.leftTopTaskId = leftTopTaskId; 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 afa2754803f1..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 @@ -79,11 +79,13 @@ import com.android.wm.shell.recents.RecentsTransitionStateListener; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.KeyguardChangeListener; +import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; +import java.io.PrintWriter; import java.util.Optional; import java.util.function.Supplier; @@ -97,6 +99,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory; private final ActivityTaskManager mActivityTaskManager; + private final ShellCommandHandler mShellCommandHandler; private final ShellTaskOrganizer mTaskOrganizer; private final ShellController mShellController; private final Context mContext; @@ -134,6 +137,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, @@ -148,6 +152,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mainHandler, mainChoreographer, shellInit, + shellCommandHandler, taskOrganizer, displayController, shellController, @@ -167,6 +172,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, @@ -189,7 +195,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTransitions = transitions; mDesktopTasksController = desktopTasksController; mRecentsTransitionHandler = recentsTransitionHandler; - + mShellCommandHandler = shellCommandHandler; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; mTransactionFactory = transactionFactory; @@ -206,6 +212,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { onRecentsTransitionStarted(transition); } }); + mShellCommandHandler.addDumpCallback(this::dump, this); } @Override @@ -369,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); @@ -593,6 +600,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { super.dispose(); } + @Override + public String toString() { + return "EventReceiver" + + "{" + + "tasksOnDisplay=" + + mTasksOnDisplay + + "}"; + } + private void incrementTaskNumber() { mTasksOnDisplay++; } @@ -981,6 +997,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && mSplitScreenController.isTaskInSplitScreen(taskId); } + private void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + "DesktopModeWindowDecorViewModel"); + pw.println(innerPrefix + "DesktopModeStatus=" + DesktopModeStatus.isEnabled()); + pw.println(innerPrefix + "mTransitionDragActive=" + mTransitionDragActive); + pw.println(innerPrefix + "mEventReceiversByDisplay=" + mEventReceiversByDisplay); + pw.println(innerPrefix + "mWindowDecorByTaskId=" + mWindowDecorByTaskId); + } + private class DragStartListenerImpl implements DragPositioningCallbackUtility.DragStartListener { @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 84ec6b389c4a..380b59e84485 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.windowingModeToString; import android.app.ActivityManager; import android.app.WindowConfiguration.WindowingMode; @@ -661,6 +662,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mRelayoutBlock++; } + @Override + public String toString() { + return "{" + + "mPositionInParent=" + mPositionInParent + ", " + + "mRelayoutBlock=" + mRelayoutBlock + ", " + + "taskId=" + mTaskInfo.taskId + ", " + + "windowingMode=" + windowingModeToString(mTaskInfo.getWindowingMode()) + ", " + + "isFocused=" + isFocused() + + "}"; + } + static class Factory { DesktopModeWindowDecoration create( 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/common/split/SplitScreenConstantsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt new file mode 100644 index 000000000000..fe261107d65b --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SplitScreenConstantsTest { + + /** + * Ensures that some important constants are not changed from their set values. These values + * are persisted in user-defined app pairs, and changing them will break things. + */ + @Test + fun shouldKeepExistingConstantValues() { + assertEquals( + "the value of SPLIT_POSITION_TOP_OR_LEFT should be 0", + 0, + SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT, + ) + assertEquals( + "the value of SPLIT_POSITION_BOTTOM_OR_RIGHT should be 1", + 1, + SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT, + ) + assertEquals( + "the value of SNAP_TO_30_70 should be 0", + 0, + SplitScreenConstants.SNAP_TO_30_70, + ) + assertEquals( + "the value of SNAP_TO_50_50 should be 1", + 1, + SplitScreenConstants.SNAP_TO_50_50, + ) + assertEquals( + "the value of SNAP_TO_70_30 should be 2", + 2, + SplitScreenConstants.SNAP_TO_70_30, + ) + } +}
\ No newline at end of file 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/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 00d70a75837b..8eaf5a004c0a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -49,6 +49,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController import com.android.wm.shell.recents.RecentsTransitionHandler import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.sysui.KeyguardChangeListener +import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions @@ -89,6 +90,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockShellExecutor: ShellExecutor @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler + @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler private val transactionFactory = Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() @@ -105,6 +107,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockMainHandler, mockMainChoreographer, shellInit, + mockShellCommandHandler, mockTaskOrganizer, mockDisplayController, mockShellController, 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/Shader.cpp b/libs/hwui/jni/Shader.cpp index 2c13ceb77b52..a952be020855 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -65,21 +65,41 @@ static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref)); } -static jlong createBitmapShaderHelper(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, - jint tileModeX, jint tileModeY, bool isDirectSampled, - const SkSamplingOptions& sampling) { +/////////////////////////////////////////////////////////////////////////////////////////////// + +static SkGainmapInfo sNoOpGainmap = { + .fGainmapRatioMin = {1.f, 1.f, 1.f, 1.0}, + .fGainmapRatioMax = {1.f, 1.f, 1.f, 1.0}, + .fGainmapGamma = {1.f, 1.f, 1.f, 1.f}, + .fEpsilonSdr = {0.f, 0.f, 0.f, 1.0}, + .fEpsilonHdr = {0.f, 0.f, 0.f, 1.0}, + .fDisplayRatioSdr = 1.f, + .fDisplayRatioHdr = 1.f, +}; + +static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, + jint tileModeX, jint tileModeY, jint maxAniso, bool filter, + bool isDirectSampled, jlong overrideGainmapPtr) { + SkSamplingOptions sampling = maxAniso > 0 ? SkSamplingOptions::Aniso(static_cast<int>(maxAniso)) + : SkSamplingOptions(filter ? SkFilterMode::kLinear + : SkFilterMode::kNearest, + SkMipmapMode::kNone); const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + const Gainmap* gainmap = reinterpret_cast<Gainmap*>(overrideGainmapPtr); sk_sp<SkImage> image; if (bitmapHandle) { // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise, // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. auto& bitmap = android::bitmap::toBitmap(bitmapHandle); image = bitmap.makeImage(); + if (!gainmap && bitmap.hasGainmap()) { + gainmap = bitmap.gainmap().get(); + } - if (!isDirectSampled && bitmap.hasGainmap()) { - sk_sp<SkShader> gainmapShader = MakeGainmapShader( - image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info, - (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling); + if (!isDirectSampled && gainmap && gainmap->info != sNoOpGainmap) { + sk_sp<SkShader> gainmapShader = + MakeGainmapShader(image, gainmap->bitmap->makeImage(), gainmap->info, + (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling); if (gainmapShader) { if (matrix) { gainmapShader = gainmapShader->makeWithLocalMatrix(*matrix); @@ -111,26 +131,6 @@ static jlong createBitmapShaderHelper(JNIEnv* env, jobject o, jlong matrixPtr, j /////////////////////////////////////////////////////////////////////////////////////////////// -static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, - jint tileModeX, jint tileModeY, bool filter, - bool isDirectSampled) { - SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest, - SkMipmapMode::kNone); - return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY, - isDirectSampled, sampling); -} - -static jlong BitmapShader_constructorWithMaxAniso(JNIEnv* env, jobject o, jlong matrixPtr, - jlong bitmapHandle, jint tileModeX, - jint tileModeY, jint maxAniso, - bool isDirectSampled) { - auto sampling = SkSamplingOptions::Aniso(static_cast<int>(maxAniso)); - return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY, - isDirectSampled, sampling); -} - -/////////////////////////////////////////////////////////////////////////////////////////////// - static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) { const size_t count = env->GetArrayLength(colorArray); const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr); @@ -419,8 +419,7 @@ static const JNINativeMethod gShaderMethods[] = { }; static const JNINativeMethod gBitmapShaderMethods[] = { - {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor}, - {"nativeCreateWithMaxAniso", "(JJIIIZ)J", (void*)BitmapShader_constructorWithMaxAniso}, + {"nativeCreate", "(JJIIIZZJ)J", (void*)BitmapShader_constructor}, }; 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/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/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/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index ba88484518aa..2318bb95dabb 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -18,21 +18,23 @@ 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.FillCallback -import android.service.autofill.SaveRequest +import android.service.autofill.FillRequest +import android.service.autofill.FillResponse 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.JSONObject import java.util.concurrent.Executors @@ -129,27 +131,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,12 +165,14 @@ class CredentialAutofillService : AutofillService() { val credentialOptions: MutableList<CredentialOption> = mutableListOf() for (credentialHint in credentialHints) { - convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) } + convertJsonToCredentialOption(credentialHint, autofillId) + .let { credentialOptions.addAll(it) } } 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() @@ -173,11 +181,14 @@ class CredentialAutofillService : AutofillService() { val options = json.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/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/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/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/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/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 91d2d1bb58e5..ba4ad365e1b3 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -218,6 +218,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, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index bec144766438..19fde758da5d 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -308,6 +308,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); diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 17ce7c7cc435..29f27f74bca4 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -120,6 +120,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); 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..34d3d446530b 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2054,12 +2054,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; } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index c0f62313cb39..9ddc976af7e2 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -101,6 +101,7 @@ public class SettingsBackupTest { 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, 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/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..18117a890298 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -13,3 +13,11 @@ 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" +} 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/packages/SystemUI/customization/res/values-en-rAU/strings.xml b/packages/SystemUI/customization/res/values-en-rAU/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rAU/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-rCA/strings.xml b/packages/SystemUI/customization/res/values-en-rCA/strings.xml new file mode 100644 index 000000000000..79919c07d189 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rCA/strings.xml @@ -0,0 +1,23 @@ +<?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"> + <!-- 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/drawable/bluetooth_tile_dialog_bg_off.xml b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off.xml new file mode 100644 index 000000000000..d4916b47158c --- /dev/null +++ b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <solid android:color="@android:color/white"/> + <corners android:radius="@dimen/settingslib_switch_bar_radius"/> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off_busy.xml b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off_busy.xml new file mode 100644 index 000000000000..7bc120ee8aad --- /dev/null +++ b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off_busy.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. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@android:id/background"> + <shape> + <solid android:color="?android:attr/colorControlHighlight" /> + <corners android:radius="@dimen/settingslib_switch_bar_radius"/> + </shape> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml index 6dd44fbe645d..1c7e9977afe5 100644 --- a/packages/SystemUI/res/layout/bluetooth_device_item.xml +++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml @@ -14,81 +14,74 @@ ~ limitations under the License. --> -<!-- TODO(b/298124674) remove this root --> -<LinearLayout 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/bluetooth_device_list_container" + android:id="@+id/bluetooth_device_row" + style="@style/BluetoothTileDialog.Device" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" + android:layout_height="@dimen/bluetooth_dialog_device_height" + android:paddingEnd="24dp" + android:paddingStart="20dp" android:layout_marginBottom="4dp"> - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/bluetooth_device_row" - style="@style/BluetoothTileDialog.Device" - android:layout_height="@dimen/bluetooth_dialog_device_height" - android:paddingEnd="24dp" - android:paddingStart="20dp" - android:baselineAligned="false"> - - <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" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - android:layout_gravity="center_vertical" /> + <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" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:layout_gravity="center_vertical" /> - <View - android:id="@+id/bluetooth_device" - android:layout_width="0dp" - android:layout_height="0dp" - app:layout_constraintTop_toTopOf="@+id/bluetooth_device_name" - app:layout_constraintBottom_toBottomOf="@+id/bluetooth_device_summary" - app:layout_constraintStart_toStartOf="@+id/bluetooth_device_name" - app:layout_constraintEnd_toEndOf="@+id/bluetooth_device_name" /> + <TextView + android:layout_width="0dp" + android:id="@+id/bluetooth_device_name" + style="@style/BluetoothTileDialog.DeviceName" + android:paddingStart="20dp" + android:paddingTop="10dp" + app:layout_constraintWidth_percent="0.7" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" + app:layout_constraintEnd_toStartOf="@+id/gear_icon" + app:layout_constraintBottom_toTopOf="@+id/bluetooth_device_summary" + android:gravity="center_vertical" + android:textSize="14sp" /> - <TextView - android:layout_width="0dp" - android:id="@+id/bluetooth_device_name" - style="@style/BluetoothTileDialog.DeviceName" - android:paddingStart="20dp" - android:paddingTop="10dp" - app:layout_constraintWidth_percent="0.7" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" - app:layout_constraintEnd_toStartOf="@+id/gear_icon" - app:layout_constraintBottom_toTopOf="@+id/bluetooth_device_summary" - android:gravity="center_vertical" - android:textSize="14sp" /> + <TextView + android:layout_width="0dp" + android:id="@+id/bluetooth_device_summary" + style="@style/BluetoothTileDialog.DeviceSummary" + android:paddingStart="20dp" + android:paddingBottom="10dp" + app:layout_constraintWidth_percent="0.7" + app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name" + app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" + app:layout_constraintEnd_toStartOf="@+id/gear_icon" + app:layout_constraintBottom_toBottomOf="parent" + android:gravity="center_vertical" /> - <TextView - android:layout_width="0dp" - android:id="@+id/bluetooth_device_summary" - style="@style/BluetoothTileDialog.DeviceSummary" - android:paddingStart="20dp" - android:paddingBottom="10dp" - app:layout_constraintWidth_percent="0.7" - app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name" - app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon" - app:layout_constraintEnd_toStartOf="@+id/gear_icon" - app:layout_constraintBottom_toBottomOf="parent" - android:gravity="center_vertical" /> + <View + android:id="@+id/gear_icon" + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name" + app:layout_constraintEnd_toEndOf="@+id/gear_icon_image" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> - <ImageView - android:id="@+id/gear_icon" - android:src="@drawable/ic_settings_24dp" - android:contentDescription="@string/accessibility_bluetooth_device_settings_gear" - android:layout_width="0dp" - android:layout_height="24dp" - app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintWidth_percent="0.3" - android:gravity="center_vertical" - android:paddingStart="10dp" /> - </androidx.constraintlayout.widget.ConstraintLayout> -</LinearLayout>
\ No newline at end of file + <ImageView + android:id="@+id/gear_icon_image" + android:src="@drawable/ic_settings_24dp" + android:contentDescription="@string/accessibility_bluetooth_device_settings_gear" + android:layout_width="0dp" + android:layout_height="24dp" + app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintWidth_percent="0.3" + android:gravity="center_vertical" + android:paddingStart="10dp" /> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file 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/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/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/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/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 2f3c1f263782..eb7a7358cbf1 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -205,19 +205,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 +225,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 +260,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 +378,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 +413,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/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..0d3f726b011b 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: @@ -687,12 +713,8 @@ public class LockIconViewController implements Dumpable { return true; } - /** - * Intercepts the touch if the onDown event and current event are within this lock icon view's - * bounds. - */ - public boolean onInterceptTouchEvent(MotionEvent event) { - if (!inLockIconArea(event) || !isActionable()) { + private boolean actionableDownEventStartedOnView(MotionEvent event) { + if (!isActionable()) { return false; } @@ -703,7 +725,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 +739,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 +762,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 +839,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..c5817881718b --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt @@ -0,0 +1,40 @@ +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 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, display: Optional<Display>): Context { + return if (display.isPresent) { + context.createDisplayContext(display.get()) + } else { + context + } + } + + @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/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index c09e68d4ace6..f94f8c594aa6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -328,7 +328,10 @@ class AuthRippleController @Inject constructor( private val udfpsControllerCallback = object : UdfpsController.Callback { override fun onFingerDown() { - showDwellRipple() + // only show dwell ripple for device entry + if (keyguardUpdateMonitor.isFingerprintDetectionRunning) { + showDwellRipple() + } } override fun onFingerUp() { 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/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index 66c6162533bf..55bc653fc737 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.bouncer.ui.viewmodel import android.annotation.StringRes -import android.util.Log import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor @@ -103,11 +102,12 @@ sealed class AuthMethodBouncerViewModel( * * @see BouncerInteractor.authenticate */ - protected fun tryAuthenticate(useAutoConfirm: Boolean = false) { + protected fun tryAuthenticate( + input: List<Any> = getInput(), + useAutoConfirm: Boolean = false, + ) { viewModelScope.launch { - Log.d("Danny", "tryAuthenticate(useAutoConfirm=$useAutoConfirm)") - val authenticationResult = interactor.authenticate(getInput(), useAutoConfirm) - Log.d("Danny", "result = $authenticationResult") + val authenticationResult = interactor.authenticate(input, useAutoConfirm) if (authenticationResult == AuthenticationResult.SKIPPED && useAutoConfirm) { return@launch } 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/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index 52adf54f8d24..d301085e823d 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -166,7 +166,8 @@ class PatternBouncerViewModel( interactor.onFalseUserInput() } - tryAuthenticate() + clearInput() + tryAuthenticate(input = pattern) } override fun clearInput() { 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/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/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..6bbd40c4f892 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java @@ -48,6 +48,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; @@ -77,9 +78,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 +161,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.*/ @@ -531,11 +537,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..f6f24e0aecc0 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 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..41bde91d9a01 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; @@ -100,6 +101,7 @@ import kotlinx.coroutines.CoroutineDispatcher; 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..ac012f840d1f 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 @@ -29,8 +29,10 @@ 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 @@ -56,7 +58,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 +98,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/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index aa76702dc3d4..f0d118cbe20f 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 @@ -134,7 +134,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 +149,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() 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/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/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/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 1943b340b9b1..67531ad9926a 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -30,6 +30,8 @@ import com.android.systemui.log.LogcatEchoTrackerProd; import com.android.systemui.log.table.TableLogBuffer; import com.android.systemui.log.table.TableLogBufferFactory; import com.android.systemui.qs.QSFragmentLegacy; +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository; +import com.android.systemui.qs.pipeline.shared.TileSpec; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.util.Compile; import com.android.systemui.util.wakelock.WakeLockLog; @@ -37,6 +39,9 @@ import com.android.systemui.util.wakelock.WakeLockLog; import dagger.Module; import dagger.Provides; +import java.util.HashMap; +import java.util.Map; + /** * Dagger module for providing instances of {@link LogBuffer}. */ @@ -173,8 +178,35 @@ public class LogModule { @Provides @SysUISingleton @QSLog - public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) { - return factory.create("QSLog", 700 /* maxSize */, false /* systrace */); + public static LogBuffer provideQuickSettingsLogBuffer( + LogBufferFactory factory, + QSPipelineFlagsRepository flags + ) { + if (flags.getPipelineTilesEnabled()) { + // we use + return factory.create("QSLog", 450 /* maxSize */, false /* systrace */); + } else { + return factory.create("QSLog", 700 /* maxSize */, false /* systrace */); + } + } + + /** + * Provides a logging buffer for all logs related to Quick Settings tiles. This LogBuffer is + * unique for each tile. + * go/qs-tile-refactor + */ + @Provides + @QSTilesDefaultLog + public static LogBuffer provideQuickSettingsTilesLogBuffer(LogBufferFactory factory) { + return factory.create("QSTileLog", 25 /* maxSize */, false /* systrace */); + } + + @Provides + @QSTilesLogBuffers + public static Map<TileSpec, LogBuffer> provideQuickSettingsTilesLogBufferCache() { + final Map<TileSpec, LogBuffer> buffers = new HashMap<>(); + // Add chatty buffers here + return buffers; } /** Provides a logging buffer for logs related to Quick Settings configuration. */ @@ -420,7 +452,7 @@ public class LogModule { /** * Provides a {@link LogBuffer} for use by - * {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}. + * {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}. */ @Provides @SysUISingleton @@ -431,7 +463,7 @@ public class LogModule { /** * Provides a {@link LogBuffer} for use by classes in the - * {@link com.android.systemui.keyguard.bouncer} package. + * {@link com.android.systemui.keyguard.bouncer} package. */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesDefaultLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesDefaultLog.kt new file mode 100644 index 000000000000..6575cdd69c93 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesDefaultLog.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.log.dagger + +import javax.inject.Qualifier + +/** + * A default [com.android.systemui.log.LogBuffer] for QS tiles messages. It's used exclusively in + * [com.android.systemui.qs.tiles.base.logging.QSTileLogger]. If you need to increase it for you + * tile, add one to the map provided by the [QSTilesLogBuffers] + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class QSTilesDefaultLog diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesLogBuffers.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesLogBuffers.kt new file mode 100644 index 000000000000..62d49fefeb6a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesLogBuffers.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.log.dagger + +import javax.inject.Qualifier + +/** + * Provides a map with custom [com.android.systemui.log.LogBuffer] for QS tiles messages. Add + * buffers to it when the tile needs to be more verbose and the default buffer provided by + * [QSTilesDefaultLog] is not enough. + * + * This is not a multibinding. Add new logs directly to [LogModule] + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class QSTilesLogBuffers diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java index 3fff136db03a..b0c2f8c59deb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java @@ -14,18 +14,23 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.log.dagger; -import java.lang.annotation.ElementType; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.android.systemui.log.LogBuffer; + +import java.lang.annotation.Documented; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; + +import javax.inject.Qualifier; /** - * Mark as tests for Robolectric pilot projects. The filter can better help grouping test results - * that runs on CI + * A {@link LogBuffer} for QS tiles messages. It's used exclusively in + * {@link com.android.systemui.qs.tiles.base.logging.QSTileLogger} */ -@Target({ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface RoboPilotTest { +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface QSTilesVerboseLog { } diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java index 68202d5629a0..80be76661098 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); 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/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 19012e29b184..fa18b35b215e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -27,11 +27,11 @@ import androidx.viewpager.widget.ViewPager; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEventLogger; -import com.android.systemui.res.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.QSPanelControllerBase.TileRecord; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.res.R; import java.util.ArrayList; import java.util.List; @@ -562,6 +562,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { if (shouldNotRunAnimation(tilesToReveal)) { return; } + // This method has side effects (beings the fake drag, if it returns true). If we have + // decided that we want to do a tile reveal, we do a last check to verify that we can + // actually perform a fake drag. + if (!beginFakeDrag()) { + return; + } final int lastPageNumber = mPages.size() - 1; final TileLayout lastPage = mPages.get(lastPageNumber); @@ -596,8 +602,10 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } private boolean shouldNotRunAnimation(Set<String> tilesToReveal) { + // None of these have side effects. That way, we don't need to rely on short-circuiting + // behavior boolean noAnimationNeeded = tilesToReveal.isEmpty() || mPages.size() < 2; - boolean scrollingInProgress = getScrollX() != 0 || !beginFakeDrag(); + boolean scrollingInProgress = getScrollX() != 0 || !isFakeDragging(); // checking mRunningInTestHarness to disable animation in functional testing as it caused // flakiness and is not needed there. Alternative solutions were more complex and would // still be either potentially flaky or modify internal data. 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..202254bb323f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java @@ -199,11 +199,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 +272,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 +328,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); } 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/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/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 128c23745e5f..051eeb0cfaf1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -72,7 +72,8 @@ import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; -public class CustomTile extends QSTileImpl<State> implements TileChangeListener { +public class CustomTile extends QSTileImpl<State> implements TileChangeListener, + CustomTileInterface { public static final String PREFIX = "custom("; private static final long CUSTOM_STALE_TIMEOUT = DateUtils.HOUR_IN_MILLIS; @@ -181,7 +182,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener private void updateDefaultTileAndIcon() { try { PackageManager pm = mUserContext.getPackageManager(); - int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE; + int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE; if (isSystemApp(pm)) { flags |= PackageManager.MATCH_DISABLED_COMPONENTS; } @@ -213,7 +215,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener * Compare two icons, only works for resources. */ private boolean iconEquals(@Nullable android.graphics.drawable.Icon icon1, - @Nullable android.graphics.drawable.Icon icon2) { + @Nullable android.graphics.drawable.Icon icon2) { if (icon1 == icon2) { return true; } @@ -252,10 +254,12 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } } + @Override public int getUser() { return mUser; } + @Override public ComponentName getComponent() { return mComponent; } @@ -265,6 +269,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener return super.populate(logMaker).setComponentName(mComponent); } + @Override public Tile getQsTile() { // TODO(b/191145007) Move to background thread safely updateDefaultTileAndIcon(); @@ -276,6 +281,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener * * @param tile tile populated with state to apply */ + @Override public void updateTileState(Tile tile, int appUid) { mServiceUid = appUid; // This comes from a binder call IQSService.updateQsTile @@ -310,10 +316,12 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mTile.setState(tile.getState()); } + @Override public void onDialogShown() { mIsShowingDialog = true; } + @Override public void onDialogHidden() { mIsShowingDialog = false; try { @@ -507,6 +515,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener return mComponent.getPackageName(); } + @Override public void startUnlockAndRun() { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { try { @@ -518,8 +527,10 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener /** * Starts an {@link android.app.Activity} + * * @param pendingIntent A PendingIntent for an Activity to be launched immediately. */ + @Override public void startActivityAndCollapse(PendingIntent pendingIntent) { if (!pendingIntent.isActivity()) { Log.i(TAG, "Intent not for activity."); diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileInterface.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileInterface.kt new file mode 100644 index 000000000000..9e023205c9fb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileInterface.kt @@ -0,0 +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.qs.external + +import android.app.PendingIntent +import android.content.ComponentName +import android.service.quicksettings.Tile + +interface CustomTileInterface { + + val user: Int + val qsTile: Tile + val component: ComponentName + + fun getTileSpec(): String + + fun refreshState() + fun updateTileState(tile: Tile, uid: Int) + + fun onDialogShown() + fun onDialogHidden() + + fun startActivityAndCollapse(pendingIntent: PendingIntent) + + fun startUnlockAndRun() +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index fc2402258009..acee8e9ad2eb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -67,9 +67,10 @@ public class TileServices extends IQSService.Stub { static final int REDUCED_MAX_BOUND = 1; private static final String TAG = "TileServices"; - private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>(); - private final SparseArrayMap<ComponentName, CustomTile> mTiles = new SparseArrayMap<>(); - private final ArrayMap<IBinder, CustomTile> mTokenMap = new ArrayMap<>(); + private final ArrayMap<CustomTileInterface, TileServiceManager> mServices = new ArrayMap<>(); + private final SparseArrayMap<ComponentName, CustomTileInterface> mTiles = + new SparseArrayMap<>(); + private final ArrayMap<IBinder, CustomTileInterface> mTokenMap = new ArrayMap<>(); private final Context mContext; private final Handler mMainHandler; private final Provider<Handler> mHandlerProvider; @@ -120,7 +121,7 @@ public class TileServices extends IQSService.Stub { return mHost; } - public TileServiceManager getTileWrapper(CustomTile tile) { + public TileServiceManager getTileWrapper(CustomTileInterface tile) { ComponentName component = tile.getComponent(); int userId = tile.getUser(); TileServiceManager service = onCreateTileService(component, mBroadcastDispatcher); @@ -140,7 +141,7 @@ public class TileServices extends IQSService.Stub { broadcastDispatcher, mUserTracker, mCustomTileAddedRepository, mBackgroundExecutor); } - public void freeService(CustomTile tile, TileServiceManager service) { + public void freeService(CustomTileInterface tile, TileServiceManager service) { synchronized (mServices) { service.setBindAllowed(false); service.handleDestroy(); @@ -184,7 +185,7 @@ public class TileServices extends IQSService.Stub { } } - private int verifyCaller(CustomTile tile) { + private int verifyCaller(CustomTileInterface tile) { try { String packageName = tile.getComponent().getPackageName(); int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, @@ -201,7 +202,7 @@ public class TileServices extends IQSService.Stub { private void requestListening(ComponentName component) { synchronized (mServices) { int userId = mUserTracker.getUserId(); - CustomTile customTile = getTileForUserAndComponent(userId, component); + CustomTileInterface customTile = getTileForUserAndComponent(userId, component); if (customTile == null) { Log.d(TAG, "Couldn't find tile for " + component + "(" + userId + ")"); return; @@ -227,7 +228,7 @@ public class TileServices extends IQSService.Stub { @Override public void updateQsTile(Tile tile, IBinder token) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { int uid = verifyCaller(customTile); synchronized (mServices) { @@ -247,7 +248,7 @@ public class TileServices extends IQSService.Stub { @Override public void onStartSuccessful(IBinder token) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); synchronized (mServices) { @@ -267,7 +268,7 @@ public class TileServices extends IQSService.Stub { @Override public void onShowDialog(IBinder token) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); customTile.onDialogShown(); @@ -278,7 +279,7 @@ public class TileServices extends IQSService.Stub { @Override public void onDialogHidden(IBinder token) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(false); @@ -288,7 +289,7 @@ public class TileServices extends IQSService.Stub { @Override public void onStartActivity(IBinder token) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); mPanelInteractor.forceCollapsePanels(); @@ -301,7 +302,7 @@ public class TileServices extends IQSService.Stub { } @VisibleForTesting - protected void startActivity(CustomTile customTile, PendingIntent pendingIntent) { + protected void startActivity(CustomTileInterface customTile, PendingIntent pendingIntent) { if (customTile != null) { verifyCaller(customTile); customTile.startActivityAndCollapse(pendingIntent); @@ -310,7 +311,7 @@ public class TileServices extends IQSService.Stub { @Override public void updateStatusIcon(IBinder token, Icon icon, String contentDescription) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); try { @@ -340,7 +341,7 @@ public class TileServices extends IQSService.Stub { @Nullable @Override public Tile getTile(IBinder token) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); return customTile.getQsTile(); @@ -367,7 +368,7 @@ public class TileServices extends IQSService.Stub { @Override public void startUnlockAndRun(IBinder token) { - CustomTile customTile = getTileForToken(token); + CustomTileInterface customTile = getTileForToken(token); if (customTile != null) { verifyCaller(customTile); customTile.startUnlockAndRun(); @@ -385,14 +386,14 @@ public class TileServices extends IQSService.Stub { } @Nullable - public CustomTile getTileForToken(IBinder token) { + public CustomTileInterface getTileForToken(IBinder token) { synchronized (mServices) { return mTokenMap.get(token); } } @Nullable - private CustomTile getTileForUserAndComponent(int userId, ComponentName component) { + private CustomTileInterface getTileForUserAndComponent(int userId, ComponentName component) { synchronized (mServices) { return mTiles.get(userId, component); } @@ -419,11 +420,6 @@ public class TileServices extends IQSService.Stub { }; private static final Comparator<TileServiceManager> SERVICE_SORT = - new Comparator<TileServiceManager>() { - @Override - public int compare(TileServiceManager left, TileServiceManager right) { - return -Integer.compare(left.getBindPriority(), right.getBindPriority()); - } - }; + (left, right) -> -Integer.compare(left.getBindPriority(), right.getBindPriority()); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt index 38fe34eb8f9f..42d3f81ceed6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt @@ -18,8 +18,6 @@ package com.android.systemui.qs.footer.dagger import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepositoryImpl -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepositoryImpl import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl import dagger.Binds @@ -28,7 +26,6 @@ import dagger.Module /** Dagger module to provide/bind footer actions singletons. */ @Module interface FooterActionsModule { - @Binds fun userSwitcherRepository(impl: UserSwitcherRepositoryImpl): UserSwitcherRepository @Binds fun foregroundServicesRepository( diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt index 8b2c3de18469..c91ed133a11e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt @@ -38,10 +38,10 @@ import com.android.systemui.qs.FgsManagerController import com.android.systemui.qs.QSSecurityFooterUtils import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig import com.android.systemui.security.data.repository.SecurityRepository import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.user.data.repository.UserSwitcherRepository import com.android.systemui.user.domain.interactor.UserInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher 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/base/analytics/QSTileAnalytics.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt new file mode 100644 index 000000000000..0d15a5b6b4d4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.base.analytics + +import com.android.internal.logging.UiEventLogger +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.qs.QSEvent +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject + +/** Tracks QS tiles analytic events to [UiEventLogger]. */ +@SysUISingleton +class QSTileAnalytics +@Inject +constructor( + private val uiEventLogger: UiEventLogger, +) { + + fun trackUserAction(config: QSTileConfig, action: QSTileUserAction) { + logAction(config, action) + } + + private fun logAction(config: QSTileConfig, action: QSTileUserAction) { + uiEventLogger.logWithInstanceId( + action.getQSEvent(), + 0, + config.metricsSpec, + config.instanceId, + ) + } + + private fun QSTileUserAction.getQSEvent(): QSEvent = + when (this) { + is QSTileUserAction.Click -> QSEvent.QS_ACTION_CLICK + is QSTileUserAction.LongClick -> QSEvent.QS_ACTION_LONG_PRESS + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt new file mode 100644 index 000000000000..70a683b81f75 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.base.logging + +import androidx.annotation.GuardedBy +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.dagger.QSTilesDefaultLog +import com.android.systemui.log.dagger.QSTilesLogBuffers +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.android.systemui.statusbar.StatusBarState +import javax.inject.Inject +import javax.inject.Provider + +@SysUISingleton +class QSTileLogger +@Inject +constructor( + @QSTilesLogBuffers logBuffers: Map<TileSpec, LogBuffer>, + @QSTilesDefaultLog private val defaultLogBufferProvider: Provider<LogBuffer>, + private val mStatusBarStateController: StatusBarStateController, +) { + @GuardedBy("logBufferCache") private val logBufferCache = logBuffers.toMutableMap() + + /** + * Tracks user action when it's first received by the ViewModel and before it reaches the + * pipeline + */ + fun logUserAction( + userAction: QSTileUserAction, + tileSpec: TileSpec, + hasData: Boolean, + hasTileState: Boolean, + ) { + tileSpec + .getLogBuffer() + .log( + tileSpec.getLogTag(), + LogLevel.DEBUG, + { + str1 = userAction.toLogString() + int1 = mStatusBarStateController.state + bool1 = hasTileState + bool2 = hasData + }, + { + "tile $str1: " + + "statusBarState=${StatusBarState.toString(int1)}, " + + "hasState=$bool1, " + + "hasData=$bool2" + } + ) + } + + /** Tracks user action when it's rejected by false gestures */ + fun logUserActionRejectedByFalsing( + userAction: QSTileUserAction, + tileSpec: TileSpec, + ) { + tileSpec + .getLogBuffer() + .log( + tileSpec.getLogTag(), + LogLevel.DEBUG, + { str1 = userAction.toLogString() }, + { "tile $str1: rejected by falsing" } + ) + } + + /** Tracks user action when it's rejected according to the policy */ + fun logUserActionRejectedByPolicy( + userAction: QSTileUserAction, + tileSpec: TileSpec, + ) { + tileSpec + .getLogBuffer() + .log( + tileSpec.getLogTag(), + LogLevel.DEBUG, + { str1 = userAction.toLogString() }, + { "tile $str1: rejected by policy" } + ) + } + + /** + * Tracks user actions when it reaches the pipeline and mixes with the last tile state and data + */ + fun <T> logUserActionPipeline( + tileSpec: TileSpec, + userAction: QSTileUserAction, + tileState: QSTileState, + data: T, + ) { + tileSpec + .getLogBuffer() + .log( + tileSpec.getLogTag(), + LogLevel.DEBUG, + { + str1 = userAction.toLogString() + str2 = tileState.toLogString() + str3 = data.toString().take(DATA_MAX_LENGTH) + }, + { + "tile $str1 pipeline: " + + "statusBarState=${StatusBarState.toString(int1)}, " + + "state=$str2, " + + "data=$str3" + } + ) + } + + /** Tracks state changes based on the data and trigger event. */ + fun <T> logStateUpdate( + tileSpec: TileSpec, + trigger: StateUpdateTrigger, + tileState: QSTileState, + data: T, + ) { + tileSpec + .getLogBuffer() + .log( + tileSpec.getLogTag(), + LogLevel.DEBUG, + { + str1 = trigger.toLogString() + str2 = tileState.toLogString() + str3 = data.toString().take(DATA_MAX_LENGTH) + }, + { "tile state update: trigger=$str1, state=$str2, data=$str3" } + ) + } + + private fun TileSpec.getLogTag(): String = "${TAG_FORMAT_PREFIX}_${this.spec}" + + private fun TileSpec.getLogBuffer(): LogBuffer = + synchronized(logBufferCache) { + logBufferCache.getOrPut(this) { defaultLogBufferProvider.get() } + } + + private fun StateUpdateTrigger.toLogString(): String = + when (this) { + is StateUpdateTrigger.ForceUpdate -> "force" + is StateUpdateTrigger.InitialRequest -> "init" + is StateUpdateTrigger.UserAction<*> -> action.toLogString() + } + + private fun QSTileUserAction.toLogString(): String = + when (this) { + is QSTileUserAction.Click -> "click" + is QSTileUserAction.LongClick -> "long click" + } + + /* Shortened version of a data class toString() */ + private fun QSTileState.toLogString(): String = + "[label=$label, " + + "state=$activationState, " + + "s_label=$secondaryLabel, " + + "cd=$contentDescription, " + + "sd=$stateDescription, " + + "svi=$sideViewIcon, " + + "enabled=$enabledState, " + + "a11y=$expandedAccessibilityClassName" + + "]" + + private companion object { + const val TAG_FORMAT_PREFIX = "QSLog" + const val DATA_MAX_LENGTH = 50 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt index 58a335e462a1..2114751ef57b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt @@ -20,12 +20,15 @@ import androidx.annotation.CallSuper import androidx.annotation.VisibleForTesting import com.android.internal.util.Preconditions import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger +import com.android.systemui.qs.tiles.base.logging.QSTileLogger import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy @@ -33,6 +36,7 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel import com.android.systemui.util.kotlin.sample +import com.android.systemui.util.kotlin.throttle import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineDispatcher @@ -70,6 +74,9 @@ constructor( private val tileDataInteractor: QSTileDataInteractor<DATA_TYPE>, private val mapper: QSTileDataToStateMapper<DATA_TYPE>, private val disabledByPolicyInteractor: DisabledByPolicyInteractor, + private val falsingManager: FalsingManager, + private val qsTileAnalytics: QSTileAnalytics, + private val qsTileLogger: QSTileLogger, private val backgroundDispatcher: CoroutineDispatcher, private val tileScope: CoroutineScope, ) : QSTileViewModel { @@ -81,6 +88,9 @@ constructor( @Assisted tileDataInteractor: QSTileDataInteractor<DATA_TYPE>, @Assisted mapper: QSTileDataToStateMapper<DATA_TYPE>, disabledByPolicyInteractor: DisabledByPolicyInteractor, + falsingManager: FalsingManager, + qsTileAnalytics: QSTileAnalytics, + qsTileLogger: QSTileLogger, @Background backgroundDispatcher: CoroutineDispatcher, ) : this( config, @@ -88,6 +98,9 @@ constructor( tileDataInteractor, mapper, disabledByPolicyInteractor, + falsingManager, + qsTileAnalytics, + qsTileLogger, backgroundDispatcher, CoroutineScope(SupervisorJob()) ) @@ -98,8 +111,10 @@ constructor( MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val spec + get() = config.tileSpec - private lateinit var tileData: SharedFlow<DATA_TYPE> + private lateinit var tileData: SharedFlow<DataWithTrigger<DATA_TYPE>> override lateinit var state: SharedFlow<QSTileState> override val isAvailable: StateFlow<Boolean> = @@ -128,8 +143,14 @@ constructor( @CallSuper override fun onActionPerformed(userAction: QSTileUserAction) { - Preconditions.checkState(tileData.replayCache.isNotEmpty()) Preconditions.checkState(currentLifeState == QSTileLifecycle.ALIVE) + + qsTileLogger.logUserAction( + userAction, + spec, + tileData.replayCache.isNotEmpty(), + state.replayCache.isNotEmpty() + ) userInputs.tryEmit(userAction) } @@ -142,7 +163,16 @@ constructor( state = tileData // TODO(b/299908705): log data and corresponding tile state - .map { mapper.map(config, it) } + .map { dataWithTrigger -> + mapper.map(config, dataWithTrigger.data).also { state -> + qsTileLogger.logStateUpdate( + spec, + dataWithTrigger.trigger, + state, + dataWithTrigger.data + ) + } + } .flowOn(backgroundDispatcher) .shareIn( tileScope, @@ -158,7 +188,7 @@ constructor( currentLifeState = lifecycle } - private fun createTileDataFlow(): SharedFlow<DATA_TYPE> = + private fun createTileDataFlow(): SharedFlow<DataWithTrigger<DATA_TYPE>> = userIds .flatMapLatest { userId -> merge( @@ -180,7 +210,7 @@ constructor( request.trigger.tileData as DATA_TYPE, ) } - dataFlow + dataFlow.map { DataWithTrigger(it, request.trigger) } } .flowOn(backgroundDispatcher) .shareIn( @@ -193,21 +223,53 @@ constructor( data class StateWithData<T>(val state: QSTileState, val data: T) return when (config.policy) { - is QSTilePolicy.NoRestrictions -> userInputs - is QSTilePolicy.Restricted -> - userInputs.filter { - val result = - disabledByPolicyInteractor.isDisabled(userId, config.policy.userRestriction) - !disabledByPolicyInteractor.handlePolicyResult(result) + is QSTilePolicy.NoRestrictions -> userInputs + is QSTilePolicy.Restricted -> + userInputs.filter { action -> + val result = + disabledByPolicyInteractor.isDisabled( + userId, + config.policy.userRestriction + ) + !disabledByPolicyInteractor.handlePolicyResult(result).also { isDisabled -> + if (isDisabled) { + qsTileLogger.logUserActionRejectedByPolicy(action, spec) + } + } + } + } + .filter { action -> + val isFalseAction = + when (action) { + is QSTileUserAction.Click -> + falsingManager.isFalseTap(FalsingManager.LOW_PENALTY) + is QSTileUserAction.LongClick -> + falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY) + } + if (isFalseAction) { + qsTileLogger.logUserActionRejectedByFalsing(action, spec) } - // Skip the input until there is some data - }.sample(state.combine(tileData) { state, data -> StateWithData(state, data) }) { - input, - stateWithData -> - StateUpdateTrigger.UserAction(input, stateWithData.state, stateWithData.data) - } + !isFalseAction + } + .throttle(500) + // Skip the input until there is some data + .sample(state.combine(tileData) { state, data -> StateWithData(state, data) }) { + input, + stateWithData -> + StateUpdateTrigger.UserAction(input, stateWithData.state, stateWithData.data).also { + qsTileLogger.logUserActionPipeline( + spec, + it.action, + stateWithData.state, + stateWithData.data + ) + qsTileAnalytics.trackUserAction(config, it.action) + } + } } + private data class DataWithTrigger<T>(val data: T, val trigger: StateUpdateTrigger) + interface Factory<T> { /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt index efad9ec548f9..8957fc3efb9c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt @@ -40,7 +40,7 @@ constructor( @Application private val coroutineScope: CoroutineScope, ) { - internal val updateBluetoothStateFlow: StateFlow<Boolean?> = + internal val bluetoothStateUpdate: StateFlow<Boolean?> = conflatedCallbackFlow { val listener = object : BluetoothCallback { 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 6815a7325081..8ae2dc227998 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 @@ -26,6 +26,8 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.Switch import android.widget.TextView +import androidx.recyclerview.widget.AsyncListDiffer +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.android.internal.logging.UiEventLogger @@ -42,24 +44,26 @@ import kotlinx.coroutines.flow.asStateFlow internal class BluetoothTileDialog constructor( private val bluetoothToggleInitialValue: Boolean, + private val subtitleResIdInitialValue: Int, private val bluetoothTileDialogCallback: BluetoothTileDialogCallback, private val uiEventLogger: UiEventLogger, context: Context, ) : SystemUIDialog(context, DEFAULT_THEME, DEFAULT_DISMISS_ON_DEVICE_LOCK) { - private val mutableBluetoothStateSwitchedFlow: MutableStateFlow<Boolean> = + private val mutableBluetoothStateToggle: MutableStateFlow<Boolean> = MutableStateFlow(bluetoothToggleInitialValue) - internal val bluetoothStateSwitchedFlow - get() = mutableBluetoothStateSwitchedFlow.asStateFlow() + internal val bluetoothStateToggle + get() = mutableBluetoothStateToggle.asStateFlow() - private val mutableClickedFlow: MutableSharedFlow<Pair<DeviceItem, Int>> = + private val mutableDeviceItemClick: MutableSharedFlow<DeviceItem> = MutableSharedFlow(extraBufferCapacity = 1) - internal val deviceItemClickedFlow - get() = mutableClickedFlow.asSharedFlow() + internal val deviceItemClick + get() = mutableDeviceItemClick.asSharedFlow() private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback) private lateinit var toggleView: Switch + private lateinit var subtitleTextView: TextView private lateinit var doneButton: View private lateinit var seeAllViewGroup: View private lateinit var pairNewDeviceViewGroup: View @@ -74,6 +78,7 @@ constructor( setContentView(LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null)) toggleView = requireViewById(R.id.bluetooth_toggle) + subtitleTextView = requireViewById(R.id.bluetooth_tile_dialog_subtitle) as TextView doneButton = requireViewById(R.id.done_button) seeAllViewGroup = requireViewById(R.id.see_all_layout_group) pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group) @@ -84,6 +89,7 @@ constructor( setupToggle() setupRecyclerView() + subtitleTextView.text = context.getString(subtitleResIdInitialValue) doneButton.setOnClickListener { dismiss() } seeAllText.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) } pairNewDeviceText.setOnClickListener { @@ -91,7 +97,6 @@ constructor( } } - // TODO(b/298124674): use DiffUtil or AsyncListDiffer to avoid updating the whole list internal fun onDeviceItemUpdated( deviceItem: List<DeviceItem>, showSeeAll: Boolean, @@ -102,18 +107,15 @@ constructor( deviceItemAdapter.refreshDeviceItemList(deviceItem) } - internal fun onDeviceItemUpdatedAtPosition(deviceItem: DeviceItem, position: Int) { - deviceItemAdapter.refreshDeviceItem(deviceItem, position) - } - - internal fun onBluetoothStateUpdated(isEnabled: Boolean) { + internal fun onBluetoothStateUpdated(isEnabled: Boolean, subtitleResId: Int) { toggleView.isChecked = isEnabled + subtitleTextView.text = context.getString(subtitleResId) } private fun setupToggle() { toggleView.isChecked = bluetoothToggleInitialValue toggleView.setOnCheckedChangeListener { _, isChecked -> - mutableBluetoothStateSwitchedFlow.value = isChecked + mutableBluetoothStateToggle.value = isChecked uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TOGGLE_CLICKED) } } @@ -128,7 +130,32 @@ constructor( internal inner class Adapter(private val onClickCallback: BluetoothTileDialogCallback) : RecyclerView.Adapter<Adapter.DeviceItemViewHolder>() { - private val deviceItem: MutableList<DeviceItem> = mutableListOf() + private val diffUtilCallback = + object : DiffUtil.ItemCallback<DeviceItem>() { + override fun areItemsTheSame( + deviceItem1: DeviceItem, + deviceItem2: DeviceItem + ): Boolean { + return deviceItem1.cachedBluetoothDevice == deviceItem2.cachedBluetoothDevice + } + + override fun areContentsTheSame( + deviceItem1: DeviceItem, + deviceItem2: DeviceItem + ): Boolean { + return deviceItem1.type == deviceItem2.type && + deviceItem1.cachedBluetoothDevice == deviceItem2.cachedBluetoothDevice && + deviceItem1.deviceName == deviceItem2.deviceName && + deviceItem1.connectionSummary == deviceItem2.connectionSummary && + // Ignored the icon drawable + deviceItem1.iconWithDescription?.second == + deviceItem2.iconWithDescription?.second && + deviceItem1.background == deviceItem2.background && + deviceItem1.isEnabled == deviceItem2.isEnabled + } + } + + private val asyncListDiffer = AsyncListDiffer(this, diffUtilCallback) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceItemViewHolder { val view = @@ -137,29 +164,21 @@ constructor( return DeviceItemViewHolder(view) } - override fun getItemCount() = deviceItem.size + override fun getItemCount() = asyncListDiffer.currentList.size override fun onBindViewHolder(holder: DeviceItemViewHolder, position: Int) { val item = getItem(position) - holder.bind(item, position, onClickCallback) + holder.bind(item, onClickCallback) } - internal fun getItem(position: Int) = deviceItem[position] + internal fun getItem(position: Int) = asyncListDiffer.currentList[position] internal fun refreshDeviceItemList(updated: List<DeviceItem>) { - deviceItem.clear() - deviceItem.addAll(updated) - notifyDataSetChanged() - } - - internal fun refreshDeviceItem(updated: DeviceItem, position: Int) { - deviceItem[position] = updated - notifyItemChanged(position) + asyncListDiffer.submitList(updated) } internal inner class DeviceItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { private val container = view.requireViewById<View>(R.id.bluetooth_device_row) - private val deviceView = view.requireViewById<View>(R.id.bluetooth_device) private val nameView = view.requireViewById<TextView>(R.id.bluetooth_device_name) private val summaryView = view.requireViewById<TextView>(R.id.bluetooth_device_summary) private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon) @@ -167,17 +186,15 @@ constructor( internal fun bind( item: DeviceItem, - position: Int, deviceItemOnClickCallback: BluetoothTileDialogCallback ) { container.apply { isEnabled = item.isEnabled - alpha = item.alpha - background = item.background - } - deviceView.setOnClickListener { - mutableClickedFlow.tryEmit(Pair(item, position)) - uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED) + background = item.background?.let { context.getDrawable(it) } + setOnClickListener { + mutableDeviceItemClick.tryEmit(item) + uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED) + } } nameView.text = item.deviceName summaryView.text = item.connectionSummary @@ -195,8 +212,6 @@ constructor( } internal companion object { - const val ENABLED_ALPHA = 1.0f - const val DISABLED_ALPHA = 0.3f const val MAX_DEVICE_ITEM_ENTRY = 3 const val ACTION_BLUETOOTH_DEVICE_DETAILS = "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS" 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 012484f2f0a6..97e178371f2d 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 @@ -21,7 +21,9 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.annotation.VisibleForTesting +import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger +import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -31,6 +33,7 @@ import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Compan import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PAIR_NEW_DEVICE import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.MAX_DEVICE_ITEM_ENTRY +import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialog import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -73,14 +76,26 @@ constructor( job = coroutineScope.launch(mainDispatcher) { dialog = createBluetoothTileDialog(context) - view?.let { dialogLaunchAnimator.showFromView(dialog!!, it) } ?: dialog!!.show() + view?.let { + dialogLaunchAnimator.showFromView( + dialog!!, + it, + animateBackgroundBoundsChange = true, + cuj = + DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG + ) + ) + } + ?: dialog!!.show() updateDeviceItemJob?.cancel() updateDeviceItemJob = launch { deviceItemInteractor.updateDeviceItems(context) } - bluetoothStateInteractor.updateBluetoothStateFlow + bluetoothStateInteractor.bluetoothStateUpdate .filterNotNull() .onEach { - dialog!!.onBluetoothStateUpdated(it) + dialog!!.onBluetoothStateUpdated(it, getSubtitleResId(it)) updateDeviceItemJob?.cancel() updateDeviceItemJob = launch { deviceItemInteractor.updateDeviceItems(context) @@ -88,7 +103,7 @@ constructor( } .launchIn(this) - deviceItemInteractor.updateDeviceItemsFlow + deviceItemInteractor.deviceItemUpdateRequest .onEach { updateDeviceItemJob?.cancel() updateDeviceItemJob = launch { @@ -97,7 +112,7 @@ constructor( } .launchIn(this) - deviceItemInteractor.deviceItemFlow + deviceItemInteractor.deviceItemUpdate .filterNotNull() .onEach { dialog!!.onDeviceItemUpdated( @@ -109,17 +124,13 @@ constructor( .launchIn(this) dialog!! - .bluetoothStateSwitchedFlow + .bluetoothStateToggle .onEach { bluetoothStateInteractor.isBluetoothEnabled = it } .launchIn(this) dialog!! - .deviceItemClickedFlow - .onEach { - if (deviceItemInteractor.updateDeviceItemOnClick(it.first)) { - dialog!!.onDeviceItemUpdatedAtPosition(it.first, it.second) - } - } + .deviceItemClick + .onEach { deviceItemInteractor.updateDeviceItemOnClick(it) } .launchIn(this) } } @@ -127,6 +138,7 @@ constructor( private fun createBluetoothTileDialog(context: Context): BluetoothTileDialog { return BluetoothTileDialog( bluetoothStateInteractor.isBluetoothEnabled, + getSubtitleResId(bluetoothStateInteractor.isBluetoothEnabled), this@BluetoothTileDialogViewModel, uiEventLogger, context @@ -175,6 +187,13 @@ constructor( ) } } + + companion object { + private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog" + private fun getSubtitleResId(isBluetoothEnabled: Boolean) = + if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle + else R.string.bt_is_off + } } internal interface BluetoothTileDialogCallback { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt index 03ae5e88f1b0..50eaf38f20d6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt @@ -34,7 +34,6 @@ package com.android.systemui.qs.tiles.dialog.bluetooth import android.graphics.drawable.Drawable import com.android.settingslib.bluetooth.CachedBluetoothDevice -import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA enum class DeviceItemType { AVAILABLE_MEDIA_BLUETOOTH_DEVICE, @@ -48,7 +47,6 @@ data class DeviceItem( val deviceName: String = "", val connectionSummary: String = "", val iconWithDescription: Pair<Drawable, String>? = null, - val background: Drawable? = null, - var isEnabled: Boolean = true, - var alpha: Float = ENABLED_ALPHA + val background: Int? = null, + var isEnabled: Boolean = true ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt index a16a9f1f1017..8c22614f9c31 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt @@ -24,6 +24,8 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.systemui.res.R private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on +private val backgroundOff = R.drawable.bluetooth_tile_dialog_bg_off +private val backgroundOffBusy = R.drawable.bluetooth_tile_dialog_bg_off_busy private val connected = R.string.quick_settings_bluetooth_device_connected private val saved = R.string.quick_settings_bluetooth_device_saved @@ -57,11 +59,8 @@ internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() { BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p -> Pair(p.first, p.second) }, - background = context.getDrawable(backgroundOn), + background = backgroundOn, isEnabled = !cachedDevice.isBusy, - alpha = - if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA - else BluetoothTileDialog.ENABLED_ALPHA, ) } } @@ -85,10 +84,8 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() { BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p -> Pair(p.first, p.second) }, + background = backgroundOn, isEnabled = !cachedDevice.isBusy, - alpha = - if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA - else BluetoothTileDialog.ENABLED_ALPHA, ) } } @@ -112,10 +109,8 @@ internal class SavedDeviceItemFactory : DeviceItemFactory() { BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p -> Pair(p.first, p.second) }, + background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff, isEnabled = !cachedDevice.isBusy, - alpha = - if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA - else BluetoothTileDialog.ENABLED_ALPHA, ) } } 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 fcd0ce6807fd..14d24f952c8d 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 @@ -55,11 +55,12 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, ) { - private val mutableDeviceItemFlow: MutableStateFlow<List<DeviceItem>?> = MutableStateFlow(null) - internal val deviceItemFlow - get() = mutableDeviceItemFlow.asStateFlow() + private val mutableDeviceItemUpdate: MutableStateFlow<List<DeviceItem>?> = + MutableStateFlow(null) + internal val deviceItemUpdate + get() = mutableDeviceItemUpdate.asStateFlow() - internal val updateDeviceItemsFlow: SharedFlow<Unit> = + internal val deviceItemUpdateRequest: SharedFlow<Unit> = conflatedCallbackFlow { val listener = object : BluetoothCallback { @@ -120,7 +121,7 @@ constructor( withContext(backgroundDispatcher) { val mostRecentlyConnectedDevices = bluetoothAdapter?.mostRecentlyConnectedDevices - mutableDeviceItemFlow.value = + mutableDeviceItemUpdate.value = bluetoothTileDialogRepository.cachedDevices .mapNotNull { cachedDevice -> deviceItemFactoryList @@ -143,28 +144,20 @@ constructor( ) } - internal fun updateDeviceItemOnClick(deviceItem: DeviceItem): Boolean { - var isClicked = false + internal fun updateDeviceItemOnClick(deviceItem: DeviceItem) { when (deviceItem.type) { DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> { if (!BluetoothUtils.isActiveMediaDevice(deviceItem.cachedBluetoothDevice)) { deviceItem.cachedBluetoothDevice.setActive() uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE) - isClicked = true } } DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {} DeviceItemType.SAVED_BLUETOOTH_DEVICE -> { deviceItem.cachedBluetoothDevice.connect() uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT) - isClicked = true } } - if (isClicked) { - deviceItem.isEnabled = false - deviceItem.alpha = BluetoothTileDialog.DISABLED_ALPHA - } - return isClicked } internal fun setDeviceItemFactoryListForTesting(list: List<DeviceItemFactory>) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt index 1a6cf99ab810..4a3bcae17fd0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt @@ -26,6 +26,7 @@ data class QSTileConfig( val tileIcon: Icon, @StringRes val tileLabelRes: Int, val instanceId: InstanceId, + val metricsSpec: String = tileSpec.spec, val policy: QSTilePolicy = QSTilePolicy.NoRestrictions, ) 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..2ef83dd42868 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; @@ -362,7 +362,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 +1275,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 +3026,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( @@ -3507,7 +3508,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 +3557,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..5414b3f30aa5 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; @@ -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; @@ -106,6 +107,8 @@ public class NotificationShadeWindowViewController implements Dumpable { private final boolean mIsTrackpadCommonEnabled; private final FeatureFlags mFeatureFlags; private final KeyEventInteractor mKeyEventInteractor; + 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) { + KeyEventInteractor keyEventInteractor, + PrimaryBouncerInteractor primaryBouncerInteractor, + AlternateBouncerInteractor alternateBouncerInteractor) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; @@ -210,6 +215,8 @@ public class NotificationShadeWindowViewController implements Dumpable { mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON); mFeatureFlags = featureFlags; mKeyEventInteractor = keyEventInteractor; + 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 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/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/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/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index 249c83166c51..e2de37fcbcbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.dagger import com.android.systemui.CoreStartable import com.android.systemui.statusbar.core.StatusBarInitializer +import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepository +import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryImpl import com.android.systemui.statusbar.data.repository.StatusBarModeRepository import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl import com.android.systemui.statusbar.phone.LightBarController @@ -49,6 +51,11 @@ abstract class StatusBarModule { abstract fun bindStatusBarModeRepositoryStart(impl: StatusBarModeRepositoryImpl): CoreStartable @Binds + abstract fun bindKeyguardStatusBarRepository( + impl: KeyguardStatusBarRepositoryImpl + ): KeyguardStatusBarRepository + + @Binds @IntoMap @ClassKey(OngoingCallController::class) abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt new file mode 100644 index 000000000000..8136de9b7ac4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.data.repository + +import android.content.Context +import com.android.internal.R +import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.user.data.repository.UserSwitcherRepository +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +/** + * Repository for data that's specific to the status bar **on keyguard**. For data that applies to + * all status bars, use [StatusBarModeRepository]. + */ +interface KeyguardStatusBarRepository { + /** True if we can show the user switcher on keyguard and false otherwise. */ + val isKeyguardUserSwitcherEnabled: Flow<Boolean> +} + +@SysUISingleton +class KeyguardStatusBarRepositoryImpl +@Inject +constructor( + context: Context, + configurationController: ConfigurationController, + userSwitcherRepository: UserSwitcherRepository, +) : KeyguardStatusBarRepository { + private val relevantConfigChanges: Flow<Unit> = + ConflatedCallbackFlow.conflatedCallbackFlow { + val callback = + object : ConfigurationController.ConfigurationListener { + override fun onSmallestScreenWidthChanged() { + trySend(Unit) + } + + override fun onDensityOrFontScaleChanged() { + trySend(Unit) + } + } + configurationController.addCallback(callback) + awaitClose { configurationController.removeCallback(callback) } + } + + private val isKeyguardUserSwitcherConfigEnabled: Flow<Boolean> = + // The config depends on screen size and user enabled settings, so re-fetch whenever any of + // those change. + merge(userSwitcherRepository.isEnabled.map {}, relevantConfigChanges).map { + context.resources.getBoolean(R.bool.config_keyguardUserSwitcher) + } + + /** True if we can show the user switcher on keyguard and false otherwise. */ + override val isKeyguardUserSwitcherEnabled: Flow<Boolean> = + combine( + userSwitcherRepository.isEnabled, + isKeyguardUserSwitcherConfigEnabled, + ) { isEnabled, isKeyguardEnabled -> + isEnabled && isKeyguardEnabled + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractor.kt new file mode 100644 index 000000000000..e0c30e5ec5c4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractor.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.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class KeyguardStatusBarInteractor +@Inject +constructor( + keyguardStatusBarRepository: KeyguardStatusBarRepository, +) { + /** True if we can show the user switcher on keyguard and false otherwise. */ + val isKeyguardUserSwitcherEnabled: Flow<Boolean> = + keyguardStatusBarRepository.isKeyguardUserSwitcherEnabled +} 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/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/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt index 20241c323ab6..805a4dba111f 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.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 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/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..061132ff4f19 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 @@ -772,6 +772,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/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/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 28f0a0c5fd78..78d75582817b 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 @@ -203,6 +203,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final ViewRefactorFlag mAnimatedInsets; private final ViewRefactorFlag mShelfRefactor; + private final boolean mNewAodTransition; + private int mContentHeight; private float mIntrinsicContentHeight; private int mPaddingBetweenElements; @@ -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,6 +632,7 @@ 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 = @@ -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..9695cb132ba9 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 @@ -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; @@ -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,7 +715,7 @@ public class NotificationStackScrollLayoutController { mUiEventLogger = uiEventLogger; mRemoteInputManager = remoteInputManager; mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator; - mSeenNotificationsProvider = seenNotificationsProvider; + mNotificationListInteractor = notificationListInteractor; mShadeController = shadeController; mNotifIconAreaController = notifIconAreaController; mFeatureFlags = featureFlags; @@ -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..8ca18521de63 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 @@ -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/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 4e81d0c7cbdc..69453c65f57d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -127,7 +127,8 @@ public class StackStateAnimator { ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, long additionalDelay) { - processAnimationEvents(mAnimationEvents); + // Animation events might generate custom animations, which are started async + boolean anyCustomAnimationCreated = processAnimationEvents(mAnimationEvents); int childCount = mHostLayout.getChildCount(); mAnimationFilter.applyCombination(mNewEvents); @@ -150,8 +151,8 @@ public class StackStateAnimator { initAnimationProperties(child, viewState, animationStaggerCount); viewState.animateTo(child, mAnimationProperties); } - if (!isRunning()) { - // no child has preformed any animation, lets finish + if (!isRunning() && !anyCustomAnimationCreated) { + // no child has performed any animation or is about to animate, lets finish onAnimationFinished(); } mHeadsUpAppearChildren.clear(); @@ -335,12 +336,15 @@ public class StackStateAnimator { } /** - * Process the animationEvents for a new animation + * Process the animationEvents for a new animation. Here is the place to do something custom, + * like to modify the ViewState or to create a custom animation for an event. * * @param animationEvents the animation events for the animation to perform + * @return true if any custom animation was created */ - private void processAnimationEvents( + private boolean processAnimationEvents( ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) { + boolean needsCustomAnimation = false; for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) { final ExpandableView changingView = (ExpandableView) event.mChangingView; boolean loggable = false; @@ -425,7 +429,8 @@ public class StackStateAnimator { } changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, 0 /* delay */, translationDirection, false /* isHeadsUpAppear */, - postAnimation, null); + postAnimation, getGlobalAnimationFinishedListener()); + needsCustomAnimation = true; } else if (event.animationType == NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { if (mHostLayout.isFullySwipedOut(changingView)) { @@ -479,7 +484,6 @@ public class StackStateAnimator { needsAnimation = false; } } - if (needsAnimation) { // We need to add the global animation listener, since once no animations are // running anymore, the panel will instantly hide itself. We need to wait until @@ -503,9 +507,11 @@ public class StackStateAnimator { } else if (endRunnable != null) { endRunnable.run(); } + needsCustomAnimation |= needsAnimation; } mNewEvents.add(event); } + return needsCustomAnimation; } public void animateOverScrollToAmount(float targetAmount, final boolean onTop, 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/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 3e2f10dd1a0c..6e6318e780dc 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,8 @@ 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 java.io.PrintWriter; import java.io.StringWriter; import java.util.List; @@ -247,8 +250,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 +418,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 +612,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 +719,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 +1089,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 +1191,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }); mStatusBarInitializer.initializeStatusBar(); - mStatusBarTouchableRegionManager.setup(this, getNotificationShadeWindowView()); + mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView()); createNavigationBar(result); @@ -2688,7 +2684,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 +2842,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 +2914,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 +3031,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 +3152,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // TODO: Bring these out of CentralSurfaces. mUserInfoControllerImpl.onDensityOrFontScaleChanged(); mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); - mHeadsUpManager.onDensityOrFontScaleChanged(); } @Override @@ -3200,7 +3188,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/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 7efa705b7929..58126ae41a1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -43,9 +43,9 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; import com.android.settingslib.Utils; -import com.android.systemui.res.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder; @@ -367,15 +367,22 @@ public class KeyguardStatusBarView extends RelativeLayout { mMultiUserAvatar.setImageDrawable(picture); } - /** Should only be called from {@link KeyguardStatusBarViewController}. */ - void onBatteryLevelChanged(boolean charging) { + /** + * Should only be called from {@link KeyguardStatusBarViewController} or + * {@link com.android.systemui.statusbar.ui.binder.KeyguardStatusBarViewBinder}. + */ + public void onBatteryChargingChanged(boolean charging) { if (mBatteryCharging != charging) { mBatteryCharging = charging; updateVisibilities(); } } - void setKeyguardUserSwitcherEnabled(boolean enabled) { + /** + * Should only be called from {@link KeyguardStatusBarViewController} or + * {@link com.android.systemui.statusbar.ui.binder.KeyguardStatusBarViewBinder}. + */ + public void setKeyguardUserSwitcherEnabled(boolean enabled) { mKeyguardUserSwitcherEnabled = enabled; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 9cf9714c274c..2960520f00b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -172,7 +172,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat new BatteryController.BatteryStateChangeCallback() { @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - mView.onBatteryLevelChanged(charging); + mView.onBatteryChargingChanged(charging); } }; @@ -430,11 +430,18 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /** Sets whether user switcher is enabled. */ public void setKeyguardUserSwitcherEnabled(boolean enabled) { + if (isMigrationEnabled()) { + return; + } mView.setKeyguardUserSwitcherEnabled(enabled); } /** Sets whether this controller should listen to battery updates. */ public void setBatteryListening(boolean listening) { + if (isMigrationEnabled()) { + return; + } + if (listening == mBatteryListening) { return; } @@ -472,6 +479,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /** Animate the keyguard status bar in. */ public void animateKeyguardStatusBarIn() { + if (isMigrationEnabled()) { + return; + } + mLogger.log(TAG, LogLevel.DEBUG, "animating status bar in"); if (mDisableStateTracker.isDisabled()) { // If our view is disabled, don't allow us to animate in. @@ -488,6 +499,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /** Animate the keyguard status bar out. */ public void animateKeyguardStatusBarOut(long startDelay, long duration) { + if (isMigrationEnabled()) { + return; + } + mLogger.log(TAG, LogLevel.DEBUG, "animating status bar out"); ValueAnimator anim = ValueAnimator.ofFloat(mView.getAlpha(), 0f); anim.addUpdateListener(mAnimatorUpdateListener); 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..1b9e5b304635 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; @@ -109,6 +111,8 @@ public class LegacyNotificationIconAreaControllerImpl implements private final ViewRefactorFlag mShelfRefactor; + private final boolean mNewAodTransition; + private int mAodIconAppearTranslation; private boolean mAnimationsEnabled; @@ -147,6 +151,7 @@ public class LegacyNotificationIconAreaControllerImpl implements mContext = context; mStatusBarStateController = statusBarStateController; mShelfRefactor = new ViewRefactorFlag(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..62b2445de13a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1573,6 +1573,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,6 +1584,10 @@ 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; 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/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/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/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt index c63ef9e5e012..6988e211855b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt @@ -22,6 +22,8 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.phone.KeyguardStatusBarView import com.android.systemui.statusbar.ui.viewmodel.KeyguardStatusBarViewModel +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch /** Binds [KeyguardStatusBarViewModel] to [KeyguardStatusBarView]. */ object KeyguardStatusBarViewBinder { @@ -32,8 +34,18 @@ object KeyguardStatusBarViewBinder { ) { view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.isVisible.collect { isVisible -> - view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + launch { + viewModel.isVisible.collect { isVisible -> + view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + } + } + + launch { viewModel.isBatteryCharging.collect { view.onBatteryChargingChanged(it) } } + + launch { + viewModel.isKeyguardUserSwitcherEnabled.distinctUntilChanged().collect { + view.setKeyguardUserSwitcherEnabled(it) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt index ddfed8795fcf..5da01e23e268 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt @@ -16,12 +16,18 @@ package com.android.systemui.statusbar.ui.viewmodel +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.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor +import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -41,6 +47,8 @@ class KeyguardStatusBarViewModel constructor( @Application scope: CoroutineScope, keyguardInteractor: KeyguardInteractor, + keyguardStatusBarInteractor: KeyguardStatusBarInteractor, + batteryController: BatteryController, ) { /** True if this view should be visible and false otherwise. */ val isVisible: StateFlow<Boolean> = @@ -51,4 +59,26 @@ constructor( !isDozing && statusBarState == StatusBarState.KEYGUARD } .stateIn(scope, SharingStarted.WhileSubscribed(), false) + + /** True if the device's battery is currently charging and false otherwise. */ + // Note: Never make this an eagerly-started state flow so that the callback is removed when the + // keyguard status bar view isn't attached. + val isBatteryCharging: Flow<Boolean> = conflatedCallbackFlow { + val callback = + object : BatteryStateChangeCallback { + override fun onBatteryLevelChanged( + level: Int, + pluggedIn: Boolean, + charging: Boolean, + ) { + trySend(charging) + } + } + batteryController.addCallback(callback) + awaitClose { batteryController.removeCallback(callback) } + } + + /** True if we can show the user switcher on keyguard and false otherwise. */ + val isKeyguardUserSwitcherEnabled: Flow<Boolean> = + keyguardStatusBarInteractor.isKeyguardUserSwitcherEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt index 18ae1070e1bb..71352eff026c 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt @@ -23,4 +23,6 @@ import dagger.Module @Module interface UserRepositoryModule { @Binds fun bindRepository(impl: UserRepositoryImpl): UserRepository + + @Binds fun userSwitcherRepository(impl: UserSwitcherRepositoryImpl): UserSwitcherRepository } diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt index 5fa75ad68165..dc7fadd5eb14 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.qs.footer.data.repository +package com.android.systemui.user.data.repository import android.content.Context import android.graphics.drawable.Drawable @@ -22,7 +22,6 @@ import android.os.Handler import android.os.UserManager import android.provider.Settings.Global.USER_SWITCHER_ENABLED import com.android.keyguard.KeyguardUpdateMonitor -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 @@ -30,6 +29,7 @@ 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.footer.data.model.UserSwitcherStatusModel +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.UserInfoController import com.android.systemui.statusbar.policy.UserSwitcherController @@ -48,6 +48,9 @@ import kotlinx.coroutines.withContext interface UserSwitcherRepository { /** The current [UserSwitcherStatusModel]. */ val userSwitcherStatus: Flow<UserSwitcherStatusModel> + + /** Whether the user switcher is currently enabled. */ + val isEnabled: Flow<Boolean> } @SysUISingleton @@ -66,8 +69,7 @@ constructor( private val showUserSwitcherForSingleUser = context.resources.getBoolean(R.bool.qs_show_user_switcher_for_single_user) - /** Whether the user switcher is currently enabled. */ - private val isEnabled: Flow<Boolean> = conflatedCallbackFlow { + override val isEnabled: Flow<Boolean> = conflatedCallbackFlow { suspend fun updateState() { trySendWithFailureLogging(isUserSwitcherEnabled(), TAG) } 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/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/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/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/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 6b5679a2a92a..8e54eb7334dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -301,7 +301,10 @@ class AuthRippleControllerTest : SysuiTestCase() { } @Test - fun testUdfps_onFingerDown_showDwellRipple() { + fun testUdfps_onFingerDown_runningForDeviceEntry_showDwellRipple() { + // GIVEN fingerprint detection is running on keyguard + `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true) + // GIVEN view is already attached controller.onViewAttached() val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java) @@ -318,4 +321,21 @@ class AuthRippleControllerTest : SysuiTestCase() { verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f) verify(rippleView).startDwellRipple(false) } + + @Test + fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() { + // GIVEN fingerprint detection is NOT running on keyguard + `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false) + + // GIVEN view is already attached + controller.onViewAttached() + val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java) + verify(udfpsController).addCallback(captor.capture()) + + // WHEN finger is down + captor.value.onFingerDown() + + // THEN doesn't show dwell ripple + verify(rippleView, never()).startDwellRipple(false) + } } 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..e9e9624580b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -38,7 +38,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 @@ -83,7 +82,6 @@ private const val SENSOR_HEIGHT = 60 @ExperimentalCoroutinesApi @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class UdfpsControllerOverlayTest : SysuiTestCase() { 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..a36f4e9ac217 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; @@ -94,6 +95,7 @@ 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; @@ -125,7 +127,6 @@ import java.util.Optional; import javax.inject.Provider; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class UdfpsControllerTest extends SysuiTestCase { 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/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java index 8508f45b02f3..b018a3e2ca6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java @@ -31,14 +31,12 @@ 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) 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..da4548bc14c0 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 : 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..0c8e7a5d356a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt @@ -26,7 +26,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 @@ -50,7 +49,6 @@ private const val SENSOR_Y = 250 private const val SENSOR_RADIUS = 10 @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class UdfpsViewTest : SysuiTestCase() { 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/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/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/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/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..faf97517ac59 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 @@ -26,7 +26,6 @@ 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 @@ -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..1e80fb69c107 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 @@ -23,7 +23,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.settings.FakeUserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots @@ -49,7 +48,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { 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..900413c14475 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() { @@ -132,58 +134,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 +270,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/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..93640975901b 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 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/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/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 new file mode 100644 index 000000000000..9861606fd1b1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.base.analytics + +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.SysuiTestCase +import com.android.systemui.qs.QSEvent +import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.eq +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +class QSTileAnalyticsTest : SysuiTestCase() { + + @Mock private lateinit var uiEventLogger: UiEventLogger + + private lateinit var underTest: QSTileAnalytics + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = QSTileAnalytics(uiEventLogger) + } + + @Test + fun testClickIsLogged() { + underTest.trackUserAction(config, QSTileUserAction.Click(null)) + + verify(uiEventLogger) + .logWithInstanceId( + eq(QSEvent.QS_ACTION_CLICK), + eq(0), + eq("test_spec"), + eq(InstanceId.fakeInstanceId(0)) + ) + } + + @Test + fun testLongClickIsLogged() { + underTest.trackUserAction(config, QSTileUserAction.LongClick(null)) + + verify(uiEventLogger) + .logWithInstanceId( + eq(QSEvent.QS_ACTION_LONG_PRESS), + eq(0), + eq("test_spec"), + eq(InstanceId.fakeInstanceId(0)) + ) + } + + private companion object { + + val config = QSTileConfigTestBuilder.build() + } +} 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 new file mode 100644 index 000000000000..f1fcee318141 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.base.logging + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.ContentDescription +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dump.LogcatEchoTrackerAlways +import com.android.systemui.log.LogBuffer +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.google.common.truth.Truth.assertThat +import java.io.PrintWriter +import java.io.StringWriter +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 QSTileLoggerTest : SysuiTestCase() { + + @Mock private lateinit var statusBarController: StatusBarStateController + + private val chattyLogBuffer = LogBuffer("TestChatty", 5, LogcatEchoTrackerAlways()) + private val logBuffer = LogBuffer("Test", 1, LogcatEchoTrackerAlways()) + + private lateinit var underTest: QSTileLogger + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = + QSTileLogger( + mapOf(TileSpec.create("chatty_tile") to chattyLogBuffer), + { logBuffer }, + statusBarController + ) + } + + @Test + fun testChattyLog() { + underTest.logUserActionRejectedByFalsing( + QSTileUserAction.Click(null), + TileSpec.create("chatty_tile"), + ) + underTest.logUserActionRejectedByFalsing( + QSTileUserAction.Click(null), + TileSpec.create("chatty_tile"), + ) + + val logs = chattyLogBuffer.getStringBuffer().lines().filter { it.isNotBlank() } + assertThat(logs).hasSize(2) + logs.forEach { assertThat(it).contains("tile click: rejected by falsing") } + } + + @Test + fun testLogUserAction() { + underTest.logUserAction( + QSTileUserAction.Click(null), + TileSpec.create("test_spec"), + hasData = false, + hasTileState = false, + ) + + assertThat(logBuffer.getStringBuffer()) + .contains("tile click: statusBarState=SHADE, hasState=false, hasData=false") + } + + @Test + fun testLogUserActionRejectedByFalsing() { + underTest.logUserActionRejectedByFalsing( + QSTileUserAction.Click(null), + TileSpec.create("test_spec"), + ) + + assertThat(logBuffer.getStringBuffer()).contains("tile click: rejected by falsing") + } + + @Test + fun testLogUserActionRejectedByPolicy() { + underTest.logUserActionRejectedByPolicy( + QSTileUserAction.Click(null), + TileSpec.create("test_spec"), + ) + + assertThat(logBuffer.getStringBuffer()).contains("tile click: rejected by policy") + } + + @Test + fun testLogUserActionPipeline() { + underTest.logUserActionPipeline( + TileSpec.create("test_spec"), + QSTileUserAction.Click(null), + QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}, + "test_data", + ) + + assertThat(logBuffer.getStringBuffer()) + .contains( + "tile click pipeline: " + + "statusBarState=SHADE, " + + "state=[" + + "label=, " + + "state=INACTIVE, " + + "s_label=null, " + + "cd=null, " + + "sd=null, " + + "svi=None, " + + "enabled=ENABLED, " + + "a11y=null" + + "], " + + "data=test_data" + ) + } + + @Test + fun testLogStateUpdate() { + underTest.logStateUpdate( + TileSpec.create("test_spec"), + StateUpdateTrigger.ForceUpdate, + QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}, + "test_data", + ) + + assertThat(logBuffer.getStringBuffer()) + .contains( + "tile state update: " + + "trigger=force, " + + "state=[" + + "label=, " + + "state=INACTIVE, " + + "s_label=null, " + + "cd=null, " + + "sd=null, " + + "svi=None, " + + "enabled=ENABLED, " + + "a11y=null" + + "], " + + "data=test_data" + ) + } + + private fun LogBuffer.getStringBuffer(): String { + val stringWriter = StringWriter() + dump(PrintWriter(stringWriter), 0) + return stringWriter.buffer.toString() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt index 89fa55b319ba..8b6604048ea8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt @@ -29,8 +29,6 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.systemui.SysuiTestCase -import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.DISABLED_ALPHA -import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -62,6 +60,8 @@ class BluetoothTileDialogTest : SysuiTestCase() { @Mock private lateinit var uiEventLogger: UiEventLogger + private val subtitleResId = R.string.quick_settings_bluetooth_tile_subtitle + private lateinit var icon: Pair<Drawable, String> private lateinit var bluetoothTileDialog: BluetoothTileDialog private lateinit var deviceItem: DeviceItem @@ -69,7 +69,13 @@ class BluetoothTileDialogTest : SysuiTestCase() { @Before fun setUp() { bluetoothTileDialog = - BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + BluetoothTileDialog( + ENABLED, + subtitleResId, + bluetoothTileDialogCallback, + uiEventLogger, + mContext + ) icon = Pair(drawable, DEVICE_NAME) deviceItem = DeviceItem( @@ -99,7 +105,13 @@ class BluetoothTileDialogTest : SysuiTestCase() { @Test fun testShowDialog_displayBluetoothDevice() { bluetoothTileDialog = - BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + BluetoothTileDialog( + ENABLED, + subtitleResId, + bluetoothTileDialogCallback, + uiEventLogger, + mContext + ) bluetoothTileDialog.show() bluetoothTileDialog.onDeviceItemUpdated( listOf(deviceItem), @@ -118,49 +130,61 @@ class BluetoothTileDialogTest : SysuiTestCase() { @Test fun testDeviceItemViewHolder_cachedDeviceNotBusy() { deviceItem.isEnabled = true - deviceItem.alpha = ENABLED_ALPHA val view = LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false) val viewHolder = - BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + BluetoothTileDialog( + ENABLED, + subtitleResId, + bluetoothTileDialogCallback, + uiEventLogger, + mContext + ) .Adapter(bluetoothTileDialogCallback) .DeviceItemViewHolder(view) - viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback) - val container = view.requireViewById<View>(R.id.bluetooth_device) - val deviceView = view.requireViewById<View>(R.id.bluetooth_device) + viewHolder.bind(deviceItem, bluetoothTileDialogCallback) + val container = view.requireViewById<View>(R.id.bluetooth_device_row) assertThat(container).isNotNull() assertThat(container.isEnabled).isTrue() - assertThat(container.alpha).isEqualTo(ENABLED_ALPHA) - assertThat(deviceView.hasOnClickListeners()).isTrue() + assertThat(container.hasOnClickListeners()).isTrue() } @Test fun testDeviceItemViewHolder_cachedDeviceBusy() { deviceItem.isEnabled = false - deviceItem.alpha = DISABLED_ALPHA val view = LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false) val viewHolder = - BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + BluetoothTileDialog( + ENABLED, + subtitleResId, + bluetoothTileDialogCallback, + uiEventLogger, + mContext + ) .Adapter(bluetoothTileDialogCallback) .DeviceItemViewHolder(view) - viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback) + viewHolder.bind(deviceItem, bluetoothTileDialogCallback) val container = view.requireViewById<View>(R.id.bluetooth_device_row) - val deviceView = view.requireViewById<View>(R.id.bluetooth_device) assertThat(container).isNotNull() assertThat(container.isEnabled).isFalse() - assertThat(container.alpha).isEqualTo(DISABLED_ALPHA) - assertThat(deviceView.hasOnClickListeners()).isTrue() + assertThat(container.hasOnClickListeners()).isTrue() } @Test fun testOnDeviceUpdated_hideSeeAll_showPairNew() { bluetoothTileDialog = - BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext) + BluetoothTileDialog( + ENABLED, + subtitleResId, + bluetoothTileDialogCallback, + uiEventLogger, + mContext + ) bluetoothTileDialog.show() bluetoothTileDialog.onDeviceItemUpdated( listOf(deviceItem), 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 7157cce8e607..a0ff2ab330b8 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 @@ -95,10 +95,11 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { testScope.backgroundScope, dispatcher, ) - `when`(deviceItemInteractor.deviceItemFlow).thenReturn(MutableStateFlow(null).asStateFlow()) - `when`(bluetoothStateInteractor.updateBluetoothStateFlow) + `when`(deviceItemInteractor.deviceItemUpdate) .thenReturn(MutableStateFlow(null).asStateFlow()) - `when`(deviceItemInteractor.updateDeviceItemsFlow) + `when`(bluetoothStateInteractor.bluetoothStateUpdate) + .thenReturn(MutableStateFlow(null).asStateFlow()) + `when`(deviceItemInteractor.deviceItemUpdateRequest) .thenReturn(MutableStateFlow(Unit).asStateFlow()) `when`(bluetoothStateInteractor.isBluetoothEnabled).thenReturn(true) } @@ -143,7 +144,7 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { bluetoothTileDialogViewModel.showDialog(context, null) assertThat(bluetoothTileDialogViewModel.dialog).isNotNull() - verify(deviceItemInteractor).deviceItemFlow + verify(deviceItemInteractor).deviceItemUpdate } } @@ -153,7 +154,7 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { bluetoothTileDialogViewModel.showDialog(context, null) assertThat(bluetoothTileDialogViewModel.dialog).isNotNull() - verify(bluetoothStateInteractor).updateBluetoothStateFlow + verify(bluetoothStateInteractor).bluetoothStateUpdate } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt index 34519023e316..92c73261a95e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt @@ -69,7 +69,7 @@ class DeviceItemFactoryTest : SysuiTestCase() { val deviceItem = savedDeviceItemFactory.create(context, cachedDevice) assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE) - assertThat(deviceItem.background).isNull() + assertThat(deviceItem.background).isNotNull() } private fun assertDeviceItem(deviceItem: DeviceItem?, deviceItemType: DeviceItemType) { 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 07a95ae330c6..3593075c70fd 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 @@ -109,7 +109,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemFlow.value).isEmpty() + assertThat(interactor.deviceItemUpdate.value).isEmpty() } } @@ -123,7 +123,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemFlow.value).isEmpty() + assertThat(interactor.deviceItemUpdate.value).isEmpty() } } @@ -137,8 +137,8 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemFlow.value).hasSize(1) - assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem1) + assertThat(interactor.deviceItemUpdate.value).hasSize(1) + assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem1) } } @@ -152,9 +152,9 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemFlow.value).hasSize(2) - assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem2) - assertThat(interactor.deviceItemFlow.value!![1]).isEqualTo(deviceItem2) + assertThat(interactor.deviceItemUpdate.value).hasSize(2) + assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem2) + assertThat(interactor.deviceItemUpdate.value!![1]).isEqualTo(deviceItem2) } } @@ -179,7 +179,8 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1)) + assertThat(interactor.deviceItemUpdate.value) + .isEqualTo(listOf(deviceItem2, deviceItem1)) } } @@ -201,7 +202,8 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor.updateDeviceItems(mContext) - assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1)) + assertThat(interactor.deviceItemUpdate.value) + .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 9024c6c5576b..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 @@ -1,21 +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.qs.tiles.viewmodel -import android.graphics.drawable.ShapeDrawable 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 import com.android.systemui.common.shared.model.Icon import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileDataRequest import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger +import com.android.systemui.qs.tiles.base.logging.QSTileLogger import com.android.systemui.qs.tiles.base.viewmodel.BaseQSTileViewModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.launchIn @@ -26,17 +43,22 @@ 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 // TODO(b/299909368): Add more tests @MediumTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() { + @Mock private lateinit var qsTileLogger: QSTileLogger + @Mock private lateinit var qsTileAnalytics: QSTileAnalytics + private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>() private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>() private val fakeDisabledByPolicyInteractor = FakeDisabledByPolicyInteractor() + private val fakeFalsingManager = FalsingManagerFake() private val testCoroutineDispatcher = StandardTestDispatcher() private val testScope = TestScope(testCoroutineDispatcher) @@ -45,6 +67,7 @@ class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() { @Before fun setup() { + MockitoAnnotations.initMocks(this) underTest = createViewModel(testScope) } @@ -79,6 +102,9 @@ class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() { QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {} }, fakeDisabledByPolicyInteractor, + fakeFalsingManager, + qsTileAnalytics, + qsTileLogger, testCoroutineDispatcher, scope.backgroundScope, ) @@ -88,7 +114,7 @@ class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() { val TEST_QS_TILE_CONFIG = QSTileConfig( TileSpec.create("default"), - Icon.Loaded(ShapeDrawable(), null), + Icon.Resource(0, null), 0, InstanceId.fakeInstanceId(0), ) 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/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..b4f9e8dcfb39 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 @@ -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 @@ -144,6 +145,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel @Mock lateinit var keyEventInteractor: KeyEventInteractor + @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() @@ -248,6 +252,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { ), BouncerLogger(logcatLogBuffer("BouncerLog")), keyEventInteractor, + 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 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..189c9e25d9b0 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 @@ -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( @@ -250,6 +254,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { ), BouncerLogger(logcatLogBuffer("BouncerLog")), Mockito.mock(KeyEventInteractor::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/data/repository/FakeKeyguardStatusBarRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt new file mode 100644 index 000000000000..f1e6a053643f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt @@ -0,0 +1,23 @@ +/* + * 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.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeKeyguardStatusBarRepository : KeyguardStatusBarRepository { + override val isKeyguardUserSwitcherEnabled = MutableStateFlow(false) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt new file mode 100644 index 000000000000..b1c994c2374e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.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.statusbar.data.repository + +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.user.data.repository.FakeUserSwitcherRepository +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +class KeyguardStatusBarRepositoryImplTest : SysuiTestCase() { + private val testScope = TestScope() + private val configurationController = mock<ConfigurationController>() + private val userSwitcherRepository = FakeUserSwitcherRepository() + + val underTest = + KeyguardStatusBarRepositoryImpl( + context, + configurationController, + userSwitcherRepository, + ) + + private val configurationListener: ConfigurationController.ConfigurationListener + get() { + val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() + verify(configurationController).addCallback(capture(captor)) + return captor.value + } + + @Test + fun isKeyguardUserSwitcherEnabled_switcherNotEnabled_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + + userSwitcherRepository.isEnabled.value = false + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_keyguardConfigNotEnabled_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + userSwitcherRepository.isEnabled.value = true + + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_switchEnabledAndKeyguardConfigEnabled_true() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + + userSwitcherRepository.isEnabled.value = true + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + + assertThat(latest).isTrue() + } + + @Test + fun isKeyguardUserSwitcherEnabled_refetchedOnSmallestWidthChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + userSwitcherRepository.isEnabled.value = true + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + assertThat(latest).isTrue() + + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + configurationListener.onSmallestScreenWidthChanged() + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_refetchedOnDensityChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + userSwitcherRepository.isEnabled.value = true + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + assertThat(latest).isTrue() + + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + configurationListener.onDensityOrFontScaleChanged() + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_refetchedOnEnabledChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + + userSwitcherRepository.isEnabled.value = false + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + assertThat(latest).isFalse() + + // WHEN the switcher becomes enabled but the keyguard switcher becomes disabled + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + userSwitcherRepository.isEnabled.value = true + + // THEN the value is still false because the keyguard config is refetched + assertThat(latest).isFalse() + } +} 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/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/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..236bcb416573 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 @@ -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..93faa77bef5c 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 @@ -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/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/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index c0d248ea15e7..648438997166 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -64,12 +64,13 @@ import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.domain.interactor.PowerInteractorFactory; import com.android.systemui.res.R; import com.android.systemui.scene.SceneTestUtils; -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository; +import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -156,7 +157,6 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { public void setup() throws Exception { mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false); mShadeViewStateProvider = new TestShadeViewStateProvider(); - mShadeViewStateProvider = new TestShadeViewStateProvider(); MockitoAnnotations.initMocks(this); @@ -176,7 +176,9 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mViewModel = new KeyguardStatusBarViewModel( mTestScope.getBackgroundScope(), - mKeyguardInteractor); + mKeyguardInteractor, + new KeyguardStatusBarInteractor(new FakeKeyguardStatusBarRepository()), + mBatteryController); allowTestableLooperAsMainThread(); TestableLooper.get(this).runWithLooper(() -> { @@ -320,6 +322,15 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + public void setBatteryListening_true_flagOn_callbackNotAdded() { + mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true); + + mController.setBatteryListening(true); + + verify(mBatteryController, never()).addCallback(any()); + } + + @Test public void updateTopClipping_viewClippingUpdated() { int viewTop = 20; mKeyguardStatusBarView.setTop(viewTop); 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..b36d09df5929 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,9 @@ 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 kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -33,7 +36,6 @@ 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; import android.testing.AndroidTestingRunner; @@ -175,6 +177,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { 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 +764,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/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/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/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/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt index f4078d59bbdb..1bc346de1568 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt @@ -29,13 +29,23 @@ import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.SceneTestUtils import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository +import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor +import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test +import org.mockito.Mockito.verify @SmallTest +@OptIn(ExperimentalCoroutinesApi::class) class KeyguardStatusBarViewModelTest : SysuiTestCase() { private val testScope = TestScope() private val sceneTestUtils = SceneTestUtils(this) @@ -54,11 +64,18 @@ class KeyguardStatusBarViewModelTest : SysuiTestCase() { ) { sceneTestUtils.sceneInteractor() } + private val keyguardStatusBarInteractor = + KeyguardStatusBarInteractor( + FakeKeyguardStatusBarRepository(), + ) + private val batteryController = mock<BatteryController>() private val underTest = KeyguardStatusBarViewModel( testScope.backgroundScope, keyguardInteractor, + keyguardStatusBarInteractor, + batteryController, ) @Test @@ -102,4 +119,46 @@ class KeyguardStatusBarViewModelTest : SysuiTestCase() { assertThat(latest).isTrue() } + + @Test + fun isBatteryCharging_matchesCallback() = + testScope.runTest { + val latest by collectLastValue(underTest.isBatteryCharging) + runCurrent() + + val captor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() + verify(batteryController).addCallback(capture(captor)) + val callback = captor.value + + callback.onBatteryLevelChanged( + /* level= */ 2, + /* pluggedIn= */ false, + /* charging= */ true, + ) + + assertThat(latest).isTrue() + + callback.onBatteryLevelChanged( + /* level= */ 2, + /* pluggedIn= */ true, + /* charging= */ false, + ) + + assertThat(latest).isFalse() + } + + @Test + fun isBatteryCharging_unregistersWhenNotListening() = + testScope.runTest { + val job = underTest.isBatteryCharging.launchIn(this) + runCurrent() + + val captor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() + verify(batteryController).addCallback(capture(captor)) + + job.cancel() + runCurrent() + + verify(batteryController).removeCallback(captor.value) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserSwitcherRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserSwitcherRepository.kt new file mode 100644 index 000000000000..758fe93a658c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserSwitcherRepository.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.user.data.repository + +import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeUserSwitcherRepository : UserSwitcherRepository { + override val isEnabled = MutableStateFlow(false) + override val userSwitcherStatus = + MutableStateFlow<UserSwitcherStatusModel>(UserSwitcherStatusModel.Disabled) +} 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/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/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 cd009dff27ff..d6632a3c5ea7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -38,16 +38,12 @@ import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.settingslib.bluetooth.LocalBluetoothManager; 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.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.phone.SystemUIDialogManager; import org.junit.After; @@ -128,20 +124,8 @@ 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); - // A lot of tests get the FalsingManager, often via several layers of indirection. - // None of them actually need it. - mDependency.injectTestDependency(FalsingManager.class, new FalsingManagerFake()); mDependency.injectMockDependency(KeyguardUpdateMonitor.class); - // A lot of tests get the LocalBluetoothManager, often via several layers of indirection. - // None of them actually need it. - mDependency.injectMockDependency(LocalBluetoothManager.class); - - // Notifications tests are injecting one of these, causing many classes (including - // KeyguardUpdateMonitor to be created (injected). - // TODO(b/1531701009) Clean up NotificationContentView creation to prevent this - mDependency.injectMockDependency(SmartReplyController.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. 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/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/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt index 1a893f8c523c..bf77b1a050cd 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 @@ -36,8 +36,6 @@ import com.android.systemui.qs.FgsManagerController import com.android.systemui.qs.QSSecurityFooterUtils import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepositoryImpl -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepositoryImpl import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel @@ -51,6 +49,8 @@ import com.android.systemui.statusbar.policy.FakeUserInfoController import com.android.systemui.statusbar.policy.SecurityController import com.android.systemui.statusbar.policy.UserInfoController import com.android.systemui.statusbar.policy.UserSwitcherController +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 diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt new file mode 100644 index 000000000000..201926df5a5c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.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.qs.tiles.viewmodel + +import androidx.annotation.StringRes +import com.android.internal.logging.InstanceId +import com.android.systemui.common.shared.model.ContentDescription +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.qs.pipeline.shared.TileSpec + +object QSTileConfigTestBuilder { + + fun build(configure: BuildingScope.() -> Unit = {}): QSTileConfig = + BuildingScope().apply(configure).build() + + class BuildingScope { + var tileSpec: TileSpec = TileSpec.create("test_spec") + var tileIcon: Icon = Icon.Resource(0, ContentDescription.Resource(0)) + @StringRes var tileLabel: Int = 0 + var instanceId: InstanceId = InstanceId.fakeInstanceId(0) + var metricsSpec: String = tileSpec.spec + var policy: QSTilePolicy = QSTilePolicy.NoRestrictions + + fun build() = + QSTileConfig( + tileSpec, + tileIcon, + tileLabel, + instanceId, + metricsSpec, + policy, + ) + } +} 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 499295c25883..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 @@ -19,7 +19,6 @@ package com.android.systemui.scene import android.content.pm.UserInfo import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable -import android.util.Log import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel import com.android.systemui.authentication.data.repository.AuthenticationRepository @@ -81,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 { @@ -206,7 +209,7 @@ class SceneTestUtils( return BouncerInteractor( applicationScope = applicationScope(), applicationContext = context, - repository = BouncerRepository(), + repository = BouncerRepository(featureFlags), deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, @@ -220,7 +223,6 @@ class SceneTestUtils( authenticationInteractor: AuthenticationInteractor, users: List<UserViewModel> = createUsers(), ): BouncerViewModel { - Log.d("ALE", "users=$users") return BouncerViewModel( applicationContext = context, applicationScope = applicationScope(), 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/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/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java index eb7e9aed1944..1866a9983495 100644 --- a/rs/java/android/renderscript/ScriptC.java +++ b/rs/java/android/renderscript/ScriptC.java @@ -16,12 +16,9 @@ package android.renderscript; -import android.app.compat.CompatChanges; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.content.res.Resources; -import android.util.Slog; +import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -38,15 +35,6 @@ public class ScriptC extends Script { private static final String TAG = "ScriptC"; /** - * In targetSdkVersion 35 and above, Renderscript's ScriptC stops being supported - * and an exception is thrown when the class is instantiated. - * In targetSdkVersion 34 and below, Renderscript's ScriptC still works. - */ - @ChangeId - @EnabledAfter(targetSdkVersion = 35) - private static final long RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID = 297019750L; - - /** * Only intended for use by the generated derived classes. * * @param id @@ -101,20 +89,7 @@ public class ScriptC extends Script { setID(id); } - private static void throwExceptionIfSDKTooHigh() { - String message = - "ScriptC scripts are not supported when targeting an API Level >= 35. Please refer " - + "to https://developer.android.com/guide/topics/renderscript/migration-guide " - + "for proposed alternatives."; - Slog.w(TAG, message); - if (CompatChanges.isChangeEnabled(RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID)) { - throw new UnsupportedOperationException(message); - } - } - - private static synchronized long internalCreate( - RenderScript rs, Resources resources, int resourceID) { - throwExceptionIfSDKTooHigh(); + private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) { byte[] pgm; int pgmLength; InputStream is = resources.openRawResource(resourceID); @@ -150,7 +125,6 @@ public class ScriptC extends Script { } private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) { - throwExceptionIfSDKTooHigh(); // Log.v(TAG, "Create script for resource = " + resName); return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length); } 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..123b65c039ba 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -5,4 +5,11 @@ flag { namespace: "autofill" description: "Test flag " bug: "297380045" -}
\ No newline at end of file +} + +flag { + name: "relayout" + namespace: "autofill" + description: "Mitigation for relayout issue" + bug: "294330426" +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 0220deca18c1..265ed4652bfb 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; } diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 9dd0dca47f0e..852e36dcc605 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -218,6 +218,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController void setActivityLaunchDefaultAllowed(boolean activityLaunchDefaultAllowed) { synchronized (mGenericWindowPolicyControllerLock) { + if (mActivityLaunchAllowedByDefault != activityLaunchDefaultAllowed) { + mActivityPolicyExemptions.clear(); + } mActivityLaunchAllowedByDefault = activityLaunchDefaultAllowed; } } diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/services/core/java/com/android/server/ExplicitHealthCheckController.java index 20de40e73130..3d610d3747c9 100644 --- a/services/core/java/com/android/server/ExplicitHealthCheckController.java +++ b/services/core/java/com/android/server/ExplicitHealthCheckController.java @@ -343,7 +343,7 @@ class ExplicitHealthCheckController { }; mContext.bindServiceAsUser(intent, mConnection, - Context.BIND_AUTO_CREATE, UserHandle.of(UserHandle.USER_SYSTEM)); + Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); Slog.i(TAG, "Explicit health check service is bound"); } } 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..93dca2f4f192 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -359,8 +359,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); diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index c094c12cc6d8..dd543349fc4d 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -522,7 +522,8 @@ public class RescueParty { Exception res = null; final ContentResolver resolver = context.getContentResolver(); try { - Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM); + Settings.Global.resetToDefaultsAsUser(resolver, null, mode, + UserHandle.SYSTEM.getIdentifier()); } catch (Exception e) { res = new RuntimeException("Failed to reset global settings", e); } @@ -779,12 +780,13 @@ public class RescueParty { } private static int[] getAllUserIds() { - int[] userIds = { UserHandle.USER_SYSTEM }; + int systemUserId = UserHandle.SYSTEM.getIdentifier(); + int[] userIds = { systemUserId }; try { for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) { try { final int userId = Integer.parseInt(file.getName()); - if (userId != UserHandle.USER_SYSTEM) { + if (userId != systemUserId) { userIds = ArrayUtils.appendInt(userIds, userId); } } catch (NumberFormatException ignored) { 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 6e984bb4bb7b..55aa7164a67b 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -330,7 +330,7 @@ public class Watchdog implements Dumpable { String describeBlockedStateLocked() { final String prefix; if (mCurrentMonitor == null) { - prefix = "Blocked in handler on "; + prefix = "Blocked in handler"; } else { prefix = "Blocked in monitor " + mCurrentMonitor.getClass().getName(); } @@ -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/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/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/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 0615ecf029fa..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; @@ -281,8 +282,8 @@ public final class ProcessList { // don't have an oom adj assigned by the system). public static final int NATIVE_ADJ = -1000; - // Memory pages are 4K. - static final int PAGE_SIZE = 4 * 1024; + // Memory page size. + static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE); // Activity manager's version of an undefined schedule group static final int SCHED_GROUP_UNDEFINED = Integer.MIN_VALUE; @@ -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/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/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index b6273e1daf82..e66fa5b3d516 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -51,22 +51,10 @@ public class DisplayManagerFlags { 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); - /** Returns whether connected display management is enabled or not. */ public boolean isConnectedDisplayManagementEnabled() { return mConnectedDisplayManagementFlagState.isEnabled(); @@ -90,7 +78,7 @@ public class DisplayManagerFlags { /** Returns whether resolution range voting feature is enabled or not. */ public boolean isDisplayResolutionRangeVotingEnabled() { - return mDisplayResolutionRangeVotingState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** @@ -98,7 +86,7 @@ public class DisplayManagerFlags { * {@link com.android.server.display.mode.DisplayModeDirector} */ public boolean isUserPreferredModeVoteEnabled() { - return mUserPreferredModeVoteState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** @@ -112,7 +100,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 */ @@ -150,19 +138,20 @@ public class DisplayManagerFlags { } private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) { - // TODO(b/299462337) Remove when the infrastructure is ready. - if ((Build.IS_ENG || Build.IS_USERDEBUG) - && SystemProperties.getBoolean("persist.sys." + flagName, false)) { - return true; - } + boolean flagValue = false; try { - return flagFunction.get(); + flagValue = flagFunction.get(); } catch (Throwable ex) { if (DEBUG) { Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex); } - return false; } + // TODO(b/299462337) Remove when the infrastructure is ready. + 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/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index a680f5001479..cd3d2f031455 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -16,6 +16,8 @@ package com.android.server.graphics.fonts; +import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE; + import static com.android.server.graphics.fonts.FontManagerService.SystemFontException; import android.annotation.NonNull; @@ -581,7 +583,8 @@ final class UpdatableFontDir { font.getFontStyle(), font.getIndex(), font.getFontVariationSettings(), null)); } FontConfig.FontFamily family = new FontConfig.FontFamily(resolvedFonts, - LocaleList.getEmptyLocaleList(), FontConfig.FontFamily.VARIANT_DEFAULT); + LocaleList.getEmptyLocaleList(), FontConfig.FontFamily.VARIANT_DEFAULT, + VARIABLE_FONT_FAMILY_TYPE_NONE); return new FontConfig.NamedFamilyList(Collections.singletonList(family), fontFamily.getName()); } 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/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java index c83a96938325..431aabdc3018 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java @@ -69,27 +69,16 @@ final class InputMethodSubtypeSwitchingController { mIsSystemLanguage = true; } else { // TODO: Use Locale#getLanguage or Locale#toLanguageTag - final String systemLanguage = parseLanguageFromLocaleString(systemLocale); - final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale); + final String systemLanguage = LocaleUtils.getLanguageFromLocaleString( + systemLocale); + final String subtypeLanguage = LocaleUtils.getLanguageFromLocaleString( + subtypeLocale); mIsSystemLanguage = systemLanguage.length() >= 2 && systemLanguage.equals(subtypeLanguage); } } } - /** - * Returns the language component of a given locale string. - * TODO: Use {@link Locale#getLanguage()} instead. - */ - private static String parseLanguageFromLocaleString(final String locale) { - final int idx = locale.indexOf('_'); - if (idx < 0) { - return locale; - } else { - return locale.substring(0, idx); - } - } - private static int compareNullableCharSequences(@Nullable CharSequence c1, @Nullable CharSequence c2) { // For historical reasons, an empty text needs to put at the last. @@ -116,7 +105,7 @@ final class InputMethodSubtypeSwitchingController { * * @param other the object to be compared. * @return a negative integer, zero, or positive integer as this object is less than, equal - * to, or greater than the specified <code>other</code> object. + * to, or greater than the specified <code>other</code> object. */ @Override public int compareTo(ImeSubtypeListItem other) { @@ -253,9 +242,10 @@ final class InputMethodSubtypeSwitchingController { /** * Returns the index of the specified input method and subtype in the given list. - * @param imi The {@link InputMethodInfo} to be searched. + * + * @param imi The {@link InputMethodInfo} to be searched. * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method - * does not have a subtype. + * does not have a subtype. * @return The index in the given list. -1 if not found. */ private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) { @@ -327,6 +317,7 @@ final class InputMethodSubtypeSwitchingController { * {@link #mUsageHistoryOfSubtypeListItemIndex}. * <p>We call the index of {@link #mUsageHistoryOfSubtypeListItemIndex} as "Usage Rank" * so as not to be confused with the index in {@link #mImeSubtypeList}. + * * @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually. */ private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) { diff --git a/services/core/java/com/android/server/inputmethod/LocaleUtils.java b/services/core/java/com/android/server/inputmethod/LocaleUtils.java index f865e6010580..7d090dbcc5d8 100644 --- a/services/core/java/com/android/server/inputmethod/LocaleUtils.java +++ b/services/core/java/com/android/server/inputmethod/LocaleUtils.java @@ -215,12 +215,7 @@ final class LocaleUtils { * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(String)} */ static String getLanguageFromLocaleString(String locale) { - final int idx = locale.indexOf('_'); - if (idx < 0) { - return locale; - } else { - return locale.substring(0, idx); - } + return Locale.forLanguageTag(locale).getLanguage(); } static Locale getSystemLocaleFromContext(Context context) { 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 83a3125884ac..c9528d8257c4 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -707,7 +707,8 @@ class MediaRouter2ServiceImpl { } private boolean checkCallerHasSystemRoutingPermissions(int pid, int uid) { - return checkCallerHasModifyAudioRoutingPermission(pid, uid); + return checkCallerHasModifyAudioRoutingPermission(pid, uid) + || checkCallerHasBluetoothPermissions(pid, uid); } private boolean checkCallerHasModifyAudioRoutingPermission(int pid, int uid) { 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..87c30674bfb7 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; @@ -6698,7 +6705,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 +7196,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 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..a99a0c05f3bd 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; } 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/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java index b8c2b8616ea6..1f12c88ede36 100644 --- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java +++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java @@ -50,7 +50,7 @@ class PackageMonitorCallbackHelper { @NonNull private final Object mLock = new Object(); - final IActivityManager mActivityManager = ActivityManager.getService(); + IActivityManager mActivityManager; @NonNull @GuardedBy("mLock") @@ -149,6 +149,9 @@ class PackageMonitorCallbackHelper { try { final int[] resolvedUserIds; if (userIds == null) { + if (mActivityManager == null) { + mActivityManager = ActivityManager.getService(); + } if (mActivityManager == null) return; resolvedUserIds = mActivityManager.getRunningUserIds(); } 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..68a8e40d0528 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -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; @@ -4374,15 +4376,17 @@ 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) { @@ -4431,6 +4435,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: * @@ -4869,7 +4880,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 +4915,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 +5011,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 +6467,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/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/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/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/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..882104a297ef 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", diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 710913712aa0..78afaa880584 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, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 82452ccc3d06..c9107e8cf5f3 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; @@ -2493,14 +2494,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 +4630,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 +4644,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 +8848,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 +8857,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 +8929,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 +8948,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 +9181,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 +9229,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 +9243,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 +9472,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/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/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index cee2524657dc..bb24d514e14b 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) } 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 c7a71eeb969c..6eacef767042 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -30,12 +30,15 @@ java_test_host { libs: [ "tradefed", "junit", - "truth-prebuilt", + "truth", ], static_libs: [ "ApexInstallHelper", + "android.security.flags-aconfig-java-host", "cts-host-utils", + "flag-junit-host", "frameworks-base-hostutils", + "kotlin-test", "PackageManagerServiceHostTestsIntentVerifyUtils", "block_device_writer_jar", ], @@ -59,6 +62,7 @@ java_test_host { ":PackageManagerTestAppUsesStaticLibrary", ":PackageManagerTestAppVersion1", ":PackageManagerTestAppVersion2", + ":PackageManagerTestAppVersion2AltKey", ":PackageManagerTestAppVersion3", ":PackageManagerTestAppVersion3Invalid", ":PackageManagerTestAppVersion4", diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt new file mode 100644 index 000000000000..c4906041ea5d --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/TamperedUpdatedSystemPackageTest.kt @@ -0,0 +1,178 @@ +/* + * 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.pm.test + +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.host.HostFlagsValueProvider +import com.android.internal.util.test.SystemPreparer +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth.assertThat +import java.io.File +import java.io.RandomAccessFile +import kotlin.test.assertNotNull +import org.junit.After +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith + +@RunWith(DeviceJUnit4ClassRunner::class) +@RequiresFlagsEnabled(android.security.Flags.FLAG_EXTEND_VB_CHAIN_TO_UPDATED_APK) +class TamperedUpdatedSystemPackageTest : BaseHostJUnit4Test() { + + companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO_ALT_KEY = "PackageManagerTestAppVersion2AltKey.apk" + private const val VERSION_TWO_ALT_KEY_IDSIG = + "PackageManagerTestAppVersion2AltKey.apk.idsig" + private const val STRICT_SIGNATURE_CONFIG_PATH = + "/system/etc/sysconfig/preinstalled-packages-strict-signature.xml" + private const val TIMESTAMP_REFERENCE_FILE_PATH = "/data/local/tmp/timestamp.ref" + + @get:ClassRule + val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) + } + + private val tempFolder = TemporaryFolder() + private val preparer: SystemPreparer = SystemPreparer( + tempFolder, + SystemPreparer.RebootStrategy.FULL, + deviceRebootRule + ) { this.device } + private val productPath = + HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT) + private lateinit var originalConfigFile: File + + @Rule + @JvmField + val checkFlagsRule = HostFlagsValueProvider.createCheckFlagsRule({ getDevice() }) + + @Rule + @JvmField + val rules = RuleChain.outerRule(tempFolder).around(preparer)!! + + @Before + @After + fun removeApk() { + device.uninstallPackage(TEST_PKG_NAME) + } + + @Before + fun backupAndModifySystemFiles() { + // Backup + device.pullFile(STRICT_SIGNATURE_CONFIG_PATH).also { + assertNotNull(it) + originalConfigFile = it + } + + // Modify to allowlist the target package on device for testing the feature + val xml = tempFolder.newFile().apply { + val newConfigText = originalConfigFile + .readText() + .replace( + "</config>", + "<require-strict-signature package=\"${TEST_PKG_NAME}\"/></config>" + ) + writeText(newConfigText) + } + device.remountSystemWritable() + device.pushFile(xml, STRICT_SIGNATURE_CONFIG_PATH) + } + + @After + fun restoreSystemFiles() { + device.remountSystemWritable() + device.pushFile(originalConfigFile, STRICT_SIGNATURE_CONFIG_PATH) + // Files pushed via a SystemPreparer are deleted automatically. + } + + @Test + fun detectApkAndXmlTamperingAtBoot() { + // Set up the scenario where both APK and packages.xml are tampered by the attacker. + // This is done by booting with the "bad" APK in a system partition, re-installing it to + // /data. Then, replace the APK in the system partition with a "good" one. + preparer.pushResourceFile(VERSION_TWO_ALT_KEY, productPath.toString()) + .reboot() + + // Install the "bad" APK to /data. This will also update package manager's XML records. + val versionTwoFile = HostUtils.copyResourceToHostFile( + VERSION_TWO_ALT_KEY, + tempFolder.newFile() + ) + assertThat(device.installPackage(versionTwoFile, true)).isNull() + assertThat(device.executeShellCommand("pm path ${TEST_PKG_NAME}")) + .doesNotContain(productPath.toString()) + + // "Restore" the system partition is to a good state with correct APK. + preparer.deleteFile(productPath.toString()) + .pushResourceFile(VERSION_ONE, productPath.toString()) + + // Verify that upon the next boot, the system detect the problem and remove the problematic + // APK in the /data. + preparer.reboot() + assertThat(device.executeShellCommand("pm path ${TEST_PKG_NAME}")) + .contains(productPath.toString()) + } + + @Test + fun detectApkTamperingAtBoot() { + // Set up the scenario where APK is tampered but not the v4 signature. First, inject a + // good APK as a system app. + preparer.pushResourceFile(VERSION_TWO_ALT_KEY, productPath.toString()) + .reboot() + + // Re-install the target APK to /data, with the corresponding .idsig from build time. + val versionTwoFile = HostUtils.copyResourceToHostFile( + VERSION_TWO_ALT_KEY, + tempFolder.newFile() + ) + assertThat(device.installPackage(versionTwoFile, true)).isNull() + val baseApkPath = device.executeShellCommand("pm path ${TEST_PKG_NAME}") + .lineSequence() + .first() + .replace("package:", "") + assertThat(baseApkPath).doesNotContain(productPath.toString()) + preparer.pushResourceFile(VERSION_TWO_ALT_KEY_IDSIG, baseApkPath.toString() + ".idsig") + + // Replace the APK in /data with a tampered version. Restore fs-verity and attributes. + RandomAccessFile(versionTwoFile, "rw").use { + // Skip the zip local file header to keep it valid. Tamper with the file name field and + // beyond, just so that it won't simply fail. + it.seek(30) + it.writeBytes("tamper") + } + device.executeShellCommand("touch ${TIMESTAMP_REFERENCE_FILE_PATH} -r $baseApkPath") + preparer.pushFile(versionTwoFile, baseApkPath) + device.executeShellCommand( + "cd ${baseApkPath.replace("base.apk", "")}" + + "&& chown system:system base.apk " + + "&& /data/local/tmp/fsverity_multilib enable base.apk" + + "&& touch base.apk -r ${TIMESTAMP_REFERENCE_FILE_PATH}" + ) + + // Verify that upon the next boot, the system detect the problem and remove the problematic + // APK in the /data. + preparer.reboot() + assertThat(device.executeShellCommand("pm path ${TEST_PKG_NAME}")) + .contains(productPath.toString()) + } +} 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/Generic/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp index 5cc3371a1a6e..bee7c4019fc1 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp @@ -66,3 +66,13 @@ android_test_helper_app { "src/**/*.kt", ], } + +android_test_helper_app { + name: "PackageManagerTestAppVersion2AltKey", + manifest: "AndroidManifestVersion2.xml", + srcs: [ + "src/**/*.kt", + ], + certificate: ":FrameworksServicesTests_keyset_A_cert", + v4_signature: true, +} 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/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/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/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/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index 037637630b7a..184c976b86bf 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 @@ -23,6 +23,7 @@ import static org.junit.Assert.fail; import android.content.Context; import android.graphics.FontListParser; +import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontManager; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontUpdateRequest; @@ -330,7 +331,8 @@ public final class UpdatableFontDirTest { FontConfig.FontFamily family = new FontConfig.FontFamily( Arrays.asList(fooFont, barFont), null, - FontConfig.FontFamily.VARIANT_DEFAULT); + FontConfig.FontFamily.VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE); return new FontConfig(Collections.emptyList(), Collections.emptyList(), Collections.singletonList(new FontConfig.NamedFamilyList( @@ -491,7 +493,8 @@ public final class UpdatableFontDirTest { file, null, "bar", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, null); FontConfig.FontFamily family = new FontConfig.FontFamily( - Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT); + Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT, + FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE); return new FontConfig( Collections.emptyList(), Collections.emptyList(), @@ -644,7 +647,8 @@ public final class UpdatableFontDirTest { file, null, "test", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, null); FontConfig.FontFamily family = new FontConfig.FontFamily( - Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT); + Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT, + 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); diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/LocaleUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/LocaleUtilsTest.java index 3fc0e4fa4a2c..255cb6499d8a 100644 --- a/services/tests/servicestests/src/com/android/server/inputmethod/LocaleUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/inputmethod/LocaleUtilsTest.java @@ -16,6 +16,8 @@ package com.android.server.inputmethod; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import android.os.LocaleList; @@ -386,4 +388,10 @@ public class LocaleUtilsTest { assertEquals(availableLocales.get(3), dest.get(0)); } } + + @Test + public void testGetLanguageFromLocaleString() { + assertThat(LocaleUtils.getLanguageFromLocaleString("en")).isEqualTo("en"); + assertThat(LocaleUtils.getLanguageFromLocaleString("en-US")).isEqualTo("en"); + } } diff --git a/services/tests/servicestests/src/com/android/server/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/timezone/OWNERS index 61652604ee9f..d64cbcdc2814 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/OWNERS +++ b/services/tests/servicestests/src/com/android/server/timezone/OWNERS @@ -1,2 +1,2 @@ -# Bug component: 24949 +# Bug component: 847766 include /services/core/java/com/android/server/timezone/OWNERS diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 2dacda0af7f4..d1f4961ab7e5 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,7 +39,7 @@ android_test { "hamcrest-library", "servicestests-utils", "testables", - "truth-prebuilt", + "truth", // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", 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..c05f81497e57 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -462,7 +462,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) { - assertEquals(a.getRankingMap(), b.getRankingMap()); + detailedAssertEquals(a.getRankingMap(), b.getRankingMap()); } private void detailedAssertEquals(String comment, Ranking a, Ranking b) { 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..91129a14ecab 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; @@ -4828,6 +4836,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 +4902,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 +4921,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 +4945,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 +4966,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 +4985,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 +11381,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 +12679,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 75716b913cee..474720f68731 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1081,6 +1081,7 @@ public class TransitionTests extends WindowTestsBase { makeWindowVisible(windows); mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs); mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); + mDisplayContent.mTransitionController.setSyncEngine(createTestBLASTSyncEngine()); final TestTransitionPlayer player = registerTestTransitionPlayer(); mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); @@ -1441,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); @@ -1481,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(), @@ -1514,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 58b5ae556f19..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,61 +1621,54 @@ 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); } + alarmQueue.addAlarm(packageName, nowElapsed + (estimatedLaunchTime - now)); } - if (changedTimes) { - mHandler.sendEmptyMessage(MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED); - } + } + if (changedTimes) { + mHandler.sendEmptyMessage(MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED); } } @@ -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); @@ -2587,11 +2612,12 @@ public class UsageStatsService extends SystemService implements @Override public void reportChooserSelection(@NonNull String packageName, int userId, @NonNull String contentType, String[] annotations, @NonNull String action) { - // A valid package name, content type, and action must be provided for these events - Objects.requireNonNull(packageName); - Objects.requireNonNull(contentType); - Objects.requireNonNull(action); - if (contentType.isBlank() || action.isBlank()) { + if (packageName == null) { + throw new IllegalArgumentException("Package selection must not be null."); + } + // A valid contentType and action must be provided for chooser selection events. + if (contentType == null || contentType.isBlank() + || action == null || action.isBlank()) { return; } diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 7d9b3790c1b5..a7675d694a24 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -1894,7 +1894,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/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/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/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/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/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: [ |