Merge "[aapt2] Generate @FlaggedApi annotations" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 95b6155..003b7f8 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,6 +21,7 @@
":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 @@
":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 @@
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 @@
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 a4a0b4b..887f7fe 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -13,6 +13,10 @@
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 0000000..24ecd3d
--- /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 0000000..4e3cb7d
--- /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 f252a0b..158d914 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 @@
"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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 43d2ae9..f47766e 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 @@
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 6d938de..45f15db 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 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 @@
// 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 @@
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 45e70719..222275f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -82,7 +82,6 @@
"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 fbcaa52..8d8fc12 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -195,6 +195,8 @@
doc_defaults {
name: "framework-docs-default",
+ sdk_version: "none",
+ system_modules: "none",
libs: framework_docs_only_libs + [
"stub-annotations",
"unsupportedappusage",
@@ -209,6 +211,7 @@
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 83804c6..8df6dab 100644
--- a/api/api.go
+++ b/api/api.go
@@ -115,6 +115,7 @@
}
type Bazel_module struct {
+ Label *string
Bp2build_available *bool
}
type bazelProperties struct {
@@ -141,6 +142,8 @@
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 @@
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 @@
},
}
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 @@
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 1f4c2af..70f2162 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -33,6 +33,8 @@
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 @@
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 @@
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 0000000..762d9d1
--- /dev/null
+++ b/api/javadoc-lint-baseline
@@ -0,0 +1,336 @@
+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: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 "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: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/telecom/Call.java:94: lint: unable to parse link/see tag: #playDtmfTone(char [101]
+android/telecom/CallControl.java:163: lint: Unresolved link/see tag "android.telecom.CallStreamingService CallStreamingService" in android.telecom.CallControl [101]
+android/telecom/CallControlCallback.java:63: lint: Unresolved link/see tag "android.telecom.CallAttributes.CallType" in android.telecom.CallControlCallback [101]
+android/telecom/PhoneAccountSuggestion.java:17: lint: Unresolved link/see tag "android.telecom.PhoneAccountSuggestionService PhoneAccountSuggestionService" in android.telecom.PhoneAccountSuggestion [101]
+android/telephony/CarrierConfigManager.java:2934: lint: Unresolved link/see tag "android.telephony.TelephonyManager.PremiumCapability TelephonyManager.PremiumCapability" in android.telephony.CarrierConfigManager [101]
+android/telephony/CarrierConfigManager.java:4020: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:4108: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:3920: lint: Unresolved link/see tag "android.telephony.ims.SipDelegateManager" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:4001: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/CarrierConfigManager.java:4089: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
+android/telephony/NetworkRegistrationInfo.java:131: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.NetworkRegistrationInfo [101]
+android/telephony/PhoneStateListener.java:293: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.PhoneStateListener [101]
+android/telephony/PhoneStateListener.java:544: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.PhoneStateListener [101]
+android/telephony/SubscriptionManager.java:1385: lint: Unresolved link/see tag "Build.VERSION_CODES.P" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
+android/telephony/SubscriptionManager.java:1385: lint: Unresolved link/see tag "Build.VERSION_CODES.V" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
+android/telephony/SubscriptionPlan.java:134: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.SubscriptionPlan [101]
+android/telephony/SubscriptionPlan.java:288: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.SubscriptionPlan.Builder [101]
+android/telephony/TelephonyCallback.java:113: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.TelephonyCallback.CallDisconnectCauseListener [101]
+android/telephony/TelephonyManager.java:2544: lint: Unresolved link/see tag "android.telephony.TelephonyManager.CarrierRestrictionStatus CarrierRestrictionStatus" in android.telephony.TelephonyManager [101]
+android/telephony/TelephonyManager.java:2841: lint: Unresolved link/see tag "android.telephony.TelephonyManager.SetOpportunisticSubscriptionResult TelephonyManager.SetOpportunisticSubscriptionResult" in android.telephony.TelephonyManager [101]
+android/telephony/TelephonyManager.java:3273: lint: Unresolved link/see tag "android.telephony.TelephonyManager.PurchasePremiumCapabilityResult PurchasePremiumCapabilityResult" in android.telephony.TelephonyManager [101]
+android/telephony/TelephonyManager.java:3989: lint: Unresolved link/see tag "android.telephony.TelephonyManager.MobileDataPolicy MobileDataPolicy" in android.telephony.TelephonyManager [101]
+android/telephony/data/ApnSetting.java:39: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV4() DataCallResponse#getMtuV4()" in android.telephony.data.ApnSetting [101]
+android/telephony/data/ApnSetting.java:50: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV6() DataCallResponse#getMtuV6()" in android.telephony.data.ApnSetting [101]
+android/telephony/data/ApnSetting.java:597: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV4() DataCallResponse#getMtuV4()" in android.telephony.data.ApnSetting.Builder [101]
+android/telephony/data/ApnSetting.java:611: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV6() DataCallResponse#getMtuV6()" in android.telephony.data.ApnSetting.Builder [101]
+android/telephony/euicc/EuiccManager.java:331: lint: Unresolved link/see tag "android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE" in android.telephony.euicc.EuiccManager [101]
+android/telephony/ims/ImsMmTelManager.java:117: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.ImsMmTelManager [101]
+android/telephony/ims/ImsRcsManager.java:43: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.ImsRcsManager [101]
+android/telephony/ims/ProvisioningManager.java:102: lint: Unresolved link/see tag "android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability MmTelFeature.MmTelCapabilities.MmTelCapability" in android.telephony.ims.ProvisioningManager [101]
+android/telephony/ims/ProvisioningManager.java:102: lint: Unresolved link/see tag "android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.ims.ProvisioningManager [101]
+android/telephony/ims/ProvisioningManager.java:136: lint: Unresolved link/see tag "android.telephony.ims.ImsRcsManager.RcsImsCapabilityFlag ImsRcsManager.RcsImsCapabilityFlag" in android.telephony.ims.ProvisioningManager [101]
+android/telephony/ims/RegistrationManager.java:21: lint: Unresolved link/see tag "android.telephony.ims.feature.ImsFeature ImsFeature" in android.telephony.ims.RegistrationManager [101]
+android/telephony/ims/RegistrationManager.java:24: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.RegistrationManager [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/telephony/TelephonyRegistryManager.java:242: lint: Unresolved link/see tag "#listenFromListener" 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/telephony/SubscriptionManager.java:1370: lint: Unresolved link/see tag "Build.VERSION_CODES.Q" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [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 2b50e38..d037c31 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4463,7 +4463,7 @@
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 @@
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 @@
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 @@
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 @@
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);
@@ -18539,8 +18539,10 @@
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();
@@ -42982,7 +42984,6 @@
field public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
field public static final String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
- field @Deprecated public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool";
@@ -44904,7 +44905,7 @@
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 57e2e73..5a4be65 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -472,6 +472,10 @@
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 358c8e7..a99eeb0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3946,7 +3946,7 @@
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";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 40c6fa8..aa48451 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -875,7 +875,7 @@
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 @@
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 @@
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.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 107be8b..93e39d5 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -191,8 +191,14 @@
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 @@
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/app/Activity.java b/core/java/android/app/Activity.java
index 7bc6f9bf..e51a41e8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -31,6 +31,7 @@
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.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 @@
* 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 @@
* @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 @@
* {@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 @@
*
* @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 @@
* @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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ca10d14..1aa8ebe 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1486,13 +1486,13 @@
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 @@
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 {}
@@ -2261,13 +2261,13 @@
"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;
@@ -2811,9 +2811,9 @@
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()
+ 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 7b3d017..d15c79f 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -468,6 +468,15 @@
*/
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 @@
SUBREASON_PACKAGE_UPDATE,
SUBREASON_UNDELIVERED_BROADCAST,
SUBREASON_EXCESSIVE_BINDER_OBJECTS,
+ SUBREASON_OOM_KILL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SubReason {}
@@ -1371,6 +1381,8 @@
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 a6a57cd..37111e9 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -473,9 +473,8 @@
* 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 bc6fe61..a55121a 100644
--- a/core/java/android/app/GrammaticalInflectionManager.java
+++ b/core/java/android/app/GrammaticalInflectionManager.java
@@ -100,7 +100,7 @@
/**
* 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 @@
}
/**
- * 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 93c2b5a..dd7db23 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9195,6 +9195,12 @@
* 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 2fb428b..a84845a 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -240,6 +240,12 @@
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 @@
/**
* 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 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/virtualnative/OWNERS b/core/java/android/companion/virtualnative/OWNERS
new file mode 100644
index 0000000..2968104
--- /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 15678a7..bfc1eec 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.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 @@
/** @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 @@
* <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 @@
*
* @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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3fc515d..8fbe50c3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4802,6 +4802,7 @@
* @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 ea0f049..3e12a1a 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -13,3 +13,10 @@
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 231e4bc..1b130a9 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -53,6 +53,15 @@
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 af448f0..490ff64 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.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.concurrent.Executor;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
/**
@@ -748,7 +751,7 @@
* 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 @@
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 @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 267ef36..151f819 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 java.security.Signature;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
/**
@@ -62,6 +66,11 @@
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 @@
}
/**
+ * 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 f594377..5bfda70 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_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.concurrent.Executor;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
/**
@@ -312,6 +315,16 @@
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 bbfed24..883f157 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -82,8 +82,8 @@
DVORAK(4, LAYOUT_TYPE_DVORAK),
COLEMAK(5, LAYOUT_TYPE_COLEMAK),
WORKMAN(6, LAYOUT_TYPE_WORKMAN),
- TURKISH_F(7, LAYOUT_TYPE_TURKISH_F),
- TURKISH_Q(8, LAYOUT_TYPE_TURKISH_Q),
+ TURKISH_Q(7, LAYOUT_TYPE_TURKISH_Q),
+ TURKISH_F(8, LAYOUT_TYPE_TURKISH_F),
EXTENDED(9, LAYOUT_TYPE_EXTENDED);
private final int mValue;
diff --git a/core/java/android/os/OomKillRecord.java b/core/java/android/os/OomKillRecord.java
new file mode 100644
index 0000000..151a65f
--- /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 c01ef3d..88f62f3 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -12,4 +12,18 @@
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 ded74ea..3d1deea 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 @@
*
* @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 e08cc42..fed1053 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 @@
*
* @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 1cdfa4f..2880454 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 @@
*
* @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 5626b94..d8534dd 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -2,6 +2,7 @@
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 @@
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 d09f0a8..36d3180 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5664,6 +5664,37 @@
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 @@
"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/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index be7b722..a2b0a66 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -153,6 +153,18 @@
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/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig
new file mode 100644
index 0000000..c414ef8
--- /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/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index c4d3070..a150187 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -23,6 +23,8 @@
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 @@
* 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/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 2761aae..45b3fdd 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 @@
* @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 @@
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 @@
}
this.inputConfig &= ~inputConfig;
}
+
+ public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
+ boolean isTrusted) {
+ if (surfaceTrustedOverlay()) {
+ t.setTrustedOverlay(sc, isTrusted);
+ } else if (isTrusted) {
+ inputConfig |= InputConfig.TRUSTED_OVERLAY;
+ }
+ }
}
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index a0a172d..5a28d5f 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -199,12 +199,33 @@
* @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/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index effc127..57b19a8 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -407,7 +407,7 @@
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 @@
}
/**
+ * 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 afa3157..cf46bcc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -84,6 +84,7 @@
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.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 @@
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 @@
&& 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);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e64274e..2f4bea0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1850,6 +1850,8 @@
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 @@
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 @@
@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 7d3d283..8fe9b7b 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -58,7 +58,7 @@
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 @@
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 @@
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 @@
// 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 @@
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;
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 0000000..e31ad82
--- /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/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 79acfbb..103725d 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -638,9 +638,13 @@
* 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 abstract int getActionTag();
public String getUniqueKey() {
- return (getActionTag() + "_" + viewId);
+ return (getActionTag() + "_" + mViewId);
}
/**
@@ -684,16 +688,12 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
};
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 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 @@
@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 @@
}
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 @@
}
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 @@
@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 @@
@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 @@
// 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 @@
@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 @@
@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 int getActionTag() {
return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
}
-
- Intent intent;
- boolean isAsync = false;
}
/**
@@ -1369,26 +1367,27 @@
* 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 @@
// 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 int getActionTag() {
return SET_ON_CLICK_RESPONSE_TAG;
}
-
- final RemoteResponse mResponse;
}
/**
@@ -1446,32 +1443,31 @@
* 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 @@
// 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 @@
* <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 @@
}
if (targetDrawable != null) {
- targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
+ targetDrawable.mutate().setColorFilter(mColorFilter, mFilterMode);
}
}
@@ -1693,10 +1693,6 @@
public int getActionTag() {
return SET_DRAWABLE_TINT_TAG;
}
-
- boolean targetBackground;
- @ColorInt int colorFilter;
- PorterDuff.Mode filterMode;
}
/**
@@ -1709,27 +1705,26 @@
* <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 @@
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 @@
}
private static class BitmapCache {
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ArrayList<Bitmap> mBitmaps;
SparseIntArray mBitmapHashes;
@@ -1861,45 +1855,45 @@
}
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 @@
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 @@
@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 @@
@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 @@
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 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 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 @@
/** 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 @@
// 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 @@
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 @@
@Nullable
@Override
protected Object getParameterValue(@Nullable View view) throws ActionException {
- return this.value;
+ return this.mValue;
}
@Override
@@ -2221,7 +2215,6 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 @@
}
}
private static final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
-
private final float mValue;
@ComplexDimensionUnit
private final int mUnit;
@@ -2435,14 +2426,14 @@
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 @@
}
private static final class NightModeReflectionAction extends BaseReflectionAction {
-
private final Object mLightValue;
private final Object mDarkValue;
@@ -2475,7 +2465,7 @@
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 @@
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 @@
@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 @@
}
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 @@
}
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 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 @@
@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 @@
// 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 @@
}
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 @@
// 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 @@
* 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 @@
// 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 @@
* (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 @@
@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 @@
@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 @@
*/
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 @@
* {@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 @@
@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 @@
* 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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
@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 @@
* {@link #setViewOutlinePreferredRadius(int, float, int)}.
*/
public static final class RemoteViewOutlineProvider extends ViewOutlineProvider {
-
private final float mRadius;
public RemoteViewOutlineProvider(float radius) {
@@ -3821,7 +3803,8 @@
return mSizedRemoteViews != null;
}
- private @Nullable SizeF getIdealSize() {
+ @Nullable
+ private SizeF getIdealSize() {
return mIdealSize;
}
@@ -4174,7 +4157,7 @@
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 @@
* 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 @@
* @hide
*/
public class ActionApplyParams {
-
public InteractionHandler handler;
public ColorResources colorResources;
public Executor executor;
@@ -6575,15 +6557,17 @@
/**
* 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 @@
* 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/window/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java
new file mode 100644
index 0000000..b2c977b
--- /dev/null
+++ b/core/java/android/window/SystemPerformanceHinter.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.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 com.android.internal.annotations.VisibleForTesting;
+
+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
+ */
+ @VisibleForTesting
+ 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)) {
+ mTransaction.setFrameRateSelectionStrategy(
+ mDisplayRootProvider.getRootForDisplay(session.displayId),
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+ transactionChanged = true;
+ Trace.beginAsyncSection("PerfHint-framerate-" + 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.apply();
+ }
+ }
+
+ /**
+ * 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)) {
+ mTransaction.setFrameRateSelectionStrategy(
+ mDisplayRootProvider.getRootForDisplay(session.displayId),
+ FRAME_RATE_SELECTION_STRATEGY_SELF);
+ 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.apply();
+ }
+ }
+
+ /**
+ * 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 3323ae5..c2b5196 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -897,6 +897,23 @@
}
/**
+ * 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 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 @@
@Nullable
private ShortcutInfo mShortcutInfo;
+ @Nullable
+ private Rect mBounds;
+
private boolean mAlwaysOnTop;
private boolean mReparentLeafTaskIfRelaunch;
@@ -1482,6 +1503,7 @@
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 @@
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 @@
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 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 @@
@Nullable
private ShortcutInfo mShortcutInfo;
+ @Nullable
+ private Rect mBounds;
+
private boolean mAlwaysOnTop;
private boolean mReparentLeafTaskIfRelaunch;
@@ -1867,6 +1899,11 @@
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 @@
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 0000000..1b98806
--- /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 1bfb51c..6e836e0 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -837,25 +837,41 @@
@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/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 06e69f2..fd435d0 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -222,6 +222,7 @@
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/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6c93680..4732702 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -98,6 +98,7 @@
// 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/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index 8b6c901..27f8138 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 63fe471..2c6e0a7 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 2ab2d91..8becb08 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 8698e86..f3e412d 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -100,7 +100,9 @@
<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 3c296de..7ef81ab 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 98a5ff9..bc9ca3d 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 71d696e..3ba150b 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 49295fd..96c4bf4 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 193f3ad..643f4b1 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 85d54e0..054d10c 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -38,7 +38,7 @@
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 a195228..824f591 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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 fac9eaa..3b9d7ba 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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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,18 +549,6 @@
}
@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]);
@@ -627,8 +601,7 @@
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 +627,8 @@
mTunerSessions[0].startBackgroundScan();
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete();
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
+ .onBackgroundScanComplete();
}
@Test
@@ -845,8 +819,8 @@
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 +845,8 @@
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 +857,7 @@
}
@Test
- public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()
+ public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
throws Exception {
openAidlClients(1);
doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
@@ -891,7 +865,7 @@
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 8c5d6d5..0e3bc65 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -30,7 +30,7 @@
"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 6f2366e..b631df1 100644
--- a/core/tests/PackageInstallerSessions/Android.bp
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -35,7 +35,7 @@
"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 5260835..1fb5f2c 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
@@ -18,7 +18,7 @@
"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 2b34ee2..7c1ac48 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -32,7 +32,7 @@
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 9cde296..81dab08 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -44,19 +44,21 @@
"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 @@
"androidx.test.runner",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
libs: [
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 2afbb47..6a9fc04 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.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.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 @@
@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 @@
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 @@
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/window/SystemPerformanceHinterTests.java b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
new file mode 100644
index 0000000..25f5819
--- /dev/null
+++ b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.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).apply();
+ }
+
+ @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).apply();
+ }
+
+ @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).apply();
+ }
+
+ @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).apply();
+ }
+
+ @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).setEarlyWakeupStart();
+ verify(mTransaction).apply();
+ 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).setEarlyWakeupEnd();
+ verify(mTransaction).apply();
+ 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).setEarlyWakeupEnd();
+ verify(mTransaction).apply();
+ 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).setEarlyWakeupStart();
+ verify(mTransaction).apply();
+ 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()).setEarlyWakeupEnd();
+ verify(mTransaction, never()).apply();
+ 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()).setEarlyWakeupEnd();
+ verify(mTransaction, never()).apply();
+ 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).setEarlyWakeupEnd();
+ verify(mTransaction).apply();
+ 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).setEarlyWakeupStart();
+ verify(mTransaction).apply();
+ 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, never()).setEarlyWakeupStart();
+ verify(mTransaction).apply();
+ 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, never()).setFrameRateSelectionStrategy(
+ eq(mSecondaryDisplayRoot),
+ anyInt());
+ verify(mTransaction, never()).setEarlyWakeupEnd();
+ verify(mTransaction).apply();
+ 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).setEarlyWakeupEnd();
+ verify(mTransaction).apply();
+ 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 3d04937..5f6eaf9 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -30,7 +30,7 @@
"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 fde7c08..2d778b1 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -38,7 +38,7 @@
"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 c74600b..f81be49 100644
--- a/core/tests/nfctests/Android.bp
+++ b/core/tests/nfctests/Android.bp
@@ -27,7 +27,7 @@
"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 063c569..931eac5 100644
--- a/core/tests/overlaytests/device_self_targeting/Android.bp
+++ b/core/tests/overlaytests/device_self_targeting/Android.bp
@@ -30,7 +30,7 @@
"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 7b5d7df..b08850e 100644
--- a/core/tests/packagemonitortests/Android.bp
+++ b/core/tests/packagemonitortests/Android.bp
@@ -32,7 +32,7 @@
"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 bc3dd81..4e24cd5 100644
--- a/core/tests/privacytests/Android.bp
+++ b/core/tests/privacytests/Android.bp
@@ -14,7 +14,7 @@
"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 3798da5..580e73c 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -33,7 +33,7 @@
"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 829409a..09608e9 100644
--- a/core/tests/vibrator/Android.bp
+++ b/core/tests/vibrator/Android.bp
@@ -18,7 +18,7 @@
"androidx.test.runner",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
"testng",
],
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index ad08026..c1d2235 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -41,7 +41,7 @@
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/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index c55a781..11278e8 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.RSAPublicKey;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
@@ -181,6 +182,8 @@
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 b6e743a..ed2ff2d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp
+++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
@@ -37,7 +37,7 @@
"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 0000000..ee9f070
--- /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 c2ee306..0000000
--- 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 e637671..0000000
--- 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 c4b688d..0000000
--- 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 705e387..7a309f5 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -434,11 +434,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 410ae78d..38550b4 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.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 @@
/** {@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 @@
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 f1ee8fa..a67821b 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 @@
/**
* 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 df19757..dc099d9 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.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 @@
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/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index c124b53..2241c34 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 @@
: 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 @@
if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
maybeShowManageEdu();
}
+ updateOverflowDotVisibility(true /* expanding */);
} /* after */);
int index;
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
@@ -2405,11 +2414,15 @@
// 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 @@
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 4d7042b..738c94e 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 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 @@
/** 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 @@
// 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 @@
@Override
SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
+ boolean isOverflow = (view instanceof BadgedImageView)
+ && BubbleOverflow.KEY.equals(((BadgedImageView) view).getKey());
return new SpringForce()
- .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
+ .setDampingRatio(isOverflow
+ ? DAMPING_RATIO_OVERFLOW_BOUNCY
+ : DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
.setStiffness(SpringForce.STIFFNESS_LOW);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 5e42782..e9344ff 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 @@
+ "SystemWindow:" + view);
return null;
}
- return root.getFocusGrantToken();
+ return root.getInputTransferToken();
}
private class PerDisplay {
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 998cd5d..e6d3abc 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.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.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 @@
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 11aa0546..5dfba5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -494,13 +494,14 @@
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 af97cf6..8a64037 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.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 570f0a3..f2631ef 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 @@
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 @@
@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 09ba4f7..412a5b5 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.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.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 @@
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 @@
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 @@
)
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 @@
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 0000000..f7977f8
--- /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/PipTransition.java
deleted file mode 100644
index b8e4c04..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.pip2;
-
-import android.annotation.NonNull;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.common.pip.PipBoundsState;
-import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.pip.PipMenuController;
-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. */
-public class PipTransition extends PipTransitionController {
- public PipTransition(
- @NonNull ShellInit shellInit,
- @NonNull ShellTaskOrganizer shellTaskOrganizer,
- @NonNull Transitions transitions,
- PipBoundsState pipBoundsState,
- PipMenuController pipMenuController,
- PipBoundsAlgorithm pipBoundsAlgorithm) {
- super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
- pipBoundsAlgorithm);
- }
-
- @Override
- protected void onInit() {
- if (PipUtils.isPip2ExperimentEnabled()) {
- mTransitions.addHandler(this);
- }
- }
-
- @Nullable
- @Override
- public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
- return null;
- }
-
- @Override
- public boolean startAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- return false;
- }
-
- @Override
- public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {}
-
- @Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
- @Nullable SurfaceControl.Transaction finishT) {}
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
new file mode 100644
index 0000000..d704b09
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.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;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * Implementation of transitions for PiP on phone.
+ */
+public class PipTransition extends PipTransitionController {
+ @Nullable
+ private IBinder mAutoEnterButtonNavTransition;
+
+ public PipTransition(
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
+ PipMenuController pipMenuController,
+ PipBoundsAlgorithm pipBoundsAlgorithm) {
+ super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+ pipBoundsAlgorithm);
+ }
+
+ @Override
+ protected void onInit() {
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ mTransitions.addHandler(this);
+ }
+ }
+
+ @Nullable
+ @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;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {}
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {}
+}
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 ead2f9c..d31476c 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.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 @@
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 @@
}
}
if (mFinishCB != null) {
- finishInner(toHome, false /* userLeave */);
+ finishInner(toHome, false /* userLeave */, null /* finishCb */);
} else {
cleanUp();
}
@@ -670,7 +671,8 @@
// 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 @@
@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 @@
}
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/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 83dc7fa..e828eed 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.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 @@
@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 e0635ac..de03f58 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 @@
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/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 780bbb5..bf99ab3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -376,7 +376,7 @@
public void onClick(View v) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
final int id = v.getId();
- if (id == R.id.close_window || id == R.id.close_button) {
+ if (id == R.id.close_window) {
mTaskOperations.closeTask(mTaskToken);
if (isTaskInSplitScreen(mTaskId)) {
RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index a7a11de..15f8f1c 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.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 @@
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 @@
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 @@
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 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);
+ 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);
}
/**
- * Set up interactive elements and color of this handle menu
+ * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions
+ * 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);
+ final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView();
+ handleMenu.setOnTouchListener(mOnTouchListener);
+ setupAppInfoPill(handleMenu);
+ if (mShouldShowWindowingPill) {
+ setupWindowingPill(handleMenu);
+ }
+ setupMoreActionsPill(handleMenu);
+ }
+
+ /**
+ * Set up interactive elements of handle menu's app info pill.
+ */
+ 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);
+ /**
+ * 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 = 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);
- }
+ 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 @@
* 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 @@
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 @@
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 @@
* @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 @@
*/
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 @@
* 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 @@
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 07fee43..6062e34 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
@@ -425,13 +425,10 @@
* @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 +439,6 @@
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 0000000..4f27ced
--- /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 54f9498..d09a90c 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -45,7 +45,7 @@
"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/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index dea1617..ebcb640 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.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 @@
@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 @@
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 @@
mToggleResizeDesktopTaskTransitionHandler,
desktopModeTaskRepository,
launchAdjacentController,
+ recentsTransitionHandler,
shellExecutor
)
}
@@ -355,7 +364,7 @@
@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 @@
@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 @@
}
@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/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 76bc25a..966a99ee 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
@@ -119,8 +119,6 @@
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 +129,6 @@
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;
@@ -417,16 +413,6 @@
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());
@@ -588,13 +574,11 @@
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 64b53cb..4dafd0a 100644
--- a/libs/dream/lowlight/tests/Android.bp
+++ b/libs/dream/lowlight/tests/Android.bp
@@ -34,7 +34,7 @@
"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 6ca991d..bc17459 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -4,9 +4,8 @@
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/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp
index 7df546a..80b501d 100644
--- a/libs/securebox/tests/Android.bp
+++ b/libs/securebox/tests/Android.bp
@@ -32,7 +32,7 @@
"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 0319f32..8800dc8 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -29,7 +29,6 @@
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 @@
@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 12db8c0..b5a9ae2 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -42,6 +42,7 @@
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 @@
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 @@
* @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 @@
@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 74f7276..b74b6a3 100644
--- a/media/java/android/media/RingtoneSelection.java
+++ b/media/java/android/media/RingtoneSelection.java
@@ -18,16 +18,23 @@
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 @@
* 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 @@
* @hide
*/
@TestApi
+@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
public final class RingtoneSelection {
/**
@@ -70,7 +78,7 @@
* 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 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 @@
/**
* 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 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 @@
@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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/* 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 @@
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 @@
}
// 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 @@
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 @@
}
}
- /** 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 @@
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 @@
*/
@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;
+ return SOUND_SOURCE_UNSPECIFIED;
}
- switch (soundSource) {
- case SOURCE_OFF_STRING: return SOUND_SOURCE_OFF;
- default: return SOUND_SOURCE_UNKNOWN;
- }
+ 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 @@
*/
@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;
+ return VIBRATION_SOURCE_UNSPECIFIED;
}
- 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 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 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 @@
*/
@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 @@
*/
@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 ca20225e..bdd7afe 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -22,7 +22,7 @@
"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 4cccf89..61b18c8 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -24,7 +24,7 @@
"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 e313c46..48cd8b6 100644
--- a/media/tests/projection/Android.bp
+++ b/media/tests/projection/Android.bp
@@ -30,7 +30,7 @@
"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 ba88484..2318bb9 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 @@
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 @@
}
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 @@
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 @@
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 633f186..86c62ef 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -25,7 +25,7 @@
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 64b4c54..61a8270 100644
--- a/packages/FusedLocation/Android.bp
+++ b/packages/FusedLocation/Android.bp
@@ -47,7 +47,7 @@
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 @@
"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 ad3199f..d9338b1 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
@@ -337,7 +337,7 @@
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 6744922..fc53cba 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 88bb30b..7f23f74 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 d0a6188..0757df3 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.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 @@
fun SettingsTextFieldPassword(
value: String,
label: String,
+ enabled: Boolean = true,
onTextChange: (String) -> Unit,
) {
var visibility by remember { mutableStateOf(false) }
@@ -60,6 +61,7 @@
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 4031cd7..639d1a7 100644
--- a/packages/SettingsLib/Spa/testutils/Android.bp
+++ b/packages/SettingsLib/Spa/testutils/Android.bp
@@ -31,7 +31,7 @@
"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 815293e9..09f34b3 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 @@
@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 @@
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 b03c43c..4b4caf5 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -52,7 +52,7 @@
"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 dd9cb9c..2d875cf 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -96,6 +96,6 @@
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 19ab1c6..6d6e2ff 100644
--- a/packages/SettingsLib/tests/unit/Android.bp
+++ b/packages/SettingsLib/tests/unit/Android.bp
@@ -31,6 +31,6 @@
"SettingsLib",
"androidx.test.ext.junit",
"androidx.test.runner",
- "truth-prebuilt",
+ "truth",
],
}
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 92ebe09..f4ca260 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -64,7 +64,7 @@
"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 91d2d1b..ba4ad36 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 @@
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 bec1447..19fde75 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 @@
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 17ce7c7..29f27f7 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 @@
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 3c8d4bc..f06b31c 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 @@
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 40f7ba6..34d3d44 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 @@
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 c0f6231..9ddc976a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -101,6 +101,7 @@
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 ee05f2d..e40fcb2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -319,6 +319,8 @@
"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 @@
"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 @@
"hamcrest-library",
"androidx.test.rules",
"testables",
- "truth-prebuilt",
+ "truth",
"monet",
"libmonet",
"dagger2",
@@ -545,7 +583,7 @@
"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 008732e..96e1e3f 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 @@
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 @@
} 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 538ecb3..3fc351c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -32,7 +32,7 @@
"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 437f8af..18117a8 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -13,3 +13,11 @@
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 0f2e4ba..4aac279 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.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 @@
override fun onLaunchAnimationProgress(linearProgress: Float) {
listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
}
+
+ override fun onLaunchAnimationCancelled() {
+ listeners.forEach { it.onLaunchAnimationCancelled() }
+ }
}
/**
@@ -191,6 +196,7 @@
"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 @@
// 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 @@
*/
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 @@
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 @@
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 @@
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 @@
)
}
controller.onLaunchAnimationCancelled()
+ listener?.onLaunchAnimationCancelled()
return
}
@@ -821,6 +900,7 @@
Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]")
}
controller.onLaunchAnimationCancelled()
+ listener?.onLaunchAnimationCancelled()
}
@UiThread
@@ -842,6 +922,7 @@
)
}
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 bd3706e..00d9056 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 @@
* 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 @@
rootView: View,
interpolator: Interpolator = DEFAULT_INTERPOLATOR,
duration: Long = DEFAULT_DURATION,
+ animateChildren: Boolean = true,
excludedViews: Set<View> = emptySet()
): Boolean {
return animate(
@@ -93,6 +96,7 @@
interpolator,
duration,
ephemeral = false,
+ animateChildren = animateChildren,
excludedViews = excludedViews
)
}
@@ -106,6 +110,7 @@
rootView: View,
interpolator: Interpolator = DEFAULT_INTERPOLATOR,
duration: Long = DEFAULT_DURATION,
+ animateChildren: Boolean = true,
excludedViews: Set<View> = emptySet()
): Boolean {
return animate(
@@ -113,6 +118,7 @@
interpolator,
duration,
ephemeral = true,
+ animateChildren = animateChildren,
excludedViews = excludedViews
)
}
@@ -122,6 +128,7 @@
interpolator: Interpolator,
duration: Long,
ephemeral: Boolean,
+ animateChildren: Boolean,
excludedViews: Set<View> = emptySet()
): Boolean {
if (
@@ -137,7 +144,13 @@
}
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 @@
view: View,
listener: View.OnLayoutChangeListener,
recursive: Boolean = false,
+ animateChildren: Boolean = true,
excludedViews: Set<View> = emptySet()
) {
if (excludedViews.contains(view)) return
@@ -952,12 +966,13 @@
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 88944f10..60c3fd3 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 @@
) {
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 @@
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 @@
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 a62c984..3bcd920f 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 @@
/** 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 @@
companion object {
val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE)
+ val AlphaUnspecified = Float.MIN_VALUE
}
}
@@ -91,7 +104,7 @@
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 @@
element
}
+ val lastSharedValues = element.lastSharedValues
+ val lastSceneValues = sceneValues.lastValues
DisposableEffect(scene, sceneValues, element) {
onDispose {
@@ -121,13 +136,14 @@
}
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 @@
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 @@
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 elementAlpha(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
- scene: Scene
+ scene: Scene,
+ sceneValues: Element.TargetValues,
): Float {
return computeValue(
layoutImpl,
@@ -258,7 +276,11 @@
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 @@
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 @@
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 @@
)
)
- element.lastSize = placeable.size()
+ val size = placeable.size()
+ element.lastSharedValues.size = size
+ sceneValues.lastValues.size = size
return placeable
}
@@ -325,7 +346,7 @@
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
element: Element,
- sceneValues: Element.SceneValues,
+ sceneValues: Element.TargetValues,
placeable: Placeable,
placementScope: Placeable.PlacementScope,
) {
@@ -334,9 +355,9 @@
// 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 @@
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 @@
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 ccdec6e..1b79dbd 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 @@
* 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 @@
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 39173d9..58c7bdb 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 @@
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 7a21211..b9f83c5 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 @@
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 1cbfe30..e275fca 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 @@
// 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 @@
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 @@
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 @@
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 @@
) {
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,
- )
-
- // 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)
- }
+ if (transition.isUserInputOngoing) {
+ transition.offsetAnimatable.snapTo(transition.dragOffset)
}
- .also { it.invokeOnCompletion { transition.isAnimatingOffset = false } }
+ 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)
+ }
+ }
}
}
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 d4ed697..95385d5 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 @@
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 8a5bd74..a1d6319 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 @@
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 5cdce94..840800d 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 @@
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 0a5ac54..17032dc 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 @@
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 73b366e..62d67f0 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 @@
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 @@
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 @@
}
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 ce754dc..233ae59 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 @@
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 a650254..2ef8d56 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 @@
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
element: Element,
- sceneValues: Element.SceneValues,
+ sceneValues: Element.TargetValues,
): Modifier
}
@@ -74,7 +74,7 @@
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 8abca61..864b937 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 @@
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 52c6385..8e9c586 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -43,7 +43,7 @@
"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 2232370..53ed2b5 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 @@
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 @@
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 @@
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 @@
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 a9944f7..f2b7b32 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 @@
}
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 @@
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 c3a3752..ee310ab 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.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 @@
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 1f9c3e6..a33eac5 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.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.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 @@
.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 2ee461f..f35ea83 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 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 @@
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 ef01266..0da562b 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.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 @@
fromScene = fromScene.toModel().key,
toScene = toScene.toModel().key,
progress = progress,
- isUserInputDriven = isUserInputDriven,
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing,
)
}
}
@@ -180,12 +184,24 @@
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 6629a25..591fa76 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.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.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 @@
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 @@
{
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 0000000..4c2c627
--- /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 0000000..847d7a5
--- /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 0000000..57d1612
--- /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 0000000..2f3b64b
--- /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 0000000..eb52f95
--- /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 0000000..90e6678
--- /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 0000000..f327da2
--- /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 0000000..6e3754a
--- /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 0000000..adf1256
--- /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 0000000..8de04ab
--- /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 0000000..967bb3f
--- /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 0000000..45da4d7
--- /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 0000000..3ffaa8b
--- /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 0000000..64f95e0
--- /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 0000000..57134b5
--- /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 0000000..a6110d5
--- /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 0000000..79919c0
--- /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 0000000..a6110d5
--- /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 0000000..a6110d5
--- /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 0000000..7c540da
--- /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 0000000..59be786
--- /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 0000000..03ef0e5
--- /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 0000000..fec793e
--- /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 0000000..a70c808
--- /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 0000000..6426d51
--- /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 0000000..9b19373
--- /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 0000000..bbd1208
--- /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 0000000..5579a42
--- /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 0000000..2da93c6
--- /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 0000000..c578a2e
--- /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 0000000..6080f80
--- /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 0000000..0a1440f
--- /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 0000000..32618a8
--- /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 0000000..d45afbf
--- /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 0000000..a6110d5
--- /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 0000000..5a370d6
--- /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 0000000..2a5087d
--- /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 0000000..ddd28f2
--- /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 0000000..744604a
--- /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 0000000..88ba1df
--- /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 0000000..9ee6522
--- /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 0000000..bbc438a
--- /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 0000000..e67319d
--- /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 0000000..fa9103b
--- /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 0000000..76cc5e2
--- /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 0000000..28f5000
--- /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 0000000..2fe7315
--- /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 0000000..e0b904a
--- /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 0000000..9b95a6e
--- /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 0000000..7f6be8a
--- /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 0000000..38369b6
--- /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 0000000..821ff10
--- /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 0000000..2f61b47
--- /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 0000000..3d137eb
--- /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 0000000..6eb4373
--- /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 0000000..c5b0877
--- /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 0000000..4f46ab8
--- /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 0000000..a74017f
--- /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 0000000..a77661a
--- /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 0000000..6f5b6f2
--- /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 0000000..c6c3cc0
--- /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 0000000..bbe4355
--- /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 0000000..ef163e9
--- /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 0000000..5ee928e
--- /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 0000000..caf9610
--- /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 0000000..1843f97
--- /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 0000000..12df66f
--- /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 0000000..1fc9f25
--- /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 0000000..6b127c9
--- /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 0000000..84ad25c
--- /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 0000000..e2ec3de
--- /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 0000000..3c9e078
--- /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 0000000..f4eea07
--- /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 0000000..c7c77d5
--- /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 0000000..61d880e
--- /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 0000000..a3484a7
--- /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 0000000..a90e985
--- /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 0000000..ee9b77b
--- /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 0000000..06a6a7c
--- /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 0000000..6b31b04
--- /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 0000000..830b6e2
--- /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 0000000..747567e
--- /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 0000000..c19cc68
--- /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 0000000..6fcd313
--- /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 0000000..c87c250a
--- /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 8eb8132..c574d1f 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 0000000..897c842
--- /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 b28920c..b076b2c 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 @@
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 949641a..dd52e39 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.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 @@
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 e2f4793..485c27e 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 @@
/** 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 0000000..2187352
--- /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 593f507f..f68ab81 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/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index d4b73a4..acee425 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 18f24ec..1add90f 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 0000000..57a49c8
--- /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 c074988..1e89614 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 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 @@
// 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 0094820..a6e04ce 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.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 @@
* @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 2f3c1f2..eb7a735 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -205,19 +205,19 @@
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 @@
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 @@
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 @@
?.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 @@
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 d677a15..dec7d79 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -68,10 +68,13 @@
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 ff8e489..3c8301f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -84,7 +84,6 @@
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 @@
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 9c015fe..8a6f101 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -419,7 +419,7 @@
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 51c0676..50be97e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -139,7 +139,7 @@
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 714ba81..57151ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -321,7 +321,7 @@
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 9d6d033..681aa70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -123,7 +123,7 @@
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/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 1d2d77f..40d0be1 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -18,6 +18,7 @@
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 @@
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 a81069a..0d3f726 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.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.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.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 @@
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 @@
@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 @@
@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 @@
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 @@
}
/** 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 @@
if (lockIconView.isAttachedToWindow()) {
registerCallbacks();
}
+
+ lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
}
private void registerCallbacks() {
@@ -388,6 +405,16 @@
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 @@
};
/**
- * 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
};
+ /**
+ * 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 0000000..c581788
--- /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 d342377..f3014f1 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 @@
*
* 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 0180384..7739021 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.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.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 @@
@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 @@
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/bouncer/data/repository/BouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
index 943216e..b2a7607 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 @@
/** 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 0c02369..4ce1422 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 @@
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
+ /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
+ val isUserSwitcherVisible: Boolean = repository.isUserSwitcherVisible
+
init {
if (flags.isEnabled()) {
// Clear the message if moved from throttling to no-longer throttling.
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 2cb98d8..ef0609a 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 @@
initialValue = emptyList(),
)
+ val isUserSwitcherVisible: Boolean = bouncerInteractor.isUserSwitcherVisible
+
private val isInputEnabled: StateFlow<Boolean> =
bouncerInteractor.isThrottled
.map { !it }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt
index 4efc21b..4b78e9e 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 @@
* 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 @@
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 @@
* 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 c0ee71c..0dfaf0f 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 @@
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 046ccf16..a90980f 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 @@
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 @@
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 9e5fd55..1dd4abf 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 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.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.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.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 @@
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 @@
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 d89332d..5d6949b 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.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 @@
@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 5b85ad0..1e29e1f 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 @@
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 @@
* 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 @@
) : DeviceEntryRepository {
override val isUnlocked =
- ConflatedCallbackFlow.conflatedCallbackFlow {
+ conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
override fun onUnlockedChanged() {
@@ -112,7 +112,24 @@
}
}
- 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 5612c9a..e96e318 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 @@
* 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 126a1b5..6bbd40c 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.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 @@
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 @@
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 @@
@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 e3cc2b0..bf9018a 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 @@
|| (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 @@
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 @@
}
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 8587329..f6f24e0 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 @@
// 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 @@
"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 @@
*/
// 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 @@
/** 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 @@
@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 @@
/** 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 f500017..86bf368 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 @@
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 7678f4d..39742a0 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.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.concurrent.Executor;
import java.util.function.Consumer;
-import dagger.Lazy;
+
+
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -1933,11 +1936,7 @@
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 03c166c..41bde91 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.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 @@
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 3ccf446..d06f31f 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.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromAlternateBouncerTransitionInteractor
@@ -129,11 +130,11 @@
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 e57c919..89aca76 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 @@
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 d6987629..ac012f8 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.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 @@
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 @@
(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 1c43609..fbe26de 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.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 @@
.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 @@
/** 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 @@
* 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 @@
* 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 @@
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 596a1c0..f38bb2b 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 @@
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 ef1d5ac..797dec2 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.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.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.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.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 @@
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 @@
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 0000000..41af9e8
--- /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 aa76702..f0d118c 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 @@
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 @@
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 231eeb5..c6d8ec7 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.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 @@
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 @@
host =
SurfaceControlViewHost(
context,
- displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)),
+ displayManager.getDisplay(DEFAULT_DISPLAY),
hostToken,
"KeyguardPreviewRenderer"
)
@@ -175,21 +179,27 @@
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 @@
*
* 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 @@
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 @@
}
@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 @@
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 @@
)
}
- setUpSmartspace(rootView)
+ setUpSmartspace(previewContext, rootView)
smartSpaceView?.let {
KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel)
}
@@ -382,7 +392,7 @@
}
}
- 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 @@
sensorBounds.bottom
)
val finger =
- LayoutInflater.from(context)
+ LayoutInflater.from(previewContext)
.inflate(
R.layout.udfps_keyguard_preview,
parentView,
@@ -409,17 +419,54 @@
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 @@
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 1a9f021..0b0f21d 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.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 @@
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 0000000..235a28d
--- /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 54abc5b..0b1079f 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 @@
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 @@
private val toAlternateBouncer: Flow<TransitionViewModel> =
keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
- transitionInteractor.anyStateToAlternateBouncerTransition.map {
+ transitionInteractor.transitionStepsToState(KeyguardState.ALTERNATE_BOUNCER).map {
TransitionViewModel(
alpha = 1f,
scale =
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 68202d5..80be766 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.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 @@
@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 2b56d0c..d08d040 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 @@
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/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
index 8589ae9..68bf88b 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 @@
@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 2a36fdb..65a2c8c 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 @@
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 a32a024..202254b 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 @@
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 @@
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 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 60c92c0..fc2f5f9 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 @@
protected void onInit() {
mView.initialize(mQSLogger);
mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
+ mHost.addCallback(mQSHostCallback);
}
/**
@@ -172,6 +173,18 @@
}
@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 @@
mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
- mHost.addCallback(mQSHostCallback);
setTiles();
mLastOrientation = getResources().getConfiguration().orientation;
mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag());
@@ -193,16 +205,11 @@
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 @@
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 327e858..ce8db7898 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 @@
/** 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 0c9c24d..0e75b21 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.useQsMediaPlayer;
import android.content.Context;
-import android.view.View;
-import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragmentLegacy;
-
-import javax.inject.Named;
-
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import javax.inject.Named;
+
/**
* Dagger Module for {@link QSFragmentComponent}.
*/
@Module(includes = {QSScopeModule.class})
public interface QSFragmentModule {
-
- @Provides
- @RootView
- static View provideRootView(QSFragmentLegacy qsFragment) {
- return qsFragment.getView();
- }
-
- /** */
- @Binds
- QS bindQS(QSFragmentLegacy qsFragment);
-
/** */
@Provides
@Named(QSScopeModule.QS_USING_MEDIA_PLAYER)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index c5512c1..4bb8c6e 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 @@
// repository
launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
}
- Log.d("Fabian", "Finished resolving tiles")
}
}
}
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 584456d..91b4d17 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_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.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 @@
// 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 @@
}
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 3927873..f704894 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 @@
* 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 4bc93a8..2e45353 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 @@
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 @@
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 2f87301..393a698 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 @@
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 @@
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 9235fcc..c42fdf8 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 @@
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 d3d38e5c..2ef83dd 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.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.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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 6cf4ff5..5414b3f 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.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.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.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 @@
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 @@
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
BouncerLogger bouncerLogger,
- KeyEventInteractor keyEventInteractor) {
+ KeyEventInteractor keyEventInteractor,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ AlternateBouncerInteractor alternateBouncerInteractor) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -210,6 +215,8 @@
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 @@
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 @@
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 @@
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 447a15d..2c4b0b9 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 @@
/** 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 367449b..fdc7eec 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.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 @@
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
private boolean mExpandedVisible;
+ private boolean mLockscreenOrShadeVisible;
private NotificationPresenter mPresenter;
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@@ -399,8 +401,19 @@
}
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 6ee6cbf..4e23e7d 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.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 @@
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 ac8333a..6117f9f 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 @@
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.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 @@
@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 @@
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 @@
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 @@
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 d24f9d8..77b0958 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 @@
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 @@
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 @@
.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 3640ae0..4ea7026 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 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 @@
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 f616b91..3a4ad0e 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.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 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 ff40f70..91ca148 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 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 ddbfcef..dc2ebe5 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.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 @@
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 @@
/**
* 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 @@
wifiManager,
connectivityManager,
mainHandler,
- workerHandler,
+ // WifiPickerTracker can take tens of seconds to finish operations, so it can't use
+ // the default background handler (it would block all other background operations).
+ // Use a custom handler instead.
+ threadFactory.buildHandlerOnNewThread("WifiPickerTracker-$name"),
clock,
MAX_SCAN_AGE_MILLIS,
SCAN_INTERVAL_MILLIS,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
index 167efc7..dc0eb7b 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.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 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 @@
@Override
public void onAnimationEnd(Animator animation) {
view.setTag(DOZE_ANIMATOR_TAG, null);
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
}
@Override
@@ -102,6 +112,9 @@
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 c62546f..756151b 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.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 @@
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 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 @@
}
}
- 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 07eb8a00..2d83970 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.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 @@
@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 @@
override fun onCleanup() {
logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs)
- seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs
+ notificationListInteractor.setHasFilteredOutSeenNotifications(hasFilteredAnyNotifs)
hasFilteredAnyNotifs = false
}
}
@@ -388,8 +388,8 @@
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 657c394d..c0f674846 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.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 @@
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 cff47e2..0000000
--- 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 59fc387..1a88815 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 @@
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 8ee0de6..8561869 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 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.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.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.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 @@
@Module(includes = {
CoordinatorsModule.class,
KeyguardNotificationVisibilityProviderModule.class,
- SeenNotificationsProviderModule.class,
ShadeEventsModule.class,
+ NotificationDataLayerModule.class,
NotifPipelineChoreographerModule.class,
NotificationSectionHeadersModule.class,
NotificationListViewModelModule.class,
@@ -206,7 +206,7 @@
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 0000000..5435fb5
--- /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 0000000..cf03d1c
--- /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 0000000..87b8e55
--- /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 20241c3..805a4db 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 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.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 @@
@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 @@
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 @@
) :
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 @@
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 @@
}
init {
- statusBarStateController.addCallback(this)
wakeUpCoordinator.addListener(this)
demoModeController.addCallback(this)
notificationListener.addNotificationSettingsListener(settingsListener)
@@ -156,9 +148,15 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 8293bb3..0d2f00a 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 f68b0ef..3289a3c 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 933c76f..c44a2b6 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 2217646..035687a 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 892b2be..65eb220 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 c61258b..847d948 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 @@
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 @@
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 d18f991..061132f 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 @@
/**
* @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 5aae488..f2f55a8 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 @@
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 42c80ed..40897da 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.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 @@
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 @@
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 6d656605..9e9116b 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.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.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.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 @@
// 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 @@
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 @@
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 @@
mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mLockscreenUserManager = notificationLockscreenUserManager;
mStatusBarStateController = statusBarStateController;
+ mStatusBarService = statusBarService;
mDeviceProvisionedController = deviceProvisionedController;
mMetricsLogger = metricsLogger;
- mHeadsUpManagerPhone = headsUpManagerPhone;
+ mHeadsUpManager = headsUpManager;
mActivityStarter = activityStarter;
}
@@ -296,7 +300,7 @@
if (mGutsListener != null) {
mGutsListener.onGutsClose(entry);
}
- mHeadsUpManagerPhone.setGutsShown(row.getEntry(), false);
+ mHeadsUpManager.setGutsShown(row.getEntry(), false);
});
View gutsView = item.getGutsView();
@@ -358,7 +362,8 @@
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 @@
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 28f0a0c..78d7558 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 @@
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 @@
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 @@
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 @@
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 @@
}
private boolean shouldShowDismissView() {
- return mClearAllEnabled
- && mController.hasActiveClearableNotifications(ROWS_ALL);
+ return mController.hasActiveClearableNotifications(ROWS_ALL);
}
private boolean shouldShowFooterView(boolean showDismissView) {
@@ -1432,12 +1431,14 @@
@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 @@
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 66b2555..9695cb1 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.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.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.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 @@
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 @@
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 @@
NotificationGutsManager notificationGutsManager,
NotificationsController notificationsController,
NotificationVisibilityProvider visibilityProvider,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
NotificationRoundnessManager notificationRoundnessManager,
TunerService tunerService,
DeviceProvisionedController deviceProvisionedController,
@@ -662,7 +662,7 @@
UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
- SeenNotificationsProvider seenNotificationsProvider,
+ NotificationListInteractor notificationListInteractor,
ShadeController shadeController,
InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger,
@@ -715,7 +715,7 @@
mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager;
mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
- mSeenNotificationsProvider = seenNotificationsProvider;
+ mNotificationListInteractor = notificationListInteractor;
mShadeController = shadeController;
mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
@@ -2006,7 +2006,7 @@
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 f2d5394..8ca1852 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 @@
viewState.setAlpha(1f);
} else if (ambientState.isOnKeyguard()) {
// Adjust alpha for wakeup to lockscreen.
- viewState.setAlpha(1f - ambientState.getHideAmount());
+ if (view.isHeadsUpState()) {
+ // Pulsing HUN should be visible on AOD and stay visible during
+ // AOD=>lockscreen transition
+ viewState.setAlpha(1f - ambientState.getHideAmount());
+ } else {
+ // Normal notifications are hidden on AOD and should fade in during
+ // AOD=>lockscreen transition
+ viewState.setAlpha(1f - ambientState.getDozeAmount());
+ }
} else if (ambientState.isExpansionChanging()) {
// Adjust alpha for shade open & close.
float expansion = ambientState.getExpansionFraction();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt
new file mode 100644
index 0000000..f6ed8c8
--- /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 0000000..3fd68a8
--- /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 3e2f10d..6e6318e 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.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.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.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 @@
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 @@
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
+ HeadsUpManager headsUpManager,
DynamicPrivacyController dynamicPrivacyController,
FalsingManager falsingManager,
FalsingCollector falsingCollector,
@@ -718,7 +719,7 @@
mWakeUpCoordinator = notificationWakeUpCoordinator;
mKeyguardBypassController = keyguardBypassController;
mKeyguardStateController = keyguardStateController;
- mHeadsUpManager = headsUpManagerPhone;
+ mHeadsUpManager = headsUpManager;
mBackActionInteractor = backActionInteractor;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
@@ -1088,11 +1089,6 @@
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 @@
});
mStatusBarInitializer.initializeStatusBar();
- mStatusBarTouchableRegionManager.setup(this, getNotificationShadeWindowView());
+ mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView());
createNavigationBar(result);
@@ -2688,7 +2684,9 @@
!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 @@
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 @@
protected boolean mDeviceInteractive;
- protected boolean mVisible;
-
protected DevicePolicyManager mDevicePolicyManager;
private final PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -3032,16 +3031,6 @@
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 @@
// TODO: Bring these out of CentralSurfaces.
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
- mHeadsUpManager.onDensityOrFontScaleChanged();
}
@Override
@@ -3200,7 +3188,8 @@
// 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 4849f64..d3d11ea 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.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 @@
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 @@
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 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mDeviceProvisionedController = deviceProvisionedController;
- mHeadsUpManagerPhone = headsUpManagerPhone;
+ mHeadsUpManager = headsUpManager;
mBatteryController = batteryController;
mScrimController = scrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
@@ -135,7 +136,7 @@
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
- mHeadsUpManagerPhone.addListener(mOnHeadsUpChangedListener);
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mDozeInteractor = dozeInteractor;
}
@@ -337,8 +338,8 @@
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 @@
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 270c40e..c493eeda 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 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.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 @@
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 @@
@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 a5ea142..6b4382f73 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 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.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.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 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// Constructor:
-
+ @Inject
public HeadsUpManagerPhone(@NonNull final Context context,
HeadsUpManagerLogger logger,
StatusBarStateController statusBarStateController,
@@ -154,7 +156,8 @@
/**
* Add a listener to receive callbacks onHeadsUpGoingAway
*/
- void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
+ @Override
+ public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
mHeadsUpPhoneListeners.add(listener);
}
@@ -162,7 +165,8 @@
* 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 @@
* @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 @@
* 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 @@
}
}
- boolean isHeadsUpGoingAway() {
+ @Override
+ public boolean isHeadsUpGoingAway() {
return mHeadsUpGoingAway;
}
@@ -260,8 +267,8 @@
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 @@
mSwipedOutKeys.add(key);
}
+ @Override
public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
boolean animate) {
if (animate) {
@@ -411,7 +419,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// HeadsUpEntryPhone:
- protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+ protected class HeadsUpEntryPhone extends BaseHeadsUpManager.HeadsUpEntry {
private boolean mGutsShownPinned;
@@ -459,11 +467,11 @@
@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 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 0000000..0d0f2cd
--- /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 dcbaac2..198272e 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.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 @@
private final HeadsUpNotificationViewController mPanel;
private ExpandableNotificationRow mPickedChild;
- public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
+ public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
IStatusBarService statusBarService,
Callback callback,
HeadsUpNotificationViewController notificationPanelView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index 660aa3f..1b9e5b3 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 @@
private final ViewRefactorFlag mShelfRefactor;
+ private final boolean mNewAodTransition;
+
private int mAodIconAppearTranslation;
private boolean mAnimationsEnabled;
@@ -147,6 +151,7 @@
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 @@
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 @@
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 7cbaf63..b15c0fd 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.ViewGroup;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
@@ -624,12 +625,32 @@
}
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 4b39854..235ed25 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.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 @@
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 @@
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 eedf35f..62b2445 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 @@
* 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 @@
* 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 053c27c..dbee080 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.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 @@
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 @@
Handler mainThreadHandler,
Executor uiBgExecutor,
NotificationVisibilityProvider visibilityProvider,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
ActivityStarter activityStarter,
NotificationClickNotifier clickNotifier,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -213,12 +214,15 @@
*/
@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 d07378e..4211cab 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 @@
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 @@
})
}
+ 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 fb7f9d1..2d14f6b 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 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.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 @@
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 @@
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 40bd8d3..ba73c10 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 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 babd435..63c022c 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.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.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.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 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 d9a5844..0618abb 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.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 @@
@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 6ef877b..3741f14 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 @@
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 @@
/** */
@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 9b404f1..e9e52a2 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 @@
}
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
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index e59ec04..cec76f3 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.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 @@
* 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 HeadsUpManager(@NonNull final Context context,
+ public BaseHeadsUpManager(@NonNull final Context context,
HeadsUpManagerLogger logger,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
@@ -131,6 +132,7 @@
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 @@
// 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 @@
}
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 @@
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 @@
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 @@
*
* @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 @@
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 @@
* 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 @@
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 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 0000000..21acfb4
--- /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 0000000..d9527fe
--- /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 c302d6a..859e636 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 @@
import android.content.res.Resources
import com.android.systemui.res.R
+import javax.inject.Inject
/**
* Fake SplitShadeStateController
@@ -24,7 +25,7 @@
* 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 a77c692..818ba95 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.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 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 0000000..1160d65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.policy.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Tracks state related to device provisioning. */
+interface DeviceProvisioningRepository {
+ /**
+ * Whether this device has been provisioned.
+ *
+ * @see android.provider.Settings.Global.DEVICE_PROVISIONED
+ */
+ val isDeviceProvisioned: Flow<Boolean>
+
+ /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */
+ val isFactoryResetProtectionActive: Flow<Boolean>
+}
+
+@Module
+interface DeviceProvisioningRepositoryModule {
+ @Binds fun bindsImpl(impl: DeviceProvisioningRepositoryImpl): DeviceProvisioningRepository
+}
+
+class DeviceProvisioningRepositoryImpl
+@Inject
+constructor(
+ private val deviceProvisionedController: DeviceProvisionedController,
+) : DeviceProvisioningRepository {
+
+ override val isDeviceProvisioned: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onDeviceProvisionedChanged() {
+ trySend(deviceProvisionedController.isDeviceProvisioned)
+ }
+ }
+ deviceProvisionedController.addCallback(listener)
+ trySend(deviceProvisionedController.isDeviceProvisioned)
+ awaitClose { deviceProvisionedController.removeCallback(listener) }
+ }
+
+ override val isFactoryResetProtectionActive: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onFrpActiveChanged() {
+ trySend(deviceProvisionedController.isFrpActive)
+ }
+ }
+ deviceProvisionedController.addCallback(listener)
+ trySend(deviceProvisionedController.isFrpActive)
+ awaitClose { deviceProvisionedController.removeCallback(listener) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index ce9d6fd..dbc3bf3 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 @@
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 @@
) {
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 @@
}
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 @@
// 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 d3f83b1..20f0fa8c 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 @@
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 @@
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 @@
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 @@
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 0000000..ac04d31
--- /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 47d505e..83ff789 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 @@
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 @@
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(
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(
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> 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 @@
* 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 @@
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 @@
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 @@
* latest value is emitted.
*
* Example:
- *
* ```kotlin
* flow {
* emit(1) // t=0ms
@@ -210,7 +208,6 @@
* during this period, only The latest value is emitted.
*
* Example:
- *
* ```kotlin
* flow {
* emit(1) // t=0ms
@@ -248,10 +245,11 @@
// 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 @@
}
}
}
-}
\ 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 0000000..51d2afa
--- /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 750b6f9..2132904 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.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 @@
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 @@
UserTracker userTracker,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarKeyguardViewManager keyguardViewManager,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
mKeyguardStateController = keyguardStateController;
mKeyguardDismissUtil = keyguardDismissUtil;
mActivityStarter = activityStarter;
@@ -103,6 +106,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardViewManager = keyguardViewManager;
mUiEventLogger = uiEventLogger;
+ mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
}
@Override
@@ -209,6 +213,7 @@
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 8990583..167e341 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
@@ -18,61 +18,94 @@
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 f943acd..d8a2c5f 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 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.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 e090093..dc1618d 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 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.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 8322b37..4a24e4a 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 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.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 2f08804..9df4dd4 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.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.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 f3a1b68..5102957 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 @@
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 d54843d3..62f9a9d 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.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.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
@@ -812,6 +810,7 @@
SceneKey.Bouncer,
flowOf(.5f),
false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -827,7 +826,8 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false
+ false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -844,7 +844,8 @@
SceneKey.Gone,
SceneKey.Bouncer,
flowOf(.5f),
- false
+ false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -862,7 +863,8 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false
+ false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -878,6 +880,7 @@
SceneKey.Lockscreen,
flowOf(.5f),
false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -895,6 +898,7 @@
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 aad11d9..156e068 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.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 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 b02b1f9..7bb6ef1 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.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.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 d0bb5a9..4290b8b 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 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.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 59cd26c..31ee641 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 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.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardSimPukViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index e4e2b0a..9a908d7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -29,9 +29,9 @@
import android.testing.TestableLooper;
import android.view.View;
-import com.android.systemui.res.R;
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;
@@ -81,7 +81,7 @@
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 +96,7 @@
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);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 21a2822..d61ca69 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.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.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 @@
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 @@
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 @@
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 @@
KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags,
mPrimaryBouncerInteractor,
- mContext
+ mContext,
+ () -> mBouncerInteractor,
+ mSceneTestUtils.getSceneContainerFlags()
);
}
@@ -227,8 +235,8 @@
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 a2dc776..4bacc3d 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 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 @@
}
@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 @@
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 @@
// 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 c372f45..1213518 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.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 @@
/** After migration, replaces LockIconViewControllerTest version */
@Test
- fun testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() =
+ fun testLockIcon_clearsIconWhenUnlocked() =
runBlocking(IMMEDIATE) {
// GIVEN udfps not enrolled
setupUdfps()
@@ -50,14 +51,14 @@
// 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 59c7e76..8faf715 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 @@
waitForIdleSync()
verify(controller).onLaunchAnimationCancelled()
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+ verify(listener).onLaunchAnimationCancelled()
+ verify(listener, never()).onLaunchAnimationStart()
+ assertNull(runner.delegate)
}
@Test
@@ -176,6 +179,9 @@
waitForIdleSync()
verify(controller).onLaunchAnimationCancelled()
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+ verify(listener).onLaunchAnimationCancelled()
+ verify(listener, never()).onLaunchAnimationStart()
+ assertNull(runner.delegate)
}
@Test
@@ -194,6 +200,15 @@
}
}
+ @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 212dad7..c2e6db3 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 @@
}
@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 0283382..6ac84bc 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 @@
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 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 a102890..2f5f460 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.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 2bc0171..885abcb 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 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 @@
@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 7365460..d68a313 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 @@
@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 @@
// Clean up job
detector.disable()
}
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val detector: AuthDialogPanelInteractionDetector
+ val shadeRepository: FakeShadeRepository
+ val testScope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ ): TestComponent
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
index 8547fa3..714461b 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 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.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 ab5d8bea5..39f0d57 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 @@
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.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 57cf834..ef06e0e 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 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 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 469f65a..e2aa984 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 @@
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.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 21e614f..e9e9624 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.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 @@
@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 ee3bd0d..a36f4e9 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.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 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.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 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 280bfdf..cd9189b 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 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 @@
@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 1afb223..5239966 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 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.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 8508f45..b018a3e 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 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 17f435b..da4548b 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 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 @@
@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 6d55254..8b374ae 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.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.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 ebadfc7..0c8e7a5 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 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_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 0d17270..2d8adca 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 @@
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 @@
@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 a9ba36a..915661b 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 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 a81ca86..4aea4f3 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 @@
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.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 cb0b74f..2018e61 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.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.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 b5177e1..8e1f5ac 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.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 b75355a..2c97809 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.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 333bd21..802f8e6 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.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.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 0926399..ba8dcef 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 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 2e7c9aa..bfaa6ed 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 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 255bbe3..7873899 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 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 @@
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 @@
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 @@
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 @@
) // 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 4c279ea..55016bb 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 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 7fa828f..3df9cbb 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.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 @@
@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 ddf788e..cdc42e0 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 @@
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.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 a10eb29..41a8be9 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.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.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 8e8cbe4..2c80035 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.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 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 @@
keyguardBypassController = keyguardBypassController,
keyguardStateController = keyguardStateController,
)
+ testScope.runCurrent()
}
@Test
@@ -66,8 +69,7 @@
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 @@
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 @@
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 55582e1..c13fde7 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.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 @@
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 781ad6b..8a35ef1 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.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.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 21192fa..2c6c793 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 @@
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.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 7c36642..2af6566 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 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.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 be7638e..d379dc6 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 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.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 8379f73..e5f9972 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 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.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 2ef227c..365f67b 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 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 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 12cb332..7ff345c 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 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 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 9566e81..39db2be 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 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.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 d32788d..315a24b 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.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.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 4a7700f..e0c6ab2 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 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 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 cd2efde..480754c 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 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 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 ffcaeee..3d1efa5 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 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 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 ff6d97d..6aa821f 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 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 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 da39381..017fdbe 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 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.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 81f6fe3..4ee4a60 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 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.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 291dda20..a646823 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 @@
}
@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 @@
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 @@
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 cfee3b8..e20d3af 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.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 @@
@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 49168d0..faf9751 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.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 @@
@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 c85c7f6..548b564 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.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 c3e28ae..4ae144c 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 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 @@
@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 ef56a98..7d68cc0 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 @@
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.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 4f071bd..1e80fb6 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 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 @@
@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 bd0b71d..99a0185 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 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 @@
@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 0797d07..a1c9f87 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.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 @@
@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 d8c0341..b15352b 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.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 @@
@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 9d983b8..521dea3 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 @@
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.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 613c4ce..02db0d7 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 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 @@
@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 1414bac..a9b9c90 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 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 @@
@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 944b059..d8cdf29 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_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 @@
@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 307204da..819d08a 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_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 @@
@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 def016a..a58bc52 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.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 @@
@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 7eb8a26..9be5558 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 @@
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 @@
@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 126b841..567e0a9 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 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 @@
@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 7983e30..b9119e1 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.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 @@
@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 4942cf8..799bd5a 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.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.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 7f784d8..ee47c58f4 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 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 @@
@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 181cc88..d6e19cb 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 @@
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 @@
@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 c407b14..a5cfbbf 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 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.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 b527510..06eb0dd 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.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 @@
fakeDeviceEntryFingerprintAuthRepository,
fakeUserRepository,
facePropertyRepository,
- fakeKeyguardRepository,
faceWakeUpTriggersConfig,
powerInteractor,
)
@@ -440,6 +441,43 @@
}
@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 ca52cdb..9ee22c8 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 @@
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 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardInteractorTest : SysuiTestCase() {
@@ -206,7 +204,8 @@
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 bbe6823..900413c 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.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.verify
import org.mockito.junit.MockitoJUnit
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardKeyEventInteractorTest : SysuiTestCase() {
@@ -132,58 +134,73 @@
}
@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 @@
.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 13025a0..0c74a38 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.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 @@
@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 8c13bb4..347d580 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.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 @@
@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 fdcc66b..71fcf6f 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 @@
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.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 9e9c25e..29b546b 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 @@
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.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyguardTransitionInteractorTest : SysuiTestCase() {
@@ -230,4 +228,432 @@
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 906d948..c02add1 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 @@
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.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 73ecae5..16f2fa2 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 @@
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.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 a22f603..5b29a86 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.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.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 7a17435..9b2db3e 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 @@
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.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 0000000..1768f8c
--- /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 bfc6f31..6d47aed 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 @@
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.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 75c8bff..cf20129 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 @@
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.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 6e94691..7de28de 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 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 12fe07f..89a1d2b 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 @@
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.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 83ae631..41f8856 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 @@
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.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 8860399..ec95cb8 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 @@
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.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 da372ea..9364097 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 @@
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.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 9ab9b3d..32acefe 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 @@
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 @@
@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 d57765c..c8c134a 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.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.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.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 @@
@Mock private FeatureFlagsClassic mFeatureFlags;
private View mQsView;
+ private final CommandQueue mCommandQueue =
+ new CommandQueue(mContext, new FakeDisplayTracker(mContext));
+
private QSImpl mUnderTest;
@@ -120,6 +126,16 @@
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 @@
private QSImpl instantiate() {
MockitoAnnotations.initMocks(this);
- CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
setupQsComponent();
setUpViews();
@@ -484,11 +499,11 @@
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 e2ac7bc..b825c08 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.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.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.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 @@
}
@Test
- public void onViewDetached_removesJustTheAssociatedCallback() {
+ public void onDestroy_removesJustTheAssociatedCallback() {
+ QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
+
+ 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).removeCallback(record.callback);
+ 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 9a55f72..d277fca 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 @@
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 @@
@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 30f5811..3db676d 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.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.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 995de66..070e07a 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.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.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 dc09a33..ff8a9bd 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.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 @@
@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 08adebb..f7c3b21 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 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.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 2087623..9516c21 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 @@
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 81fd72b..36e860e 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 @@
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.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 389580c1..d4a9fab 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 @@
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.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 15e401d..4454a3c 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 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.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 7c6dd24..d153e9d 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 @@
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 @@
@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 469eee3..ec139e4 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 @@
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 @@
@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 b6eaa39..4fae532 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 @@
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 @@
@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 a755fbb..9e2d1f8 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 @@
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 @@
@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 daacca51..0116bd9 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 @@
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 @@
@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 4b5f7f6..e7ea9a6 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 @@
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 @@
@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 32d9db2..20fd360 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 @@
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 @@
@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 fb513a6..19ac63c 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 @@
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 @@
@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 5ce15fa..d645ee3 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 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 @@
@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 1c8cb54..83ff35d 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 @@
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 @@
@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 de1d29fd..adccc84 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_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 @@
@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 bb18115..41a7ec0 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 @@
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.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 a750524..a89338a 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.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.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 151b256..0d97115 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 @@
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.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 2e6b50b..f73cab8 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 @@
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.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 34c4c98..558e769 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 @@
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 06b7a9f..5659f01 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 @@
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.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
index 2c4e10e..9861606 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
@@ -20,7 +20,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSEvent
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
@@ -34,7 +33,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QSTileAnalyticsTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
index 4f25d12..a6199c2 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.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.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DisabledByPolicyInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 4401e0d..f1fcee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -39,7 +38,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QSTileLoggerTest : SysuiTestCase() {
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 4760dfa..2084aeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -20,7 +20,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.android.internal.logging.InstanceId
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.common.shared.model.ContentDescription
@@ -49,7 +48,6 @@
// TODO(b/299909368): Add more tests
@MediumTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index f1c99d7..58e36be 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 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 91f3865..2e16577 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 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 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 @@
*/
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class SceneFrameworkIntegrationTest : SysuiTestCase() {
private val utils = SceneTestUtils(this)
@@ -462,7 +463,8 @@
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 ff28d2d..740c6d9 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.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 @@
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 2187f36..7ae501d 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.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 afc0e69..31d26c0 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.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 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
@@ -124,7 +122,8 @@
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 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
val transitioning by
@@ -180,7 +180,8 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
underTest.setTransitionState(transitionState)
@@ -197,7 +198,8 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
val transitioning by
@@ -225,7 +227,8 @@
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 3785fd7..8be4eeb 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.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 0216a0a..3b9621e 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 @@
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 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 @@
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 @@
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 0b56a59..c89cd9e 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.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 1bb2ff8..263b001 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 @@
@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 52b25f8..c32d259 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 @@
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 7b12931..8d8c70e 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.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.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.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.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.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 java.util.List;
import java.util.Optional;
-import dagger.Lazy;
import kotlinx.coroutines.CoroutineDispatcher;
public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@@ -208,7 +209,7 @@
@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 @@
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
mDumpManager,
- mock(HeadsUpManagerPhone.class),
+ mock(HeadsUpManager.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
mInteractionJankMonitor, mShadeExpansionStateManager),
mKeyguardBypassController,
@@ -547,7 +548,7 @@
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 2ed2090..eb00610 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 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.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 @@
@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 @@
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private float mPreferredRefreshRate = -1;
+ private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+ private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
@Before
public void setUp() {
@@ -137,22 +156,86 @@
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 9651072..b4f9e8d 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.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.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.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 @@
@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 @@
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 @@
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
keyEventInteractor,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
)
underTest.setupExpandedStatusBar()
underTest.setDragDownHelper(dragDownHelper)
@@ -425,29 +431,37 @@
}
@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()
+ }
- // Verify that NPVC gets a chance to use the touch
- verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT)
+ @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)
+
+ // 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 0023020..189c9e2 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.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 @@
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 @@
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 @@
),
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 8138b32..65174ba 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 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.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 @@
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 @@
@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 @@
@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 @@
new ShadeExpansionStateManager();
protected FragmentHostManager.FragmentListener mFragmentListener;
+ private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+ private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
@Before
public void setup() {
@@ -169,21 +188,89 @@
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 @@
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 b5841a9..215f8b1 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.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 2be1c09..bcb060d 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 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class ShadeInteractorTest : SysuiTestCase() {
+
+ @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
- 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
-
@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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -662,7 +633,8 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -698,7 +670,8 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -974,7 +947,8 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1011,7 +985,8 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isUserInputDriven = true,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1048,7 +1023,8 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1085,7 +1061,8 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isUserInputDriven = true,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1120,8 +1097,9 @@
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 @@
// 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 7463e65..6eeafefd 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 @@
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 @@
@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 0925858..bb20d94 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.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 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = MutableStateFlow(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
)
@@ -102,7 +104,8 @@
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
progress = MutableStateFlow(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
)
@@ -120,7 +123,8 @@
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 602bd5f..c423782 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 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 e714736..ae659f4 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 @@
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 @@
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 @@
@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 @@
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 @@
@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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
// 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 @@
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 @@
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 7a2d122..b8fe2f9 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 @@
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.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 f1c181f..e093859 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.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.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 7af29ba..886c61a 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.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 @@
@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 a7c223d..0b5aea7 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 @@
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.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 14e58e5..e8923a5 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.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.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 @@
@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 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 scrimController: ScrimController
+ @Mock lateinit var shadeViewController: ShadeViewController
+ @Mock lateinit var stackscroller: NotificationStackScrollLayout
+ @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 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
transitionController.dragDownAmount = 10f
- verify(singleShadeOverScroller).expansionDragDownAmount = 10f
- verifyZeroInteractions(splitShadeOverScroller)
+ verify(nsslController).setOverScrollAmount(0)
+ verify(scrimController, never()).setNotificationsOverScrollAmount(anyInt())
}
@Test
@@ -542,8 +483,8 @@
transitionController.dragDownAmount = 10f
- verify(splitShadeOverScroller).expansionDragDownAmount = 10f
- verifyZeroInteractions(singleShadeOverScroller)
+ verify(nsslController).setOverScrollAmount(0)
+ verify(scrimController).setNotificationsOverScrollAmount(0)
}
@Test
@@ -591,6 +532,32 @@
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 fbb8ebf..20e5c43 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.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 @@
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 5b919d9..3fef1d9 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 @@
@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 @@
@Test
fun testReturnEmptyListWhenNoWifiPickerTracker() {
- `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(null)
+ `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(null)
val otherController = AccessPointControllerImpl(
userManager,
userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index d86f8bb..235ac5c 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 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 @@
@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 257cc5b..4f1581c 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.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 @@
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 @@
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 fbd61f4..546abd4 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.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.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 @@
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 @@
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 @@
testScope.backgroundScope,
sectionHeaderVisibilityProvider,
fakeSettings,
- seenNotificationsProvider,
+ notificationListInteractor,
statusBarStateController,
)
keyguardCoordinator.attach(notifPipeline)
@@ -619,7 +618,7 @@
KeyguardCoordinatorTestScope(
keyguardCoordinator,
testScope,
- seenNotificationsProvider,
+ notificationListInteractor,
fakeSettings,
)
.testBlock()
@@ -629,7 +628,7 @@
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 0000000..2f8f3bb
--- /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 0000000..705a5a3
--- /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 7caa5ccc..e57986d 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.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.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -46,7 +43,6 @@
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 @@
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 0000000..31efebb
--- /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 0000000..e1e7f92
--- /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 c9b77c5..9c20e54 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.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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
.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 9f2afdf..8a730cf 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.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.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 @@
@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 @@
mTestScope.getBackgroundScope(),
new WindowRootViewVisibilityRepository(mBarService, mExecutor),
new FakeKeyguardRepository(),
- mHeadsUpManagerPhone,
+ mHeadsUpManager,
PowerInteractorFactory.create().getPowerInteractor());
mGutsManager = new NotificationGutsManager(
@@ -196,9 +195,10 @@
mWindowRootViewVisibilityInteractor,
mNotificationLockscreenUserManager,
mStatusBarStateController,
+ mBarService,
mDeviceProvisionedController,
mMetricsLogger,
- mHeadsUpManagerPhone,
+ mHeadsUpManager,
mActivityStarter);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mOnSettingsClickListener);
@@ -239,7 +239,7 @@
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 @@
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 ac680e6..cb73108 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.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.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 @@
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 @@
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 ffe312b..20197e3 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.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.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 @@
@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 @@
@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 @@
@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 @@
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 3a820e8..236bcb4 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 @@
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 @@
}
@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 a52466d..93faa77 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 @@
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 @@
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 e254dd0..ac11ff2 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 @@
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 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerViewModelTest : SysuiTestCase() {
- private val utils = SceneTestUtils(this)
- private val testScope = utils.testScope
+ private lateinit var testComponent: TestComponent
- 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 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 @@
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 @@
)
)
}
+
+ @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 a5d3484..e7dad6a 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.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 @@
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 e33fa22..f18af61 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.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.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 @@
@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 @@
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 @@
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 ff6f40d5..593c587 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.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 @@
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 ec6286b..d84bb72 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.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 @@
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 @@
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 1bc522d..cda2a74 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 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.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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@Ignore("b/141538055")
@Test
public void testCanRemoveImmediately_notTopEntry() {
- final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+ final HeadsUpManager hmp = createHeadsUpManagerPhone();
final NotificationEntry earlierEntry = createEntry(/* id = */ 0);
final NotificationEntry laterEntry = createEntry(/* id = */ 1);
laterEntry.setRow(mRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bac8579..b36d09d 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 @@
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.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 @@
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 @@
}
@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 8013e5e..beac995 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.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 @@
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 233f407..ee4f208 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.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 @@
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 dbaa29b..d06a6e2 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.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 @@
@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 206ac1d..ce00250 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 @@
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.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 c2e75aa..cf20ba8 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.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 @@
@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 afab623..c2f5665 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 @@
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 1db8065..7fbbfc7 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 @@
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 @@
@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 49a2648..2d1a27f 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 @@
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.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 64ebcd9..4f3f564 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.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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
.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 @@
@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 @@
.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 @@
@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 @@
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 0000000..12694ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.policy.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProvisioningRepositoryImplTest : SysuiTestCase() {
+
+ @Mock lateinit var deviceProvisionedController: DeviceProvisionedController
+
+ lateinit var underTest: DeviceProvisioningRepositoryImpl
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ DeviceProvisioningRepositoryImpl(
+ deviceProvisionedController,
+ )
+ }
+
+ @Test
+ fun isDeviceProvisioned_reflectsCurrentControllerState() = runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+ assertThat(deviceProvisioned).isTrue()
+ }
+
+ @Test
+ fun isDeviceProvisioned_updatesWhenControllerStateChanges_toTrue() = runTest {
+ val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+ runCurrent()
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onDeviceProvisionedChanged()
+ assertThat(deviceProvisioned).isTrue()
+ }
+
+ @Test
+ fun isDeviceProvisioned_updatesWhenControllerStateChanges_toFalse() = runTest {
+ val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+ runCurrent()
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onDeviceProvisionedChanged()
+ assertThat(deviceProvisioned).isFalse()
+ }
+
+ @Test
+ fun isFrpActive_reflectsCurrentControllerState() = runTest {
+ whenever(deviceProvisionedController.isFrpActive).thenReturn(true)
+ val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+ assertThat(frpActive).isTrue()
+ }
+
+ @Test
+ fun isFrpActive_updatesWhenControllerStateChanges_toTrue() = runTest {
+ val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+ runCurrent()
+ whenever(deviceProvisionedController.isFrpActive).thenReturn(true)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onFrpActiveChanged()
+ assertThat(frpActive).isTrue()
+ }
+
+ @Test
+ fun isFrpActive_updatesWhenControllerStateChanges_toFalse() = runTest {
+ val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+ runCurrent()
+ whenever(deviceProvisionedController.isFrpActive).thenReturn(false)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onFrpActiveChanged()
+ assertThat(frpActive).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index af941d0..c56266d 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 @@
@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 @@
@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 @@
}
@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 @@
}
}
+ @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 6932f5e..c236b12 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.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 @@
}
@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 0000000..aaf8d07
--- /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 409ba48..c832702 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 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.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 @@
private UserHandle mUser0;
private FakeBubbleProperties mBubbleProperties;
+ private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+ private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
@Before
public void setUp() throws Exception {
@@ -350,21 +369,94 @@
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 0e59496..dc5fd95 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/RoboPilotTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
deleted file mode 100644
index 3fff136..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Mark as tests for Robolectric pilot projects. The filter can better help grouping test results
- * that runs on CI
- */
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface RoboPilotTest {
-}
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 0000000..8fa6695
--- /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 4fc3e3f..ddfe79a 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.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 @@
}
}
}
+
+@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 0000000..23bad39
--- /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 0000000..92acc94
--- /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 0000000..554fc75
--- /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 d47e88f..5038285 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 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 @@
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 f866932..cffbf02 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 @@
@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 0000000..ef02bdd
--- /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 0000000..f4feee1
--- /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 5e60a09..f029348 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 @@
}
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/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 766f748..a4881bc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -80,7 +80,11 @@
) {
val testDispatcher = StandardTestDispatcher()
val testScope = TestScope(testDispatcher)
- val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
+ val featureFlags =
+ FakeFeatureFlagsClassic().apply {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ }
val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true }
val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() }
val authenticationRepository: FakeAuthenticationRepository by lazy {
@@ -205,7 +209,7 @@
return BouncerInteractor(
applicationScope = applicationScope(),
applicationContext = context,
- repository = BouncerRepository(),
+ repository = BouncerRepository(featureFlags),
deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt
index 1bec82b..e59f642 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 0000000..788e3aa
--- /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 0000000..5d3cb4d
--- /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 16a3268..23477d8 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 @@
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 0000000..14bab84
--- /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 0000000..5aece1b
--- /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 0000000..3002299
--- /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 5de05c2..1f48d94 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.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 0000000..3e3d7cb
--- /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 155dc1a..18f78314 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -49,7 +49,7 @@
"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 b781602..0244c0f 100644
--- a/packages/overlays/tests/Android.bp
+++ b/packages/overlays/tests/Android.bp
@@ -34,7 +34,7 @@
"androidx.test.rules",
"androidx.test.espresso.core",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
dxflags: ["--multi-dex"],
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 11189cf..6fa9c08 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -34,3 +34,10 @@
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 c5495d9..9455628 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.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 @@
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 7e09b5e..258820a 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 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 @@
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 @@
.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 @@
return false;
}
- @GuardedBy("mLock")
+ @GuardedBy("AppWidgetServiceImpl.mLock")
public AppWidgetProviderInfo getInfoLocked(Context context) {
if (!mInfoParsed) {
// parse
@@ -4159,18 +4181,18 @@
* 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 d43a219..eb23f2f 100644
--- a/services/autofill/Android.bp
+++ b/services/autofill/Android.bp
@@ -19,19 +19,4 @@
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 ef23754..123b65c 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -5,4 +5,11 @@
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/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 55130e4..987507f 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -16,9 +16,6 @@
# 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 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 9bab261..93dca2f 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -359,8 +359,10 @@
@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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8cc2665..962f38f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -214,6 +214,9 @@
// 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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 b05b397..55aa716 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -644,6 +644,16 @@
}
/**
+ * 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/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java
index 378a386..9ba49ce 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 @@
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 @@
*/
@VisibleForTesting
static class Injector {
- /**
- * Return a handler for the given Callback.
- */
- Handler getHandler(@NonNull Handler.Callback callback) {
- return null;
- };
+ private final Handler mReferenceHandler;
+
+ Injector(@NonNull Handler handler) {
+ mReferenceHandler = handler;
+ }
/**
- * Return a CpuTracker.
+ * 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) {
+ Looper looper = mReferenceHandler.getLooper();
+ if (looper == null) looper = Looper.getMainLooper();
+ return new Handler(looper, callback);
+ };
+
+ /** 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 @@
/** 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 @@
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 @@
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 907069d..147f8d1 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 @@
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 @@
}
}
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 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 5d31d15..e07c2bc 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 @@
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 eb219a8..a428907 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 0000000..17a4ce5
--- /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 a97675f..4572766 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.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.OomKillRecord;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -412,6 +413,8 @@
private static LmkdConnection sLmkdConnection = null;
+ private static OomConnection sOomConnection = null;
+
private boolean mOomLevelsSet = false;
private boolean mAppDataIsolationEnabled = false;
@@ -855,6 +858,21 @@
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/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4a0bc4b..1ba1f55 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 @@
"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 b03cc62..26d99d8 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 @@
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/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 9805fd3..333f62a 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 @@
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 9e92c8d..cfbe0c6 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.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 @@
* </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 @@
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 @@
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 @@
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 @@
}
/**
+ * @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 @@
+ ", mThermalBrightnessThrottlingDataMapByThrottlingId="
+ mThermalBrightnessThrottlingDataMapByThrottlingId
+ "\n"
+ + ", mPowerThrottlingDataMapByThrottlingId="
+ + mPowerThrottlingDataMapByThrottlingId
+ + "\n"
+ "mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
@@ -1853,6 +1904,7 @@
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
loadThermalThrottlingConfig(config);
+ loadPowerThrottlingConfigData(config);
loadHighBrightnessModeData(config);
loadLuxThrottling(config);
loadQuirks(config);
@@ -2171,6 +2223,59 @@
}
}
+ 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 @@
}
/**
+ * 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 652e6cf..39f0b13 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 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 b6273e1..e66fa5b 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 @@
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 @@
/** Returns whether resolution range voting feature is enabled or not. */
public boolean isDisplayResolutionRangeVotingEnabled() {
- return mDisplayResolutionRangeVotingState.isEnabled();
+ return isExternalDisplayLimitModeEnabled();
}
/**
@@ -98,7 +86,7 @@
* {@link com.android.server.display.mode.DisplayModeDirector}
*/
public boolean isUserPreferredModeVoteEnabled() {
- return mUserPreferredModeVoteState.isEnabled();
+ return isExternalDisplayLimitModeEnabled();
}
/**
@@ -112,7 +100,7 @@
* @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 @@
}
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/input/FocusEventDebugView.java b/services/core/java/com/android/server/input/FocusEventDebugView.java
deleted file mode 100644
index 4b8fabde..0000000
--- a/services/core/java/com/android/server/input/FocusEventDebugView.java
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.input;
-
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-import static android.util.TypedValue.COMPLEX_UNIT_SP;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.animation.LayoutTransition;
-import android.annotation.AnyThread;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RoundedCorner;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.WindowInsets;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.HorizontalScrollView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/**
- * Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
- * the screen.
- */
-class FocusEventDebugView extends RelativeLayout {
-
- private static final String TAG = FocusEventDebugView.class.getSimpleName();
-
- private static final int KEY_FADEOUT_DURATION_MILLIS = 1000;
- private static final int KEY_TRANSITION_DURATION_MILLIS = 100;
-
- private static final int OUTER_PADDING_DP = 16;
- private static final int KEY_SEPARATION_MARGIN_DP = 16;
- private static final int KEY_VIEW_SIDE_PADDING_DP = 16;
- private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8;
- private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
- private static final int KEY_VIEW_TEXT_SIZE_SP = 12;
- private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5;
-
- private final InputManagerService mService;
- private final int mOuterPadding;
- private final DisplayMetrics mDm;
-
- // Tracks all keys that are currently pressed/down.
- private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
- mPressedKeys = new HashMap<>();
-
- @Nullable
- private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor;
- @Nullable
- private PressedKeyContainer mPressedKeyContainer;
- @Nullable
- private PressedKeyContainer mPressedModifierContainer;
- private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
- @Nullable
- private RotaryInputValueView mRotaryInputValueView;
- private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory;
- @Nullable
- private RotaryInputGraphView mRotaryInputGraphView;
-
- @VisibleForTesting
- FocusEventDebugView(Context c, InputManagerService service,
- Supplier<RotaryInputValueView> rotaryInputValueViewFactory,
- Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) {
- super(c);
- setFocusableInTouchMode(true);
-
- mService = service;
- mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
- mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory;
- mDm = mContext.getResources().getDisplayMetrics();
- mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm);
- }
-
- FocusEventDebugView(Context c, InputManagerService service) {
- this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c));
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- int paddingBottom = 0;
-
- final RoundedCorner bottomLeft =
- insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- if (bottomLeft != null && !insets.isRound()) {
- paddingBottom = bottomLeft.getRadius();
- }
-
- final RoundedCorner bottomRight =
- insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
- if (bottomRight != null && !insets.isRound()) {
- paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
- }
-
- if (insets.getDisplayCutout() != null) {
- paddingBottom =
- Math.max(paddingBottom, insets.getDisplayCutout().getSafeInsetBottom());
- }
-
- setPadding(mOuterPadding, mOuterPadding, mOuterPadding, mOuterPadding + paddingBottom);
- setClipToPadding(false);
- invalidate();
- return super.onApplyWindowInsets(insets);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- handleKeyEvent(event);
- return super.dispatchKeyEvent(event);
- }
-
- @AnyThread
- public void updateShowKeyPresses(boolean enabled) {
- post(() -> handleUpdateShowKeyPresses(enabled));
- }
-
- @AnyThread
- public void updateShowRotaryInput(boolean enabled) {
- post(() -> handleUpdateShowRotaryInput(enabled));
- }
-
- private void handleUpdateShowKeyPresses(boolean enabled) {
- if (enabled == showKeyPresses()) {
- return;
- }
-
- if (!enabled) {
- removeView(mPressedKeyContainer);
- mPressedKeyContainer = null;
- removeView(mPressedModifierContainer);
- mPressedModifierContainer = null;
- return;
- }
-
- mPressedKeyContainer = new PressedKeyContainer(mContext);
- mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
- mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
- mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
- final var scroller = new HorizontalScrollView(mContext);
- scroller.addView(mPressedKeyContainer);
- scroller.setHorizontalScrollBarEnabled(false);
- scroller.addOnLayoutChangeListener(
- (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
- scroller.setHorizontalFadingEdgeEnabled(true);
- LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
- scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
- addView(scroller, scrollerLayoutParams);
-
- mPressedModifierContainer = new PressedKeyContainer(mContext);
- mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
- mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
- LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
- modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
- modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
- addView(mPressedModifierContainer, modifierLayoutParams);
- }
-
- @VisibleForTesting
- void handleUpdateShowRotaryInput(boolean enabled) {
- if (enabled == showRotaryInput()) {
- return;
- }
-
- if (!enabled) {
- mFocusEventDebugGlobalMonitor.dispose();
- mFocusEventDebugGlobalMonitor = null;
- removeView(mRotaryInputValueView);
- mRotaryInputValueView = null;
- removeView(mRotaryInputGraphView);
- mRotaryInputGraphView = null;
- return;
- }
-
- mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
-
- mRotaryInputValueView = mRotaryInputValueViewFactory.get();
- LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- valueLayoutParams.addRule(CENTER_HORIZONTAL);
- valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
- addView(mRotaryInputValueView, valueLayoutParams);
-
- mRotaryInputGraphView = mRotaryInputGraphViewFactory.get();
- LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
- (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels));
- graphLayoutParams.addRule(CENTER_IN_PARENT);
- addView(mRotaryInputGraphView, graphLayoutParams);
- }
-
- /** Report a key event to the debug view. */
- @AnyThread
- public void reportKeyEvent(KeyEvent event) {
- post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
- }
-
- /** Report a motion event to the debug view. */
- @AnyThread
- public void reportMotionEvent(MotionEvent event) {
- if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) {
- return;
- }
-
- post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event)));
- }
-
- private void handleKeyEvent(KeyEvent keyEvent) {
- if (!showKeyPresses()) {
- return;
- }
-
- final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode());
- final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode())
- ? mPressedModifierContainer
- : mPressedKeyContainer;
- PressedKeyView pressedKeyView = mPressedKeys.get(identifier);
- switch (keyEvent.getAction()) {
- case KeyEvent.ACTION_DOWN: {
- if (pressedKeyView != null) {
- if (keyEvent.getRepeatCount() == 0) {
- Slog.w(TAG, "Got key down for "
- + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
- + " that was already tracked as being down.");
- break;
- }
- container.handleKeyRepeat(pressedKeyView);
- break;
- }
-
- pressedKeyView = new PressedKeyView(mContext, getLabel(keyEvent));
- mPressedKeys.put(identifier, pressedKeyView);
- container.handleKeyPressed(pressedKeyView);
- break;
- }
- case KeyEvent.ACTION_UP: {
- if (pressedKeyView == null) {
- Slog.w(TAG, "Got key up for " + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
- + " that was not tracked as being down.");
- break;
- }
- mPressedKeys.remove(identifier);
- container.handleKeyRelease(pressedKeyView);
- break;
- }
- default:
- break;
- }
- keyEvent.recycle();
- }
-
- @VisibleForTesting
- void handleRotaryInput(MotionEvent motionEvent) {
- if (!showRotaryInput()) {
- return;
- }
-
- float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
- mRotaryInputValueView.updateValue(scrollAxisValue);
- mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime());
-
- motionEvent.recycle();
- }
-
- private static String getLabel(KeyEvent event) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_SPACE:
- return "\u2423";
- case KeyEvent.KEYCODE_TAB:
- return "\u21e5";
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_NUMPAD_ENTER:
- return "\u23CE";
- case KeyEvent.KEYCODE_DEL:
- return "\u232B";
- case KeyEvent.KEYCODE_FORWARD_DEL:
- return "\u2326";
- case KeyEvent.KEYCODE_ESCAPE:
- return "ESC";
- case KeyEvent.KEYCODE_DPAD_UP:
- return "\u2191";
- case KeyEvent.KEYCODE_DPAD_DOWN:
- return "\u2193";
- case KeyEvent.KEYCODE_DPAD_LEFT:
- return "\u2190";
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- return "\u2192";
- case KeyEvent.KEYCODE_DPAD_UP_RIGHT:
- return "\u2197";
- case KeyEvent.KEYCODE_DPAD_UP_LEFT:
- return "\u2196";
- case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT:
- return "\u2198";
- case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
- return "\u2199";
- default:
- break;
- }
-
- final int unicodeChar = event.getUnicodeChar();
- if (unicodeChar != 0) {
- return new String(Character.toChars(unicodeChar));
- }
-
- final var label = KeyEvent.keyCodeToString(event.getKeyCode());
- if (label.startsWith("KEYCODE_")) {
- return label.substring(8);
- }
- return label;
- }
-
- /** Determine whether to show key presses by checking one of the key-related objects. */
- private boolean showKeyPresses() {
- return mPressedKeyContainer != null;
- }
-
- /** Determine whether to show rotary input by checking one of the rotary-related objects. */
- private boolean showRotaryInput() {
- return mRotaryInputValueView != null;
- }
-
- /**
- * Converts a dimension in scaled pixel units to integer display pixels.
- */
- private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
- return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
- }
-
- private static class PressedKeyView extends TextView {
-
- private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{
- -1.0f, 0, 0, 0, 255, // red
- 0, -1.0f, 0, 0, 255, // green
- 0, 0, -1.0f, 0, 255, // blue
- 0, 0, 0, 1.0f, 0 // alpha
- });
-
- PressedKeyView(Context c, String label) {
- super(c);
-
- final var dm = c.getResources().getDisplayMetrics();
- final int keyViewSidePadding =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_SIDE_PADDING_DP, dm);
- final int keyViewVerticalPadding =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_VERTICAL_PADDING_DP,
- dm);
- final int keyViewMinWidth =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_MIN_WIDTH_DP, dm);
- final int textSize =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, KEY_VIEW_TEXT_SIZE_SP, dm);
-
- setText(label);
- setGravity(Gravity.CENTER);
- setMinimumWidth(keyViewMinWidth);
- setTextSize(textSize);
- setTypeface(Typeface.SANS_SERIF);
- setBackgroundResource(R.drawable.focus_event_pressed_key_background);
- setPaddingRelative(keyViewSidePadding, keyViewVerticalPadding, keyViewSidePadding,
- keyViewVerticalPadding);
-
- setHighlighted(true);
- }
-
- void setHighlighted(boolean isHighlighted) {
- if (isHighlighted) {
- setTextColor(Color.BLACK);
- getBackground().setColorFilter(sInvertColors);
- } else {
- setTextColor(Color.WHITE);
- getBackground().clearColorFilter();
- }
- invalidate();
- }
- }
-
- private static class PressedKeyContainer extends LinearLayout {
-
- private final MarginLayoutParams mPressedKeyLayoutParams;
-
- PressedKeyContainer(Context c) {
- super(c);
-
- final var dm = c.getResources().getDisplayMetrics();
- final int keySeparationMargin =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_SEPARATION_MARGIN_DP, dm);
-
- final var transition = new LayoutTransition();
- transition.disableTransitionType(LayoutTransition.APPEARING);
- transition.disableTransitionType(LayoutTransition.DISAPPEARING);
- transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
- transition.setDuration(KEY_TRANSITION_DURATION_MILLIS);
- setLayoutTransition(transition);
-
- mPressedKeyLayoutParams = new MarginLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- if (getOrientation() == VERTICAL) {
- mPressedKeyLayoutParams.setMargins(0, keySeparationMargin, 0, 0);
- } else {
- mPressedKeyLayoutParams.setMargins(keySeparationMargin, 0, 0, 0);
- }
- }
-
- public void handleKeyPressed(PressedKeyView pressedKeyView) {
- addView(pressedKeyView, getChildCount(), mPressedKeyLayoutParams);
- invalidate();
- }
-
- public void handleKeyRepeat(PressedKeyView repeatedKeyView) {
- // Do nothing for now.
- }
-
- public void handleKeyRelease(PressedKeyView releasedKeyView) {
- releasedKeyView.setHighlighted(false);
- releasedKeyView.clearAnimation();
- releasedKeyView.animate()
- .alpha(0)
- .setDuration(KEY_FADEOUT_DURATION_MILLIS)
- .setInterpolator(new AccelerateInterpolator())
- .withEndAction(this::cleanUpPressedKeyViews)
- .start();
- }
-
- private void cleanUpPressedKeyViews() {
- int numChildrenToRemove = 0;
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- if (child.getAlpha() != 0) {
- break;
- }
- child.setVisibility(View.GONE);
- child.clearAnimation();
- numChildrenToRemove++;
- }
- removeViews(0, numChildrenToRemove);
- 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/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index 2ede56d..a2c8748 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 @@
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 6b399de..2533e02 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.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 0eb620f..bad6bf0 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 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 @@
*
* @hide
*/
-final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+class KeyboardLayoutManager implements InputManager.InputDeviceListener {
private static final String TAG = "KeyboardLayoutManager";
@@ -1295,7 +1296,8 @@
}
@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 @@
}
}
- 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
similarity index 94%
rename from services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java
rename to services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java
index 67c221f..2b21e49 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.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/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
new file mode 100644
index 0000000..6eec0de
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -0,0 +1,466 @@
+/*
+ * 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_DIP;
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.animation.LayoutTransition;
+import android.annotation.AnyThread;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Typeface;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.RoundedCorner;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.input.InputManagerService;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
+ * the screen.
+ */
+public class FocusEventDebugView extends RelativeLayout {
+
+ private static final String TAG = FocusEventDebugView.class.getSimpleName();
+
+ private static final int KEY_FADEOUT_DURATION_MILLIS = 1000;
+ private static final int KEY_TRANSITION_DURATION_MILLIS = 100;
+
+ private static final int OUTER_PADDING_DP = 16;
+ private static final int KEY_SEPARATION_MARGIN_DP = 16;
+ private static final int KEY_VIEW_SIDE_PADDING_DP = 16;
+ private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8;
+ private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
+ private static final int KEY_VIEW_TEXT_SIZE_SP = 12;
+ private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5;
+
+ private final InputManagerService mService;
+ private final int mOuterPadding;
+ private final DisplayMetrics mDm;
+
+ // Tracks all keys that are currently pressed/down.
+ private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
+ mPressedKeys = new HashMap<>();
+
+ @Nullable
+ private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor;
+ @Nullable
+ private PressedKeyContainer mPressedKeyContainer;
+ @Nullable
+ private PressedKeyContainer mPressedModifierContainer;
+ private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
+ @Nullable
+ private RotaryInputValueView mRotaryInputValueView;
+ private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory;
+ @Nullable
+ private RotaryInputGraphView mRotaryInputGraphView;
+
+ @VisibleForTesting
+ FocusEventDebugView(Context c, InputManagerService service,
+ Supplier<RotaryInputValueView> rotaryInputValueViewFactory,
+ Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) {
+ super(c);
+ setFocusableInTouchMode(true);
+
+ mService = service;
+ mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
+ mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory;
+ mDm = mContext.getResources().getDisplayMetrics();
+ mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm);
+ }
+
+ public FocusEventDebugView(Context c, InputManagerService service) {
+ this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c));
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ int paddingBottom = 0;
+
+ final RoundedCorner bottomLeft =
+ insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ if (bottomLeft != null && !insets.isRound()) {
+ paddingBottom = bottomLeft.getRadius();
+ }
+
+ final RoundedCorner bottomRight =
+ insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+ if (bottomRight != null && !insets.isRound()) {
+ paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
+ }
+
+ if (insets.getDisplayCutout() != null) {
+ paddingBottom =
+ Math.max(paddingBottom, insets.getDisplayCutout().getSafeInsetBottom());
+ }
+
+ setPadding(mOuterPadding, mOuterPadding, mOuterPadding, mOuterPadding + paddingBottom);
+ setClipToPadding(false);
+ invalidate();
+ return super.onApplyWindowInsets(insets);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ handleKeyEvent(event);
+ 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));
+ }
+
+ private void handleUpdateShowKeyPresses(boolean enabled) {
+ if (enabled == showKeyPresses()) {
+ return;
+ }
+
+ if (!enabled) {
+ removeView(mPressedKeyContainer);
+ mPressedKeyContainer = null;
+ removeView(mPressedModifierContainer);
+ mPressedModifierContainer = null;
+ return;
+ }
+
+ mPressedKeyContainer = new PressedKeyContainer(mContext);
+ mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
+ mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
+ mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
+ final var scroller = new HorizontalScrollView(mContext);
+ scroller.addView(mPressedKeyContainer);
+ scroller.setHorizontalScrollBarEnabled(false);
+ scroller.addOnLayoutChangeListener(
+ (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
+ scroller.setHorizontalFadingEdgeEnabled(true);
+ LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
+ addView(scroller, scrollerLayoutParams);
+
+ mPressedModifierContainer = new PressedKeyContainer(mContext);
+ mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
+ mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
+ LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
+ modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
+ addView(mPressedModifierContainer, modifierLayoutParams);
+ }
+
+ @VisibleForTesting
+ void handleUpdateShowRotaryInput(boolean enabled) {
+ if (enabled == showRotaryInput()) {
+ return;
+ }
+
+ if (!enabled) {
+ mFocusEventDebugGlobalMonitor.dispose();
+ mFocusEventDebugGlobalMonitor = null;
+ removeView(mRotaryInputValueView);
+ mRotaryInputValueView = null;
+ removeView(mRotaryInputGraphView);
+ mRotaryInputGraphView = null;
+ return;
+ }
+
+ mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
+
+ mRotaryInputValueView = mRotaryInputValueViewFactory.get();
+ LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ valueLayoutParams.addRule(CENTER_HORIZONTAL);
+ valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ addView(mRotaryInputValueView, valueLayoutParams);
+
+ mRotaryInputGraphView = mRotaryInputGraphViewFactory.get();
+ LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
+ (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels));
+ graphLayoutParams.addRule(CENTER_IN_PARENT);
+ addView(mRotaryInputGraphView, graphLayoutParams);
+ }
+
+ /** Report a key event to the debug view. */
+ @AnyThread
+ public void reportKeyEvent(KeyEvent event) {
+ post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
+ }
+
+ /** Report a motion event to the debug view. */
+ @AnyThread
+ public void reportMotionEvent(MotionEvent event) {
+ if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) {
+ return;
+ }
+
+ post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event)));
+ }
+
+ private void handleKeyEvent(KeyEvent keyEvent) {
+ if (!showKeyPresses()) {
+ return;
+ }
+
+ final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode());
+ final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode())
+ ? mPressedModifierContainer
+ : mPressedKeyContainer;
+ PressedKeyView pressedKeyView = mPressedKeys.get(identifier);
+ switch (keyEvent.getAction()) {
+ case KeyEvent.ACTION_DOWN: {
+ if (pressedKeyView != null) {
+ if (keyEvent.getRepeatCount() == 0) {
+ Slog.w(TAG, "Got key down for "
+ + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
+ + " that was already tracked as being down.");
+ break;
+ }
+ container.handleKeyRepeat(pressedKeyView);
+ break;
+ }
+
+ pressedKeyView = new PressedKeyView(mContext, getLabel(keyEvent));
+ mPressedKeys.put(identifier, pressedKeyView);
+ container.handleKeyPressed(pressedKeyView);
+ break;
+ }
+ case KeyEvent.ACTION_UP: {
+ if (pressedKeyView == null) {
+ Slog.w(TAG, "Got key up for " + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
+ + " that was not tracked as being down.");
+ break;
+ }
+ mPressedKeys.remove(identifier);
+ container.handleKeyRelease(pressedKeyView);
+ break;
+ }
+ default:
+ break;
+ }
+ keyEvent.recycle();
+ }
+
+ @VisibleForTesting
+ void handleRotaryInput(MotionEvent motionEvent) {
+ if (!showRotaryInput()) {
+ return;
+ }
+
+ float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
+ mRotaryInputValueView.updateValue(scrollAxisValue);
+ mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime());
+
+ motionEvent.recycle();
+ }
+
+ private static String getLabel(KeyEvent event) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_SPACE:
+ return "\u2423";
+ case KeyEvent.KEYCODE_TAB:
+ return "\u21e5";
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_NUMPAD_ENTER:
+ return "\u23CE";
+ case KeyEvent.KEYCODE_DEL:
+ return "\u232B";
+ case KeyEvent.KEYCODE_FORWARD_DEL:
+ return "\u2326";
+ case KeyEvent.KEYCODE_ESCAPE:
+ return "ESC";
+ case KeyEvent.KEYCODE_DPAD_UP:
+ return "\u2191";
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return "\u2193";
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ return "\u2190";
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return "\u2192";
+ case KeyEvent.KEYCODE_DPAD_UP_RIGHT:
+ return "\u2197";
+ case KeyEvent.KEYCODE_DPAD_UP_LEFT:
+ return "\u2196";
+ case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT:
+ return "\u2198";
+ case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
+ return "\u2199";
+ default:
+ break;
+ }
+
+ final int unicodeChar = event.getUnicodeChar();
+ if (unicodeChar != 0) {
+ return new String(Character.toChars(unicodeChar));
+ }
+
+ final var label = KeyEvent.keyCodeToString(event.getKeyCode());
+ if (label.startsWith("KEYCODE_")) {
+ return label.substring(8);
+ }
+ return label;
+ }
+
+ /** Determine whether to show key presses by checking one of the key-related objects. */
+ private boolean showKeyPresses() {
+ return mPressedKeyContainer != null;
+ }
+
+ /** Determine whether to show rotary input by checking one of the rotary-related objects. */
+ private boolean showRotaryInput() {
+ return mRotaryInputValueView != null;
+ }
+
+ private static class PressedKeyView extends TextView {
+
+ private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{
+ -1.0f, 0, 0, 0, 255, // red
+ 0, -1.0f, 0, 0, 255, // green
+ 0, 0, -1.0f, 0, 255, // blue
+ 0, 0, 0, 1.0f, 0 // alpha
+ });
+
+ PressedKeyView(Context c, String label) {
+ super(c);
+
+ final var dm = c.getResources().getDisplayMetrics();
+ final int keyViewSidePadding =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_SIDE_PADDING_DP, dm);
+ final int keyViewVerticalPadding =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_VERTICAL_PADDING_DP,
+ dm);
+ final int keyViewMinWidth =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_MIN_WIDTH_DP, dm);
+ final int textSize =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, KEY_VIEW_TEXT_SIZE_SP, dm);
+
+ setText(label);
+ setGravity(Gravity.CENTER);
+ setMinimumWidth(keyViewMinWidth);
+ setTextSize(textSize);
+ setTypeface(Typeface.SANS_SERIF);
+ setBackgroundResource(R.drawable.focus_event_pressed_key_background);
+ setPaddingRelative(keyViewSidePadding, keyViewVerticalPadding, keyViewSidePadding,
+ keyViewVerticalPadding);
+
+ setHighlighted(true);
+ }
+
+ void setHighlighted(boolean isHighlighted) {
+ if (isHighlighted) {
+ setTextColor(Color.BLACK);
+ getBackground().setColorFilter(sInvertColors);
+ } else {
+ setTextColor(Color.WHITE);
+ getBackground().clearColorFilter();
+ }
+ invalidate();
+ }
+ }
+
+ private static class PressedKeyContainer extends LinearLayout {
+
+ private final MarginLayoutParams mPressedKeyLayoutParams;
+
+ PressedKeyContainer(Context c) {
+ super(c);
+
+ final var dm = c.getResources().getDisplayMetrics();
+ final int keySeparationMargin =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_SEPARATION_MARGIN_DP, dm);
+
+ final var transition = new LayoutTransition();
+ transition.disableTransitionType(LayoutTransition.APPEARING);
+ transition.disableTransitionType(LayoutTransition.DISAPPEARING);
+ transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ transition.setDuration(KEY_TRANSITION_DURATION_MILLIS);
+ setLayoutTransition(transition);
+
+ mPressedKeyLayoutParams = new MarginLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ if (getOrientation() == VERTICAL) {
+ mPressedKeyLayoutParams.setMargins(0, keySeparationMargin, 0, 0);
+ } else {
+ mPressedKeyLayoutParams.setMargins(keySeparationMargin, 0, 0, 0);
+ }
+ }
+
+ public void handleKeyPressed(PressedKeyView pressedKeyView) {
+ addView(pressedKeyView, getChildCount(), mPressedKeyLayoutParams);
+ invalidate();
+ }
+
+ public void handleKeyRepeat(PressedKeyView repeatedKeyView) {
+ // Do nothing for now.
+ }
+
+ public void handleKeyRelease(PressedKeyView releasedKeyView) {
+ releasedKeyView.setHighlighted(false);
+ releasedKeyView.clearAnimation();
+ releasedKeyView.animate()
+ .alpha(0)
+ .setDuration(KEY_FADEOUT_DURATION_MILLIS)
+ .setInterpolator(new AccelerateInterpolator())
+ .withEndAction(this::cleanUpPressedKeyViews)
+ .start();
+ }
+
+ private void cleanUpPressedKeyViews() {
+ int numChildrenToRemove = 0;
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getAlpha() != 0) {
+ break;
+ }
+ child.setVisibility(View.GONE);
+ child.clearAnimation();
+ numChildrenToRemove++;
+ }
+ removeViews(0, numChildrenToRemove);
+ invalidate();
+ }
+ }
+}
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 0000000..95635d9
--- /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 0000000..9fadac5
--- /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 7726f40..dbbbed3 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 @@
InputConfig.NOT_FOCUSABLE
| InputConfig.NOT_TOUCHABLE
| InputConfig.SPY
- | InputConfig.INTERCEPTS_STYLUS
- | InputConfig.TRUSTED_OVERLAY;
+ | InputConfig.INTERCEPTS_STYLUS;
// Configure the surface to receive stylus events across the entire display.
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 93c66a1..08cf3f7 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 @@
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/notification/NotificationBitmapJobService.java b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java
index 4335a1d..e1a0707 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 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 @@
* @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 3776ad7..c9317d1 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.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 @@
}
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 a3c71c2..87c3067 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 @@
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 @@
@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 @@
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 @@
+ "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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 82d3e91..68a8e40 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 @@
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 @@
// 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 @@
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 @@
@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 @@
// 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 @@
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 @@
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 ebc7163..b83421f 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.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 @@
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 @@
* @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 097656c..3a6664a 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}
}
+ 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 3d89afa..becbbf2 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.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 @@
// 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 @@
/** @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 @@
hapticCustomizations = null;
}
mHapticCustomizations = hapticCustomizations;
+ mViewFeatureFlags = viewFeatureFlags;
mSafeModeEnabledVibrationEffect =
effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED)
@@ -201,12 +207,16 @@
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;
}
- return attrs;
+ if (shouldBypassInterruptionPolicy(effectId, mViewFeatureFlags)) {
+ flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+ }
+
+ return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build();
}
/** Dumps relevant state. */
@@ -295,4 +305,19 @@
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 ee3d697..45bd152 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 @@
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 @@
}
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 @@
}
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 @@
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 10a2b97..05da9df 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.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 @@
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 @@
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 @@
@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 @@
@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 @@
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 @@
* 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 @@
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 @@
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 148bf9b..01b8bf7 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 @@
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 c39b266..4a5311b 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 @@
.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 a450b4d..0b67321 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 @@
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 @@
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 @@
@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 c2885da..4237668 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 @@
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 2439159..73bcc8d 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 @@
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0f1a105..7af4aad 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.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 @@
// 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 @@
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 c9bae12..275396f 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
*/
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 @@
mOwnerPid = ownerPid;
mWindowType = windowType;
mDisplayId = displayId;
- mFocusGrantToken = focusGrantToken;
+ mInputTransferToken = inputTransferToken;
final String hostWindowName =
(mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
: "";
@@ -260,8 +260,8 @@
return mOwnerUid;
}
- IBinder getFocusGrantToken() {
- return mFocusGrantToken;
+ IBinder getInputTransferToken() {
+ return mInputTransferToken;
}
IBinder getInputChannelToken() {
@@ -290,7 +290,7 @@
// 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 @@
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 39622c1..c21930d 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 @@
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 @@
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 825d38b..af307ec3 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 @@
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 @@
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 64b7a60..90d81bd 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 @@
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 def32a1..82d4b90 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.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 @@
}
@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 @@
} 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 @@
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 5533759..cf6a1fe 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_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 @@
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 @@
// 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 @@
// 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 @@
} finally {
mService.continueWindowLayout();
try {
- ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ if (!isPip2ExperimentEnabled()) {
+ ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ }
} finally {
transitionController.continueTransitionReady();
}
@@ -2214,7 +2232,9 @@
newTransition.setReady(rootTask, true /* ready */);
}
- resumeFocusedTasksTopActivities();
+ if (!isPip2ExperimentEnabled()) {
+ resumeFocusedTasksTopActivities();
+ }
notifyActivityPipModeChanged(r.getTask(), r);
}
@@ -2299,19 +2319,36 @@
*
* @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 bbe44c5..e6d4866 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -884,8 +884,8 @@
@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 +896,7 @@
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 37f9730..2e7ff7a 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 @@
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 4eb4290..2b12e74 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 @@
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,25 +145,34 @@
}
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) {
+ /**
+ * 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;
+ }
+ }
+
+ 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;
}
- private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) {
- for (int i = tasks.size() - 1; i >= 0; i--) {
- recordSnapshot(tasks.valueAt(i), allowSnapshotHome);
- }
- }
-
/**
* Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW
* MANAGER LOCK WHEN CALLING THIS METHOD!
@@ -320,19 +330,22 @@
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 774be9e..882104a 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 @@
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 @@
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 @@
// 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 7109137..78afaa8 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 @@
// 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 82452cc..074b404 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2493,14 +2493,6 @@
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);
@@ -8803,7 +8795,7 @@
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 +8804,7 @@
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 +8876,6 @@
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 +8895,8 @@
}
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 +9128,10 @@
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 +9176,8 @@
}
}
- 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 +9190,7 @@
return;
}
final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
- mEmbeddedWindowController.getByFocusToken(targetFocusToken);
+ mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
if (embeddedWindow == null) {
Slog.e(TAG, "Embedded window not found");
return;
@@ -9430,7 +9419,7 @@
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 f8e9a56..dd9a88f 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_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 @@
}
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 ebef606..4beec2b 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.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 @@
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 @@
}
}
- 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 @@
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
+ if (surfaceTrustedOverlay()) {
+ getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
+ }
}
super.prepareSurfaces();
}
@@ -5939,7 +5945,13 @@
}
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 ec5378f..bc70658 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -78,6 +78,7 @@
"onload.cpp",
":lib_cachedAppOptimizer_native",
":lib_gameManagerService_native",
+ ":lib_oomConnection_native",
],
include_dirs: [
@@ -122,6 +123,7 @@
"libhardware_legacy",
"libhidlbase",
"libmeminfo",
+ "libmemevents",
"libmemtrackproxy",
"libmtp",
"libnativehelper",
@@ -239,3 +241,8 @@
"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 0000000..e892d23
--- /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 f6f6737..df44895 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -52,6 +52,7 @@
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 @@
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 74fdabf..68e2c9a 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -168,6 +168,24 @@
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 @@
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 e8b1c99..b7b4b16 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -54,6 +54,7 @@
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 d0a9c45..debd891 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 949b1f2..2d27f0c 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 @@
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 @@
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 @@
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 57b5d00..4e95465 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 2d4f7a4..195cae5 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 @@
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 @@
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/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index b90f08e..3c190bf 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.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 @@
.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 3eb6718..7bd1cc4 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.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.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 @@
// 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 6aa135a..43e47d7 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_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 @@
} 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 a8db05e..84a6df3 100644
--- a/services/foldables/devicestateprovider/tests/Android.bp
+++ b/services/foldables/devicestateprovider/tests/Android.bp
@@ -20,11 +20,11 @@
"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 57fa12d..49ad84a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -264,6 +264,8 @@
"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 @@
}
}
+ 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 cee2524..bb24d51 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.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 @@
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 @@
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 e04dd68..8b9efb3 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -59,7 +59,7 @@
"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 36446f6..ffe6dc5 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -44,7 +44,7 @@
"service-permission.stubs.system_server",
"servicestests-core-utils",
"servicestests-utils-mockito-extended",
- "truth-prebuilt",
+ "truth",
],
libs: [
@@ -92,7 +92,7 @@
"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 35d754b..e8fce8e 100644
--- a/services/tests/PackageManager/packageinstaller/Android.bp
+++ b/services/tests/PackageManager/packageinstaller/Android.bp
@@ -32,7 +32,7 @@
"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 bc36970..00850a5 100644
--- a/services/tests/PackageManagerComponentOverrideTests/Android.bp
+++ b/services/tests/PackageManagerComponentOverrideTests/Android.bp
@@ -38,7 +38,7 @@
"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 9c4e6fd..ad7af44 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -29,7 +29,7 @@
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 ce28682..6eacef7 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -30,7 +30,7 @@
libs: [
"tradefed",
"junit",
- "truth-prebuilt",
+ "truth",
],
static_libs: [
"ApexInstallHelper",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index 462c580..cea9c59 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 @@
"junit-params",
"androidx.test.ext.junit",
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
index 5718474..ed5f2b5 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 @@
"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 a1d846e..3aca1ca 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -41,7 +41,7 @@
"mockito-target-minus-junit4",
"platform-test-annotations",
"ShortcutManagerTestUtils",
- "truth-prebuilt",
+ "truth",
"testables",
"platformprotosnano",
"framework-protos",
@@ -51,7 +51,7 @@
"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 9b3b8c35..85059838 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -37,7 +37,7 @@
"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 fc2c085..19c9136 100644
--- a/services/tests/RemoteProvisioningServiceTests/Android.bp
+++ b/services/tests/RemoteProvisioningServiceTests/Android.bp
@@ -30,8 +30,8 @@
"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 e724e804..9dacfea 100644
--- a/services/tests/apexsystemservices/Android.bp
+++ b/services/tests/apexsystemservices/Android.bp
@@ -34,7 +34,7 @@
"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 6ef150c..c37d21a 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 @@
}
@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 @@
+ "</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 @@
+ "</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 c63fac9..ee187ba 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 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 @@
}
@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 7c237ac..086e84b 100644
--- a/services/tests/inprocesstests/Android.bp
+++ b/services/tests/inprocesstests/Android.bp
@@ -14,7 +14,7 @@
"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 101498a..063af57 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -46,6 +46,7 @@
"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 @@
"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 f1dc1fa..1eb9888 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -22,6 +22,7 @@
srcs: [
":lib_cachedAppOptimizer_native",
":lib_gameManagerService_native",
+ ":lib_oomConnection_native",
"onload.cpp",
],
@@ -42,6 +43,7 @@
"libgui",
"libhidlbase",
"liblog",
+ "libmemevents",
"libmeminfo",
"libnativehelper",
"libprocessgroup",
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index 23ccb22..fb91051 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 @@
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 5b1508b..be29163 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 @@
}
@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 @@
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 64cc397..9ba4f5b 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 @@
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 2bc66ac..40b5458 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 @@
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 1c0989c..9391d5b 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 @@
}
}
+ // 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 d56229c..e15942b 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 @@
};
}
}
+
+ // 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 0abf46b..596a3f3 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 @@
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 434d200..dfb8fda 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 @@
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 fd1b068..7ec27be 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 @@
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 2b56ea8..bded9b4 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.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.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.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 @@
@Mock
private PackageManager mPackageManager;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private Constants mConstants;
private FlexibilityController mFlexibilityController;
@@ -898,7 +904,8 @@
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,6 +917,8 @@
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)))
@@ -918,6 +927,21 @@
// 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));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownUp, 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 6304270..305569e 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 @@
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 @@
.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 8ab4507..18a4f00 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -16,7 +16,7 @@
"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 20d8a5d..2ece8c7 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 @@
android_test {
name: "FrameworksServicesTests",
+ defaults: [
+ "FrameworksServicesTests-jni-defaults",
+ ],
// Include all test java files.
srcs: [
@@ -51,7 +61,7 @@
"mockito-target-minus-junit4",
"platform-test-annotations",
"ShortcutManagerTestUtils",
- "truth-prebuilt",
+ "truth",
"testables",
"androidx.test.uiautomator_uiautomator",
"platformprotosnano",
@@ -62,7 +72,7 @@
// 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 0000000..174beb8
--- /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 0000000..f160b3d
--- /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 acdfee9..c0051c6 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 @@
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 9fdbdda..70527ce 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 @@
TestHandler mTestHandler;
TestInjector(int skip, boolean immediate) {
+ super(mHandler);
mTracker = new TestTracker(skip);
mImmediate = immediate;
}
@@ -249,9 +250,16 @@
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 @@
// 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/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 2dacda0..d1f4961 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -31,6 +31,7 @@
"androidx.test.rules",
"hamcrest-library",
"mockito-target-inline-minus-junit4",
+ "platform-compat-test-rules",
"platform-test-annotations",
"platformprotosnano",
"statsdprotolite",
@@ -38,7 +39,7 @@
"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 312057ee..348d1bf 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.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 @@
@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();
- assertThat(timeUntilRemoval).isEqualTo(23);
+ 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(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 d758e71..3499a12 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 @@
@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 7a55143..c05f814 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 @@
}
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 9543a2d..91129a1 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.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.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.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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 a91bd2b..0003555 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.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.VibratorInfo;
import android.util.AtomicFile;
import android.util.SparseArray;
+import android.view.flags.FeatureFlags;
import androidx.test.InstrumentationRegistry;
@@ -59,23 +67,25 @@
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 @@
@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 @@
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 @@
+ "</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 @@
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 @@
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 @@
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 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 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 0eec9cd..40e0e84 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.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.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.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 @@
private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
@Mock
private AudioManager mAudioManagerMock;
+ @Mock
+ private FeatureFlags mViewFeatureFlags;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -321,7 +326,8 @@
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
Resources resources, VibratorInfo vibratorInfo) {
return new HapticFeedbackVibrationProvider(
- resources, vibratorInfo, mHapticFeedbackVibrationMap);
+ resources, vibratorInfo, mHapticFeedbackVibrationMap,
+ mViewFeatureFlags);
}
});
return mService;
@@ -649,6 +655,42 @@
}
@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 @@
}
@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 @@
}
@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 @@
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 @@
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 @@
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 @@
if (vib != null) {
vib.waitForEnd();
}
+
+ return vib;
}
private void vibrate(VibratorManagerService service, VibrationEffect effect,
@@ -2368,7 +2449,15 @@
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 e704ebf..744cb63 100644
--- a/services/tests/voiceinteractiontests/Android.bp
+++ b/services/tests/voiceinteractiontests/Android.bp
@@ -43,7 +43,7 @@
"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 c2812a1..af39b2f 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -57,12 +57,14 @@
"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 42e3383..762e23c 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 0a7bb00..71098aa 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.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 @@
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 @@
mPhoneWindowManager.overrideEnableBugReportTrigger(true);
mPhoneWindowManager.overrideStatusBarManagerInternal();
mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.overrideSendBroadcast();
mPhoneWindowManager.overrideUserSetupComplete();
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideTogglePanel();
@@ -330,4 +342,15 @@
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 ef28ffa..2244dbe 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 @@
mPhoneWindowManager.mDoubleTapOnHomeBehavior = behavior;
}
+ void overrideShortPressOnSettingsBehavior(int behavior) {
+ mPhoneWindowManager.mShortPressOnSettingsBehavior = behavior;
+ }
+
void overrideCanStartDreaming(boolean canDream) {
doReturn(canDream).when(mDreamManagerInternal).canStartDreaming(anyBoolean());
}
@@ -484,6 +488,10 @@
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 3bc6450..c241033 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 @@
// 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 c1d5147..8119fd4 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 @@
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 91256ee..4a33594 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 @@
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 @@
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 @@
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 @@
// 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 241f7d8..474720f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1442,7 +1442,7 @@
// normally.
mWm.mSyncEngine.abort(openTransition.getSyncId());
- verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2), eq(false));
+ verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2));
controller.finishTransition(openTransition);
@@ -1482,7 +1482,7 @@
// Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be
// called until finish).
- verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1), eq(false));
+ verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1));
enteringAnimReports.clear();
doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(),
@@ -1515,7 +1515,7 @@
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 0000000..ac49839
--- /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/UsageStatsHandlerThread.java b/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
deleted file mode 100644
index 6801c94..0000000
--- a/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.usage;
-
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-import android.os.Trace;
-
-import java.util.concurrent.Executor;
-
-/**
- * Shared singleton default priority thread for usage stats message handling.
- *
- * @see com.android.internal.os.BackgroundThread
- */
-public final class UsageStatsHandlerThread extends HandlerThread {
- private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
- private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
- private static UsageStatsHandlerThread sInstance;
- private static Handler sHandler;
- private static Executor sHandlerExecutor;
-
- private UsageStatsHandlerThread() {
- super("usagestats.default", Process.THREAD_PRIORITY_DEFAULT);
- }
-
- private static void ensureThreadLocked() {
- if (sInstance == null) {
- sInstance = new UsageStatsHandlerThread();
- sInstance.start();
- final Looper looper = sInstance.getLooper();
- looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
- looper.setSlowLogThresholdMs(
- SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
- sHandler = new Handler(sInstance.getLooper());
- sHandlerExecutor = new HandlerExecutor(sHandler);
- }
- }
-
- /** Returns the UsageStatsHandlerThread singleton */
- public static UsageStatsHandlerThread get() {
- synchronized (UsageStatsHandlerThread.class) {
- ensureThreadLocked();
- return sInstance;
- }
- }
-
- /** Returns the singleton handler for UsageStatsHandlerThread */
- public static Handler getHandler() {
- synchronized (UsageStatsHandlerThread.class) {
- ensureThreadLocked();
- return sHandler;
- }
- }
-
- /** Returns the singleton handler executor for UsageStatsHandlerThread */
- public static Executor getExecutor() {
- synchronized (UsageStatsHandlerThread.class) {
- ensureThreadLocked();
- return sHandlerExecutor;
- }
- }
-}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 18c960e..f3bf026 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -332,8 +332,7 @@
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mPackageManager = getContext().getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-
- mHandler = new H(UsageStatsHandlerThread.get().getLooper());
+ mHandler = new H(BackgroundThread.get().getLooper());
mIoHandler = new Handler(IoThread.get().getLooper(), mIoHandlerCallback);
mAppStandby = mInjector.getAppStandbyController(getContext());
@@ -495,9 +494,12 @@
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();
@@ -518,8 +520,7 @@
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.
@@ -916,6 +917,7 @@
}
}
+ @GuardedBy({"mLock", "mReportedEvents"})
private void persistPendingEventsLocked(int userId) {
final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
if (pendingEvents == null || pendingEvents.isEmpty()) {
@@ -1019,7 +1021,7 @@
+ 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<>();
@@ -1943,16 +1945,19 @@
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();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a5e0638..98bbb40 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -519,8 +519,6 @@
/**
* 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";
@@ -9541,9 +9539,9 @@
* 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";
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index de2e952..6ebf3be 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -259,7 +259,7 @@
/**
* Whether this subscription is used for communicating with non-terrestrial networks.
*/
- private final boolean mIsNtn;
+ private final boolean mIsOnlyNonTerrestrialNetwork;
/**
* @hide
@@ -385,7 +385,7 @@
this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
this.mPortIndex = portIndex;
this.mUsageSetting = usageSetting;
- this.mIsNtn = false;
+ this.mIsOnlyNonTerrestrialNetwork = false;
}
/**
@@ -424,7 +424,7 @@
this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
this.mPortIndex = builder.mPortIndex;
this.mUsageSetting = builder.mUsageSetting;
- this.mIsNtn = builder.mIsNtn;
+ this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork;
}
/**
@@ -878,8 +878,8 @@
* otherwise.
*/
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
- public boolean isNtn() {
- return mIsNtn;
+ public boolean isOnlyNonTerrestrialNetwork() {
+ return mIsOnlyNonTerrestrialNetwork;
}
@NonNull
@@ -918,7 +918,7 @@
UiccAccessRule.CREATOR))
.setUiccApplicationsEnabled(source.readBoolean())
.setUsageSetting(source.readInt())
- .setNtn(source.readBoolean())
+ .setOnlyNonTerrestrialNetwork(source.readBoolean())
.build();
}
@@ -960,7 +960,7 @@
dest.writeTypedArray(mCarrierConfigAccessRules, flags);
dest.writeBoolean(mAreUiccApplicationsEnabled);
dest.writeInt(mUsageSetting);
- dest.writeBoolean(mIsNtn);
+ dest.writeBoolean(mIsOnlyNonTerrestrialNetwork);
}
@Override
@@ -1023,7 +1023,7 @@
+ " mType=" + SubscriptionManager.subscriptionTypeToString(mType)
+ " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
- + " ntn=" + mIsNtn
+ + " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
+ "]";
}
@@ -1049,7 +1049,7 @@
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 @@
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 @@
/**
* {@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 @@
mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
mPortIndex = info.mPortIndex;
mUsageSetting = info.mUsageSetting;
- mIsNtn = info.mIsNtn;
+ mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork;
}
/**
@@ -1692,12 +1692,13 @@
/**
* 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/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp
index 5233a5b..c2a7015 100644
--- a/tests/BatteryStatsPerfTest/Android.bp
+++ b/tests/BatteryStatsPerfTest/Android.bp
@@ -27,7 +27,7 @@
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 615990f..38cb9869 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -30,7 +30,7 @@
"compatibility-host-util",
],
static_libs: [
- "truth-prebuilt",
+ "truth",
],
data: [
":BinaryTransparencyTestApp",
diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp
index c4faf7f..1fb73e2 100644
--- a/tests/BlobStoreTestUtils/Android.bp
+++ b/tests/BlobStoreTestUtils/Android.bp
@@ -22,12 +22,12 @@
}
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 ca30267..5d49120 100644
--- a/tests/ChoreographerTests/Android.bp
+++ b/tests/ChoreographerTests/Android.bp
@@ -34,7 +34,7 @@
"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 6809521..96e4a9e 100644
--- a/tests/CtsSurfaceControlTestsStaging/Android.bp
+++ b/tests/CtsSurfaceControlTestsStaging/Android.bp
@@ -37,7 +37,7 @@
"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 448d46f..3f2c808 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.bp
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
@@ -47,7 +47,7 @@
static_libs: [
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
],
compile_multilib: "both",
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index a2ae56e..82aa85d 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -236,7 +236,7 @@
static_libs: [
"flickerlib",
"flickerlib-helpers",
- "truth-prebuilt",
+ "truth",
"app-helpers-core",
],
}
@@ -255,7 +255,7 @@
"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 c30786f..da51eff 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 @@
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 cdb1d42..12eaad1 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 @@
"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 @@
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 @@
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 39944be..7d59d77 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 365e00e..cf2d5d6 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,6 +9,10 @@
android_test {
name: "InputTests",
+ defaults: [
+ // For ExtendedMockito dependencies.
+ "modules-utils-testable-device-config-defaults",
+ ],
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -35,7 +39,7 @@
"services.core.unboosted",
"testables",
"testng",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.mock",
diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
deleted file mode 100644
index 1b98887..0000000
--- a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.input;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
-import android.view.ViewConfiguration;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Build/Install/Run:
- * atest FocusEventDebugViewTest
- */
-@RunWith(AndroidJUnit4.class)
-public class FocusEventDebugViewTest {
-
- private FocusEventDebugView mFocusEventDebugView;
- private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView;
- private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView;
- private float mScaledVerticalScrollFactor;
-
- @Before
- public void setUp() throws Exception {
- Context context = InstrumentationRegistry.getContext();
- mScaledVerticalScrollFactor =
- ViewConfiguration.get(context).getScaledVerticalScrollFactor();
- InputManagerService mockService = mock(InputManagerService.class);
- when(mockService.monitorInput(anyString(), anyInt()))
- .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
-
- mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context);
- mRotaryInputGraphView = new FocusEventDebugView.RotaryInputGraphView(context);
- mFocusEventDebugView = new FocusEventDebugView(context, mockService,
- () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
- }
-
- @Test
- public void startsRotaryInputValueViewWithDefaultValue() {
- assertEquals("+0.0", mRotaryInputValueView.getText());
- }
-
- @Test
- public void startsRotaryInputGraphViewWithDefaultFrameCenter() {
- assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01);
- }
-
- @Test
- public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() {
- mFocusEventDebugView.handleUpdateShowRotaryInput(true);
-
- mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f));
-
- assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor),
- mRotaryInputValueView.getText());
- }
-
- @Test
- public void handleRotaryInput_translatesRotaryInputGraphViewWithHighScrollValue() {
- mFocusEventDebugView.handleUpdateShowRotaryInput(true);
-
- mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f));
-
- assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0);
- }
-
- @Test
- public void updateActivityStatus_setsAndRemovesColorFilter() {
- // It should not be active initially.
- assertNull(mRotaryInputValueView.getBackground().getColorFilter());
-
- mRotaryInputValueView.updateActivityStatus(true);
- // It should be active after rotary input.
- assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
-
- mRotaryInputValueView.updateActivityStatus(false);
- // It should not be active after waiting for mUpdateActivityStatusCallback.
- assertNull(mRotaryInputValueView.getBackground().getColorFilter());
- }
-
- private MotionEvent createRotaryMotionEvent(float scrollAxisValue) {
- PointerCoords pointerCoords = new PointerCoords();
- pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
- PointerProperties pointerProperties = new PointerProperties();
-
- return MotionEvent.obtain(
- /* downTime */ 0,
- /* eventTime */ 0,
- /* action */ MotionEvent.ACTION_SCROLL,
- /* pointerCount */ 1,
- /* pointerProperties */ new PointerProperties[] {pointerProperties},
- /* pointerCoords */ new PointerCoords[] {pointerCoords},
- /* metaState */ 0,
- /* buttonState */ 0,
- /* xPrecision */ 0,
- /* yPrecision */ 0,
- /* deviceId */ 0,
- /* edgeFlags */ 0,
- /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
- /* flags */ 0
- );
- }
-}
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index b6477510..fa86e9c 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.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.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 @@
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 @@
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 @@
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 @@
}
}
+ @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 @@
}
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/debug/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
new file mode 100644
index 0000000..ae7fb3b
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.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;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+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;
+
+/**
+ * Build/Install/Run:
+ * atest FocusEventDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FocusEventDebugViewTest {
+
+ private FocusEventDebugView mFocusEventDebugView;
+ private RotaryInputValueView mRotaryInputValueView;
+ private RotaryInputGraphView mRotaryInputGraphView;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ InputManagerService mockService = mock(InputManagerService.class);
+ when(mockService.monitorInput(anyString(), anyInt()))
+ .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
+
+ mRotaryInputValueView = spy(new RotaryInputValueView(context));
+ mRotaryInputGraphView = spy(new RotaryInputGraphView(context));
+ mFocusEventDebugView = new FocusEventDebugView(context, mockService,
+ () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
+ }
+
+ @Test
+ public void handleRotaryInput_sendsMotionEventWhenEnabled() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(true);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L));
+
+ verify(mRotaryInputGraphView).addValue(0.5f, 10L);
+ verify(mRotaryInputValueView).updateValue(0.5f);
+ }
+
+ @Test
+ public void handleRotaryInput_doesNotSendMotionEventWhenDisabled() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(false);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L));
+
+ verify(mRotaryInputGraphView, never()).addValue(anyFloat(), anyLong());
+ verify(mRotaryInputValueView, never()).updateValue(anyFloat());
+ }
+
+ 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 */ eventTime,
+ /* action */ MotionEvent.ACTION_SCROLL,
+ /* pointerCount */ 1,
+ /* pointerProperties */ new PointerProperties[] {pointerProperties},
+ /* pointerCoords */ new PointerCoords[] {pointerCoords},
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 0,
+ /* yPrecision */ 0,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
+ /* flags */ 0
+ );
+ }
+}
diff --git a/tests/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 0000000..af6ece4
--- /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 0000000..e5e3852
--- /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 84845c6..58ceb3f 100644
--- a/tests/InputMethodStressTest/Android.bp
+++ b/tests/InputMethodStressTest/Android.bp
@@ -26,7 +26,7 @@
"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 eee486f..15aaa46 100644
--- a/tests/InputScreenshotTest/Android.bp
+++ b/tests/InputScreenshotTest/Android.bp
@@ -29,7 +29,7 @@
"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 @@
"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 ef45864..ddec8fa 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -19,7 +19,7 @@
"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 4e0b0a8..909ca59 100644
--- a/tests/LocalizationTest/Android.bp
+++ b/tests/LocalizationTest/Android.bp
@@ -34,7 +34,7 @@
"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 254770d..fcacab3 100644
--- a/tests/MidiTests/Android.bp
+++ b/tests/MidiTests/Android.bp
@@ -31,7 +31,7 @@
"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 1e1dc84..e0e6c4c 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -32,7 +32,7 @@
"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 f0f9c4b..fd992cf 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -38,7 +38,7 @@
"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 5f91f9d0..f6a41c2 100644
--- a/tests/PlatformCompatGating/test-rules/Android.bp
+++ b/tests/PlatformCompatGating/test-rules/Android.bp
@@ -29,7 +29,7 @@
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 38313f8..055d625 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -45,7 +45,7 @@
"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 bf12f42..d2ade34 100644
--- a/tests/TaskOrganizerTest/Android.bp
+++ b/tests/TaskOrganizerTest/Android.bp
@@ -43,6 +43,6 @@
"kotlinx-coroutines-android",
"flickerlib",
"flickerlib-trace_processor_shell",
- "truth-prebuilt",
+ "truth",
],
}
diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
index 81ec265..b968e5d 100644
--- a/tests/TelephonyCommonTests/Android.bp
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -32,11 +32,11 @@
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 c216bce..4e75a1d 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -28,7 +28,7 @@
"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 9bfcc18..ddb3649 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -30,7 +30,7 @@
"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 97fbf5b..c02d8e9 100644
--- a/tests/UsbManagerTests/Android.bp
+++ b/tests/UsbManagerTests/Android.bp
@@ -31,7 +31,7 @@
"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 994484c..4e5a70fe 100644
--- a/tests/UsbManagerTests/lib/Android.bp
+++ b/tests/UsbManagerTests/lib/Android.bp
@@ -34,7 +34,7 @@
"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 c60c519..92c2711 100644
--- a/tests/UsbTests/Android.bp
+++ b/tests/UsbTests/Android.bp
@@ -34,7 +34,7 @@
"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 01d34e4..39037f2 100644
--- a/tests/componentalias/Android.bp
+++ b/tests/componentalias/Android.bp
@@ -25,7 +25,7 @@
"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 7323b0f..977b276 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -220,6 +220,7 @@
name: "aapt2-protos",
tools: [":soong_zip"],
srcs: [
+ "ApkInfo.proto",
"Configuration.proto",
"ResourcesInternal.proto",
"ResourceMetadata.proto",
diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
index e7fb2de..b71e5c4 100644
--- a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
+++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
@@ -24,7 +24,7 @@
],
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 05d6a43..f9dc305 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -104,7 +104,7 @@
],
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 a7d69039..ecc283b 100644
--- a/tools/processors/immutability/Android.bp
+++ b/tools/processors/immutability/Android.bp
@@ -50,7 +50,7 @@
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 7059c52..9c755b7 100644
--- a/tools/processors/intdef_mappings/Android.bp
+++ b/tools/processors/intdef_mappings/Android.bp
@@ -38,7 +38,7 @@
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 7a29969..5a0f742 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -40,7 +40,7 @@
"frameworks-base-testutils",
"guava",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
libs: [