diff options
718 files changed, 12676 insertions, 4063 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index c30164c065af..003b7f87fa23 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -42,6 +42,7 @@ aconfig_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 { @@ -416,3 +417,19 @@ java_aconfig_library { aconfig_declarations: "android.service.voice.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// Autofill +aconfig_declarations { + name: "android.service.autofill.flags-aconfig", + package: "android.service.autofill", + srcs: [ + "services/autofill/bugfixes.aconfig", + "services/autofill/features.aconfig" + ], +} + +java_aconfig_library { + name: "android.service.autofill.flags-aconfig-java", + aconfig_declarations: "android.service.autofill.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp index a4a0b4b29200..887f7fe3a0e2 100644 --- a/apex/jobscheduler/service/Android.bp +++ b/apex/jobscheduler/service/Android.bp @@ -13,6 +13,10 @@ java_library { name: "service-jobscheduler", installable: true, + defaults: [ + "service-jobscheduler-aconfig-libraries", + ], + srcs: [ "java/**/*.java", ":framework-jobscheduler-shared-srcs", diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp new file mode 100644 index 000000000000..24ecd3d73814 --- /dev/null +++ b/apex/jobscheduler/service/aconfig/Android.bp @@ -0,0 +1,29 @@ +// JobScheduler +aconfig_declarations { + name: "service-job.flags-aconfig", + package: "com.android.server.job", + srcs: [ + "job.aconfig", + ], +} + +java_aconfig_library { + name: "service-jobscheduler-job.flags-aconfig-java", + aconfig_declarations: "service-job.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], + visibility: ["//frameworks/base:__subpackages__"], +} + +service_jobscheduler_aconfig_srcjars = [ + ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}", +] + +// Aconfig declarations and libraries for the core framework +java_defaults { + name: "service-jobscheduler-aconfig-libraries", + // Add java_aconfig_libraries to here to add them to the core framework + srcs: service_jobscheduler_aconfig_srcjars, + // Add aconfig-annotations-lib as a dependency for the optimization + libs: ["aconfig-annotations-lib"], + visibility: ["//frameworks/base:__subpackages__"], +} diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig new file mode 100644 index 000000000000..4e3cb7d83451 --- /dev/null +++ b/apex/jobscheduler/service/aconfig/job.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.job" + +flag { + name: "relax_prefetch_connectivity_constraint_only_on_charger" + namespace: "backstagepower" + description: "Only relax a prefetch job's connectivity constraint when the device is charging" + bug: "299329948" +}
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index f252a0ba48cf..158d914575c6 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -1030,6 +1030,12 @@ public class DeviceIdleController extends SystemService "light_idle_to_initial_flex"; private static final String KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX = "light_max_idle_to_flex"; private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor"; + private static final String KEY_LIGHT_IDLE_INCREASE_LINEARLY = + "light_idle_increase_linearly"; + private static final String KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = + "light_idle_linear_increase_factor_ms"; + private static final String KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = + "light_idle_flex_linear_increase_factor_ms"; private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to"; private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = "light_idle_maintenance_min_budget"; @@ -1079,6 +1085,10 @@ public class DeviceIdleController extends SystemService private long mDefaultLightIdleTimeoutMaxFlex = !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L; private float mDefaultLightIdleFactor = 2f; + private boolean mDefaultLightIdleIncreaseLinearly; + private long mDefaultLightIdleLinearIncreaseFactorMs = mDefaultLightIdleTimeout; + private long mDefaultLightIdleFlexLinearIncreaseFactorMs = + mDefaultLightIdleTimeoutInitialFlex; private long mDefaultLightMaxIdleTimeout = !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L; private long mDefaultLightIdleMaintenanceMinBudget = @@ -1174,6 +1184,37 @@ public class DeviceIdleController extends SystemService public float LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor; /** + * Whether to increase the light idle mode time linearly or exponentially. + * If true, will increase linearly + * (i.e. {@link #LIGHT_IDLE_TIMEOUT} + x * {@link #LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS}). + * If false, will increase by exponentially + * (i.e. {@link #LIGHT_IDLE_TIMEOUT} * ({@link #LIGHT_IDLE_FACTOR} ^ x)). + * This will also impact how the light idle flex value + * ({@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX}) is increased (using + * {@link #LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS} for the linear increase).. + * + * @see #KEY_LIGHT_IDLE_INCREASE_LINEARLY + */ + public boolean LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly; + + /** + * Amount of time to increase the light idle time by, if increasing it linearly. + * + * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS + * @see #LIGHT_IDLE_INCREASE_LINEARLY + */ + public long LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs; + + /** + * Amount of time to increase the light idle flex time by, if increasing it linearly. + * + * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS + * @see #LIGHT_IDLE_INCREASE_LINEARLY + */ + public long LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = + mDefaultLightIdleFlexLinearIncreaseFactorMs; + + /** * This is the maximum time we will stay in light idle mode. * * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT @@ -1409,6 +1450,16 @@ public class DeviceIdleController extends SystemService mDefaultLightIdleTimeoutMaxFlex); mDefaultLightIdleFactor = res.getFloat( com.android.internal.R.integer.device_idle_light_idle_factor); + mDefaultLightIdleIncreaseLinearly = res.getBoolean( + com.android.internal.R.bool.device_idle_light_idle_increase_linearly); + mDefaultLightIdleLinearIncreaseFactorMs = getTimeout(res.getInteger( + com.android.internal.R.integer + .device_idle_light_idle_linear_increase_factor_ms), + mDefaultLightIdleLinearIncreaseFactorMs); + mDefaultLightIdleFlexLinearIncreaseFactorMs = getTimeout(res.getInteger( + com.android.internal.R.integer + .device_idle_light_idle_flex_linear_increase_factor_ms), + mDefaultLightIdleFlexLinearIncreaseFactorMs); mDefaultLightMaxIdleTimeout = getTimeout( res.getInteger(com.android.internal.R.integer.device_idle_light_max_idle_to_ms), mDefaultLightMaxIdleTimeout); @@ -1487,6 +1538,9 @@ public class DeviceIdleController extends SystemService LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mDefaultLightIdleTimeoutInitialFlex; LIGHT_IDLE_TIMEOUT_MAX_FLEX = mDefaultLightIdleTimeoutMaxFlex; LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor; + LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly; + LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs; + LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleFlexLinearIncreaseFactorMs; LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout; LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget; LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget; @@ -1556,6 +1610,21 @@ public class DeviceIdleController extends SystemService LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat( KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor)); break; + case KEY_LIGHT_IDLE_INCREASE_LINEARLY: + LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean( + KEY_LIGHT_IDLE_INCREASE_LINEARLY, + mDefaultLightIdleIncreaseLinearly); + break; + case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS: + LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong( + KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS, + mDefaultLightIdleLinearIncreaseFactorMs); + break; + case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS: + LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong( + KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS, + mDefaultLightIdleFlexLinearIncreaseFactorMs); + break; case KEY_LIGHT_MAX_IDLE_TIMEOUT: LIGHT_MAX_IDLE_TIMEOUT = properties.getLong( KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout); @@ -1716,6 +1785,20 @@ public class DeviceIdleController extends SystemService pw.print(LIGHT_IDLE_FACTOR); pw.println(); + pw.print(" "); pw.print(KEY_LIGHT_IDLE_INCREASE_LINEARLY); pw.print("="); + pw.print(LIGHT_IDLE_INCREASE_LINEARLY); + pw.println(); + + pw.print(" "); pw.print(KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS); + pw.print("="); + pw.print(LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS); + pw.println(); + + pw.print(" "); pw.print(KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS); + pw.print("="); + pw.print(LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS); + pw.println(); + pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT, pw); pw.println(); @@ -3694,10 +3777,18 @@ public class DeviceIdleController extends SystemService } mMaintenanceStartTime = 0; scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex, true); - mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT, - (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR)); - mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX, - (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR)); + if (!mConstants.LIGHT_IDLE_INCREASE_LINEARLY) { + mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT, + (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR)); + mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX, + (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR)); + } else { + mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT, + mNextLightIdleDelay + mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS); + mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX, + mNextLightIdleDelayFlex + + mConstants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS); + } moveToLightStateLocked(LIGHT_STATE_IDLE, reason); addEvent(EVENT_LIGHT_IDLE, null); mGoingIdleWakeLock.acquire(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 43d2ae9e077b..f47766ed0393 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -1335,13 +1335,13 @@ public final class JobServiceContext implements ServiceConnection { private void handleOpTimeoutLocked() { switch (mVerb) { case VERB_BINDING: - onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true, + // The system may have been too busy. Don't drop the job or trigger an ANR. + onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true, /* texCounterMetricId */ "job_scheduler.value_cntr_w_uid_slow_app_response_binding", /* debugReason */ "timed out while binding", /* anrMessage */ "Timed out while trying to bind", - CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES, - mRunningJob.getUid())); + /* triggerAnr */ false); break; case VERB_STARTING: // Client unresponsive - wedged or failed to respond in time. We don't really diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 6d938debde10..45f15db93a0d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -22,6 +22,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_MET import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER; +import static com.android.server.job.Flags.relaxPrefetchConnectivityConstraintOnlyOnCharger; import android.annotation.NonNull; import android.annotation.Nullable; @@ -929,6 +931,11 @@ public final class ConnectivityController extends RestrictingController implemen // Need to at least know the estimated download bytes for a prefetch job. return false; } + if (relaxPrefetchConnectivityConstraintOnlyOnCharger()) { + if (!mService.isBatteryCharging()) { + return false; + } + } // See if we match after relaxing any unmetered request final NetworkCapabilities.Builder builder = @@ -1735,6 +1742,14 @@ public final class ConnectivityController extends RestrictingController implemen Predicate<JobStatus> predicate) { final long nowElapsed = sElapsedRealtimeClock.millis(); + pw.println("Aconfig flags:"); + pw.increaseIndent(); + pw.print(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER, + relaxPrefetchConnectivityConstraintOnlyOnCharger()); + pw.println(); + pw.decreaseIndent(); + pw.println(); + if (mRequestedWhitelistJobs.size() > 0) { pw.print("Requested standby exceptions:"); for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) { diff --git a/api/Android.bp b/api/Android.bp index 45e70719e6c0..222275f9a4e4 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -82,7 +82,6 @@ combined_apis { "framework-media", "framework-mediaprovider", "framework-ondevicepersonalization", - "framework-pdf", "framework-permission", "framework-permission-s", "framework-scheduling", diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline index 07d1f4b6dee3..796a0c6a4813 100644 --- a/api/javadoc-lint-baseline +++ b/api/javadoc-lint-baseline @@ -257,76 +257,6 @@ android/view/inputmethod/InputMethodManager.java:456: lint: Unresolved link/see 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] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_30" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_35" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_40" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_45" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_50" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_55" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_60" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_65" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_70" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_75" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_80" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_85" in android [101] -com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_90" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_30" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_35" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_40" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_45" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_50" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_55" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_60" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_65" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_70" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_75" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_80" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_85" in android [101] -com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_90" in android [101] -com/android/server/wm/CompatModePackages.java:135: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:135: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:135: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_90" in android [101] -com/android/server/wm/CompatModePackages.java:148: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:148: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:148: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_85" in android [101] -com/android/server/wm/CompatModePackages.java:161: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:161: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:161: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_80" in android [101] -com/android/server/wm/CompatModePackages.java:174: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:174: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:174: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_75" in android [101] -com/android/server/wm/CompatModePackages.java:187: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:187: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:187: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_70" in android [101] -com/android/server/wm/CompatModePackages.java:200: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:200: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:200: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_65" in android [101] -com/android/server/wm/CompatModePackages.java:213: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:213: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:213: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_60" in android [101] -com/android/server/wm/CompatModePackages.java:226: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:226: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:226: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_55" in android [101] -com/android/server/wm/CompatModePackages.java:239: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:239: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:239: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_50" in android [101] -com/android/server/wm/CompatModePackages.java:252: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:252: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:252: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_45" in android [101] -com/android/server/wm/CompatModePackages.java:265: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:265: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:265: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_40" in android [101] -com/android/server/wm/CompatModePackages.java:278: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:278: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:278: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_35" in android [101] -com/android/server/wm/CompatModePackages.java:291: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101] -com/android/server/wm/CompatModePackages.java:291: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101] -com/android/server/wm/CompatModePackages.java:291: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_30" in android [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] diff --git a/core/api/current.txt b/core/api/current.txt index 96a959572949..d037c31e5af7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -18539,8 +18539,10 @@ package android.hardware.biometrics { ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac); ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential); ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession); + ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement); method public javax.crypto.Cipher getCipher(); method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential(); + method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement(); method public javax.crypto.Mac getMac(); method @Nullable public android.security.identity.PresentationSession getPresentationSession(); method public java.security.Signature getSignature(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 358c8e72064b..a99eeb0c36d8 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3946,7 +3946,7 @@ package android.content.pm { field public static final int DELETE_FAILED_OWNER_BLOCKED = -4; // 0xfffffffc field public static final int DELETE_KEEP_DATA = 1; // 0x1 field public static final int DELETE_SUCCEEDED = 1; // 0x1 - field public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; + field @FlaggedApi("android.permission.flags.device_aware_permission_apis") public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; field public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index db751a432dd7..aa48451fd24c 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1903,7 +1903,7 @@ package android.media { method public android.media.PlaybackParams setAudioStretchMode(int); } - public final class RingtoneSelection { + @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection { method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int); method public int getSoundSource(); method @Nullable public android.net.Uri getSoundUri(); @@ -1915,14 +1915,16 @@ package android.media { field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3 field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1 field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2 - field public static final int SOUND_SOURCE_DEFAULT = 0; // 0x0 field public static final int SOUND_SOURCE_OFF = 1; // 0x1 + field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3 + field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0 field public static final int SOUND_SOURCE_URI = 2; // 0x2 - field public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3; // 0x3 + field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4 field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa - field public static final int VIBRATION_SOURCE_DEFAULT = 0; // 0x0 field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1 + field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3 + field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0 field public static final int VIBRATION_SOURCE_URI = 2; // 0x2 } @@ -2610,17 +2612,17 @@ package android.os.vibrator { package android.os.vibrator.persistence { - public class ParsedVibration { + @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public class ParsedVibration { method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects(); method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); } - public final class VibrationXmlParser { + @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlParser { method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException; method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException; } - public final class VibrationXmlSerializer { + @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlSerializer { method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException; } diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 107be8be42e2..93e39d5f41f7 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -191,8 +191,14 @@ UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_DEFAULT: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF +UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT +UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI +UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL: @@ -203,6 +209,10 @@ UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_HAPTIC_GENERATOR: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF +UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT +UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED: + New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI: New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int): diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index ca10d144d4cd..1aa8ebedea3b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1486,13 +1486,13 @@ public class AppOpsManager { AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; /** - * Allows the assistant app to get the training data from the PCC sandbox to improve the + * Allows the assistant app to get the training data from the trusted process to improve the * hotword training model. * * @hide */ - public static final int OP_RECEIVE_SANDBOX_TRAINING_DATA = - AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRAINING_DATA; + public static final int OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA = + AppProtoEnums.APP_OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA; /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -1640,7 +1640,7 @@ public class AppOpsManager { OPSTR_CAMERA_SANDBOXED, OPSTR_RECORD_AUDIO_SANDBOXED, OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO, - OPSTR_RECEIVE_SANDBOX_TRAINING_DATA + OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA }) public @interface AppOpString {} @@ -2261,13 +2261,13 @@ public class AppOpsManager { "android:receive_sandbox_trigger_audio"; /** - * Allows the assistant app to get the training data from the PCC sandbox to improve + * Allows the assistant app to get the training data from the trusted process to improve * the hotword training model. * * @hide */ - public static final String OPSTR_RECEIVE_SANDBOX_TRAINING_DATA = - "android:receive_sandbox_training_data"; + public static final String OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA = + "android:receive_trusted_process_training_data"; /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; @@ -2811,9 +2811,9 @@ public class AppOpsManager { 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 7b3d01712867..d15c79f41d3f 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -468,6 +468,15 @@ public final class ApplicationExitInfo implements Parcelable { */ public static final int SUBREASON_EXCESSIVE_BINDER_OBJECTS = 29; + /** + * The process was killed by the [kernel] Out-of-memory (OOM) killer; this + * would be set only when the reason is {@link #REASON_LOW_MEMORY}. + * + * For internal use only. + * @hide + */ + public static final int SUBREASON_OOM_KILL = 30; + // If there is any OEM code which involves additional app kill reasons, it should // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000. @@ -644,6 +653,7 @@ public final class ApplicationExitInfo implements Parcelable { SUBREASON_PACKAGE_UPDATE, SUBREASON_UNDELIVERED_BROADCAST, SUBREASON_EXCESSIVE_BINDER_OBJECTS, + SUBREASON_OOM_KILL, }) @Retention(RetentionPolicy.SOURCE) public @interface SubReason {} @@ -1371,6 +1381,8 @@ public final class ApplicationExitInfo implements Parcelable { return "UNDELIVERED BROADCAST"; case SUBREASON_EXCESSIVE_BINDER_OBJECTS: return "EXCESSIVE BINDER OBJECTS"; + case SUBREASON_OOM_KILL: + return "OOM KILL"; default: return "UNKNOWN"; } diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 2fb428b3e0b4..a84845a15a79 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -240,6 +240,12 @@ public final class CompanionDeviceManager { public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES /** + * The length limit of Association tag. + * @hide + */ + private static final int ASSOCIATION_TAG_LENGTH_LIMIT = 100; + + /** * Callback for applications to receive updates about and the outcome of * {@link AssociationRequest} issued via {@code associate()} call. * @@ -1409,7 +1415,7 @@ public final class CompanionDeviceManager { /** * Sets the {@link AssociationInfo#getTag() tag} for this association. * - * <p>The length of the tag must be at most 20 characters. + * <p>The length of the tag must be at most 100 characters to save disk space. * * <p>This allows to store useful information about the associated devices. * @@ -1421,8 +1427,8 @@ public final class CompanionDeviceManager { public void setAssociationTag(int associationId, @NonNull String tag) { Objects.requireNonNull(tag, "tag cannot be null"); - if (tag.length() > 20) { - throw new IllegalArgumentException("Length of the tag must be at most 20 characters"); + if (tag.length() > ASSOCIATION_TAG_LENGTH_LIMIT) { + throw new IllegalArgumentException("Length of the tag must be at most 100 characters"); } try { diff --git a/core/java/android/companion/virtualnative/OWNERS b/core/java/android/companion/virtualnative/OWNERS new file mode 100644 index 000000000000..29681045ac4a --- /dev/null +++ b/core/java/android/companion/virtualnative/OWNERS @@ -0,0 +1 @@ +include /services/companion/java/com/android/server/companion/virtual/OWNERS diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 3fc515d3d37d..8fbe50c32881 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4802,6 +4802,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index ea0f049cece3..3e12a1a66791 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -13,3 +13,10 @@ flag { description: "Bind wallpaper service on its own thread instead of system_server's main handler during a user switch." bug: "302100344" } + +flag { + name: "support_communal_profile" + namespace: "multiuser" + description: "Framework support for communal profile." + bug: "285426179" +} diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java index 231e4bc4ac75..1b130a9fb64d 100644 --- a/core/java/android/credentials/GetCandidateCredentialsResponse.java +++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java @@ -53,6 +53,15 @@ public final class GetCandidateCredentialsResponse implements Parcelable { mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList); } + /** + * Returns candidate provider data list. + * + * @hide + */ + public List<GetCredentialProviderData> getCandidateProviderDataList() { + return mCandidateProviderDataList; + } + protected GetCandidateCredentialsResponse(Parcel in) { List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>(); in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR); diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index af448f0c4917..490ff640885e 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -20,8 +20,10 @@ import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; +import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -53,6 +55,7 @@ import java.util.List; import java.util.concurrent.Executor; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; /** @@ -748,7 +751,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * A wrapper class for the cryptographic operations supported by BiometricPrompt. * * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, - * {@link IdentityCredential}, and {@link PresentationSession}. + * {@link IdentityCredential}, {@link PresentationSession} and {@link KeyAgreement}. * * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and * time-based. This is specified during key creation via the timeout parameter of the @@ -793,6 +796,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan super(session); } + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public CryptoObject(@NonNull KeyAgreement keyAgreement) { + super(keyAgreement); + } + /** * Get {@link Signature} object. * @return {@link Signature} object or null if this doesn't contain one. @@ -834,6 +842,15 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan public @Nullable PresentationSession getPresentationSession() { return super.getPresentationSession(); } + + /** + * Get {@link KeyAgreement} object. + * @return {@link KeyAgreement} object or null if this doesn't contain one. + */ + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public @Nullable KeyAgreement getKeyAgreement() { + return super.getKeyAgreement(); + } } /** diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java index 267ef3637ce7..151f819329c9 100644 --- a/core/java/android/hardware/biometrics/CryptoObject.java +++ b/core/java/android/hardware/biometrics/CryptoObject.java @@ -16,6 +16,9 @@ package android.hardware.biometrics; +import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.security.identity.IdentityCredential; import android.security.identity.PresentationSession; @@ -24,6 +27,7 @@ import android.security.keystore2.AndroidKeyStoreProvider; import java.security.Signature; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; /** @@ -62,6 +66,11 @@ public class CryptoObject { mCrypto = session; } + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public CryptoObject(@NonNull KeyAgreement keyAgreement) { + mCrypto = keyAgreement; + } + /** * Get {@link Signature} object. * @return {@link Signature} object or null if this doesn't contain one. @@ -105,6 +114,15 @@ public class CryptoObject { } /** + * Get {@link PresentationSession} object. + * @return {@link PresentationSession} object or null if this doesn't contain one. + */ + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public KeyAgreement getKeyAgreement() { + return mCrypto instanceof KeyAgreement ? (KeyAgreement) mCrypto : null; + } + + /** * @hide * @return the opId associated with this object or 0 if none */ diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index f59437795b9c..5bfda70f03de 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -24,12 +24,14 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE; +import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_HAS_ENROLLED_FINGERPRINTS; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_IS_HARDWARE_DETECTED; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -76,6 +78,7 @@ import java.util.List; import java.util.concurrent.Executor; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; /** @@ -312,6 +315,16 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public PresentationSession getPresentationSession() { return super.getPresentationSession(); } + + /** + * Get {@link KeyAgreement} object. + * @return {@link KeyAgreement} object or null if this doesn't contain one. + * @hide + */ + @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT) + public KeyAgreement getKeyAgreement() { + return super.getKeyAgreement(); + } } /** diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java index bbfed24f9dc1..883f157bbfe5 100644 --- a/core/java/android/hardware/input/KeyboardLayout.java +++ b/core/java/android/hardware/input/KeyboardLayout.java @@ -82,8 +82,8 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo DVORAK(4, LAYOUT_TYPE_DVORAK), COLEMAK(5, LAYOUT_TYPE_COLEMAK), WORKMAN(6, LAYOUT_TYPE_WORKMAN), - TURKISH_F(7, LAYOUT_TYPE_TURKISH_F), - TURKISH_Q(8, LAYOUT_TYPE_TURKISH_Q), + TURKISH_Q(7, LAYOUT_TYPE_TURKISH_Q), + TURKISH_F(8, LAYOUT_TYPE_TURKISH_F), EXTENDED(9, LAYOUT_TYPE_EXTENDED); private final int mValue; diff --git a/core/java/android/os/OomKillRecord.java b/core/java/android/os/OomKillRecord.java new file mode 100644 index 000000000000..151a65fdfaf5 --- /dev/null +++ b/core/java/android/os/OomKillRecord.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + + +/** + * Expected data to get back from the OOM event's file. + * Note that this should be equivalent to the struct <b>OomKill</b> inside + * <pre> + * system/memory/libmeminfo/libmemevents/include/memevents.h + * </pre> + * + * @hide + */ +public final class OomKillRecord { + private long mTimeStampInMillis; + private int mPid; + private int mUid; + private String mProcessName; + private short mOomScoreAdj; + + public OomKillRecord(long timeStampInMillis, int pid, int uid, + String processName, short oomScoreAdj) { + this.mTimeStampInMillis = timeStampInMillis; + this.mPid = pid; + this.mUid = uid; + this.mProcessName = processName; + this.mOomScoreAdj = oomScoreAdj; + } + + public long getTimestampMilli() { + return mTimeStampInMillis; + } + + public int getPid() { + return mPid; + } + + public int getUid() { + return mUid; + } + + public String getProcessName() { + return mProcessName; + } + + public short getOomScoreAdj() { + return mOomScoreAdj; + } +} diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index c01ef3d79bb9..88f62f327fdd 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -12,4 +12,18 @@ flag { name: "haptics_customization_enabled" description: "Enables the haptics customization feature" bug: "241918098" -}
\ No newline at end of file +} + +flag { + namespace: "haptics" + name: "haptics_customization_ringtone_v2_enabled" + description: "Enables the usage of the new RingtoneV2 class" + bug: "241918098" +} + +flag { + namespace: "haptics" + name: "enable_vibration_serialization_apis" + description: "Enables the APIs for vibration serialization/deserialization." + bug: "245129509" +} diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java index ded74eab149e..3d1deea57f14 100644 --- a/core/java/android/os/vibrator/persistence/ParsedVibration.java +++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java @@ -16,6 +16,7 @@ package android.os.vibrator.persistence; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -34,6 +35,7 @@ import java.util.List; * * @hide */ +@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS) @TestApi public class ParsedVibration { private final List<VibrationEffect> mEffects; diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java index e08cc4262bed..fed1053e2a12 100644 --- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java +++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java @@ -16,6 +16,7 @@ package android.os.vibrator.persistence; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -115,6 +116,7 @@ import java.util.List; * * @hide */ +@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS) @TestApi public final class VibrationXmlParser { private static final String TAG = "VibrationXmlParser"; diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java index 1cdfa4f74dbd..28804544edaf 100644 --- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java +++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java @@ -16,6 +16,7 @@ package android.os.vibrator.persistence; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; @@ -42,6 +43,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS) @TestApi public final class VibrationXmlSerializer { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b414ed4faea2..36d318008560 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11607,6 +11607,45 @@ public final class Settings { "accessibility_magnification_joystick_enabled"; /** + * Controls magnification enable gesture. Accessibility magnification can have one or more + * enable gestures. + * + * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE + * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP + * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP + * @hide + */ + public static final String ACCESSIBILITY_MAGNIFICATION_GESTURE = + "accessibility_magnification_gesture"; + + /** + * Magnification enable gesture value that is a default value. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE = 0x0; + + /** + * Magnification enable gesture value is single finger triple tap. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP = 0x1; + + /** + * Magnification enable gesture value is two finger triple tap. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP = 0x2; + + /** + * Magnification enable gesture values include single finger triple tap and two finger + * triple tap. + * @hide + */ + public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL = + ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP + | ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP; + + /** * Controls magnification capability. Accessibility magnification is capable of at least one * of the magnification modes. * diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 115894f586f2..a29bf7a06334 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -18,6 +18,7 @@ package android.service.autofill; import static android.view.autofill.Helper.sDebug; +import android.annotation.Hide; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,6 +27,7 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.ClipData; import android.content.IntentSender; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; @@ -187,6 +189,9 @@ public final class Dataset implements Parcelable { @Nullable private final InlinePresentation mInlinePresentation; @Nullable private final InlinePresentation mInlineTooltipPresentation; private final IntentSender mAuthentication; + + @Nullable private final Bundle mAuthenticationExtras; + @Nullable String mId; /** @@ -224,6 +229,7 @@ public final class Dataset implements Parcelable { mInlinePresentation = inlinePresentation; mInlineTooltipPresentation = inlineTooltipPresentation; mAuthentication = authentication; + mAuthenticationExtras = null; mId = id; } @@ -246,6 +252,7 @@ public final class Dataset implements Parcelable { mInlinePresentation = dataset.mInlinePresentation; mInlineTooltipPresentation = dataset.mInlineTooltipPresentation; mAuthentication = dataset.mAuthentication; + mAuthenticationExtras = dataset.mAuthenticationExtras; mId = dataset.mId; mAutofillDatatypes = dataset.mAutofillDatatypes; } @@ -264,6 +271,7 @@ public final class Dataset implements Parcelable { mInlinePresentation = builder.mInlinePresentation; mInlineTooltipPresentation = builder.mInlineTooltipPresentation; mAuthentication = builder.mAuthentication; + mAuthenticationExtras = builder.mAuthenticationExtras; mId = builder.mId; mAutofillDatatypes = builder.mAutofillDatatypes; } @@ -345,6 +353,12 @@ public final class Dataset implements Parcelable { } /** @hide */ + @Hide + public @Nullable Bundle getAuthenticationExtras() { + return mAuthenticationExtras; + } + + /** @hide */ @TestApi public boolean isEmpty() { return mFieldIds == null || mFieldIds.isEmpty(); @@ -401,6 +415,9 @@ public final class Dataset implements Parcelable { if (mAuthentication != null) { builder.append(", hasAuthentication"); } + if (mAuthenticationExtras != null) { + builder.append(", hasAuthenticationExtras"); + } if (mAutofillDatatypes != null) { builder.append(", autofillDatatypes=").append(mAutofillDatatypes); } @@ -454,6 +471,8 @@ public final class Dataset implements Parcelable { @Nullable private InlinePresentation mInlinePresentation; @Nullable private InlinePresentation mInlineTooltipPresentation; private IntentSender mAuthentication; + + private Bundle mAuthenticationExtras; private boolean mDestroyed; @Nullable private String mId; @@ -624,6 +643,25 @@ public final class Dataset implements Parcelable { } /** + * Sets extras to be associated with the {@code authentication} intent sender, to be + * set on the intent that is fired through the intent sender. + * + * Autofill providers can set any extras they wish to receive directly on the intent + * that is used to create the {@code authentication}. This is an internal API, to be + * used by the platform to associate data with a given dataset. These extras will be + * merged with the {@code clientState} and sent as part of the fill in intent when + * the {@code authentication} intentSender is invoked. + * + * @hide + */ + @Hide + public @NonNull Builder setAuthenticationExtras(@Nullable Bundle authenticationExtra) { + throwIfDestroyed(); + mAuthenticationExtras = authenticationExtra; + return this; + } + + /** * Sets the id for the dataset so its usage can be tracked. * * <p>Dataset usage can be tracked for 2 purposes: diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java index be7b72202b3f..a2b0a669c768 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -153,6 +153,18 @@ public abstract class CredentialProviderService extends Service { public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST"; + /** + * The key to autofillId associated with the requested credential option and the corresponding + * credential entry. The associated autofillId will be contained inside the candidate query + * bundle of {@link android.credentials.CredentialOption} if requested through the + * {@link com.android.credentialmanager.autofill.CredentialAutofillService}. The resulting + * credential entry will contain the autofillId inside its framework extras intent. + * + * @hide + */ + public static final String EXTRA_AUTOFILL_ID = + "android.service.credentials.extra.AUTOFILL_ID"; + private static final String TAG = "CredProviderService"; /** diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index c4d307073d12..a150187bbc6c 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -23,6 +23,8 @@ import android.graphics.GraphicBuffer; import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; +import com.android.internal.os.IResultReceiver; + /** * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the * runner control certain aspects of the recents animation, and to notify window manager when the @@ -58,7 +60,7 @@ interface IRecentsAnimationController { * top resumed app, false otherwise. */ @UnsupportedAppUsage - void finish(boolean moveHomeToTop, boolean sendUserLeaveHint); + void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, in IResultReceiver finishCb); /** * Called by the handler to indicate that the recents animation input consumer should be diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 2761aaeb4a7d..45b3fdd7e5bc 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -16,6 +16,8 @@ package android.view; +import static com.android.window.flags.Flags.surfaceTrustedOverlay; + import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Matrix; @@ -35,7 +37,6 @@ import java.lang.ref.WeakReference; * @hide */ public final class InputWindowHandle { - /** * An internal annotation for all the {@link android.os.InputConfig} flags that can be * specified to {@link #inputConfig} to control the behavior of an input window. Only the @@ -59,7 +60,6 @@ public final class InputWindowHandle { InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER, InputConfig.IS_WALLPAPER, InputConfig.PAUSE_DISPATCHING, - InputConfig.TRUSTED_OVERLAY, InputConfig.WATCH_OUTSIDE_TOUCH, InputConfig.SLIPPERY, InputConfig.DISABLE_USER_ACTIVITY, @@ -272,4 +272,13 @@ public final class InputWindowHandle { } this.inputConfig &= ~inputConfig; } + + public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc, + boolean isTrusted) { + if (surfaceTrustedOverlay()) { + t.setTrustedOverlay(sc, isTrusted); + } else if (isTrusted) { + inputConfig |= InputConfig.TRUSTED_OVERLAY; + } + } } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index effc127dabd2..57b19a8ce12f 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -407,7 +407,7 @@ public class SurfaceControlViewHost { public @Nullable SurfacePackage getSurfacePackage() { if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), - mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface); + mAccessibilityEmbeddedConnection, getInputTransferToken(), mRemoteInterface); } else { return null; } @@ -526,10 +526,12 @@ public class SurfaceControlViewHost { } /** + * Returns an input token used which can be used to request focus on the embedded surface. + * * @hide */ - public IBinder getFocusGrantToken() { - return mWm.getFocusGrantToken(getWindowToken().asBinder()); + public IBinder getInputTransferToken() { + return mWm.getInputTransferToken(getWindowToken().asBinder()); } private void addWindowToken(WindowManager.LayoutParams attrs) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e64274ec3892..2f4bea0270c1 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1850,6 +1850,8 @@ public interface WindowManager extends ViewManager { to = "PHONE"), @ViewDebug.IntToString(from = TYPE_SYSTEM_ALERT, to = "SYSTEM_ALERT"), + @ViewDebug.IntToString(from = TYPE_KEYGUARD, + to = "KEYGUARD"), @ViewDebug.IntToString(from = TYPE_TOAST, to = "TOAST"), @ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY, @@ -1898,6 +1900,8 @@ public interface WindowManager extends ViewManager { to = "PRIVATE_PRESENTATION"), @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "VOICE_INTERACTION"), + @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_OVERLAY, + to = "ACCESSIBILITY_OVERLAY"), @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "VOICE_INTERACTION_STARTING"), @ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, @@ -1907,7 +1911,13 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_SCREENSHOT, to = "SCREENSHOT"), @ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY, - to = "APPLICATION_OVERLAY") + to = "APPLICATION_OVERLAY"), + @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, + to = "ACCESSIBILITY_MAGNIFICATION_OVERLAY"), + @ViewDebug.IntToString(from = TYPE_NOTIFICATION_SHADE, + to = "NOTIFICATION_SHADE"), + @ViewDebug.IntToString(from = TYPE_STATUS_BAR_ADDITIONAL, + to = "STATUS_BAR_ADDITIONAL") }) @WindowType public int type; diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 7d3d283a45f2..8fe9b7bc0ca4 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -58,7 +58,7 @@ public class WindowlessWindowManager implements IWindowSession { SurfaceControl mLeash; Rect mFrame; Rect mAttachedFrame; - IBinder mFocusGrantToken; + IBinder mInputTransferToken; State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client, SurfaceControl leash, Rect frame) { @@ -89,7 +89,7 @@ public class WindowlessWindowManager implements IWindowSession { private final Configuration mConfiguration; private final IWindowSession mRealWm; private final IBinder mHostInputToken; - private final IBinder mFocusGrantToken = new Binder(); + private final IBinder mInputTransferToken = new Binder(); private InsetsState mInsetsState; private final ClientWindowFrames mTmpFrames = new ClientWindowFrames(); private final MergedConfiguration mTmpConfig = new MergedConfiguration(); @@ -109,17 +109,17 @@ public class WindowlessWindowManager implements IWindowSession { mConfiguration.setTo(configuration); } - IBinder getFocusGrantToken(IBinder window) { + IBinder getInputTransferToken(IBinder window) { synchronized (this) { // This can only happen if someone requested the focusGrantToken before setView was // called for the SCVH. In that case, use the root focusGrantToken since this will be // the same token sent to WMS for the root window once setView is called. if (mStateForWindow.isEmpty()) { - return mFocusGrantToken; + return mInputTransferToken; } State state = mStateForWindow.get(window); if (state != null) { - return state.mFocusGrantToken; + return state.mInputTransferToken; } } @@ -207,9 +207,9 @@ public class WindowlessWindowManager implements IWindowSession { // Give the first window the mFocusGrantToken since that's the token the host can use // to give focus to the embedded. if (mStateForWindow.isEmpty()) { - state.mFocusGrantToken = mFocusGrantToken; + state.mInputTransferToken = mInputTransferToken; } else { - state.mFocusGrantToken = new Binder(); + state.mInputTransferToken = new Binder(); } mStateForWindow.put(window.asBinder(), state); @@ -230,12 +230,13 @@ public class WindowlessWindowManager implements IWindowSession { new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"), window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, - attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(), + attrs.token, state.mInputTransferToken, attrs.getTitle().toString(), outInputChannel); } else { mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token, - state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); + state.mInputTransferToken, attrs.getTitle().toString(), + outInputChannel); } state.mInputChannelToken = outInputChannel != null ? outInputChannel.getToken() : null; diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index a0d58001a30d..e31ad82d3f55 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -4,5 +4,5 @@ flag { namespace: "accessibility" name: "force_invert_color" description: "Enable force force-dark for smart inversion and dark theme everywhere" - bug: "239594271" + bug: "282821643" }
\ No newline at end of file diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 89fa83e5c7aa..a95f2ef191f5 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -277,6 +277,12 @@ public final class AutofillManager { "android.view.autofill.extra.CLIENT_STATE"; /** + * @hide + */ + public static final String EXTRA_AUTH_STATE = + "android.view.autofill.extra.AUTH_STATE"; + + /** * Intent extra: the {@link android.view.inputmethod.InlineSuggestionsRequest} in the * autofill request. * diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 79acfbb0b39c..103725da9af0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -638,9 +638,13 @@ public class RemoteViews implements Parcelable, Filter { * Base class for all actions that can be performed on an * inflated view. * - * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! + * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! */ private abstract static class Action implements Parcelable { + @IdRes + @UnsupportedAppUsage + int mViewId; + public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException; @@ -664,7 +668,7 @@ public class RemoteViews implements Parcelable, Filter { public abstract int getActionTag(); public String getUniqueKey() { - return (getActionTag() + "_" + viewId); + return (getActionTag() + "_" + mViewId); } /** @@ -684,16 +688,12 @@ public class RemoteViews implements Parcelable, Filter { public void visitUris(@NonNull Consumer<Uri> visitor) { // Nothing to visit by default } - - @IdRes - @UnsupportedAppUsage - int viewId; } /** * Action class used during async inflation of RemoteViews. Subclasses are not parcelable. */ - private static abstract class RuntimeAction extends Action { + private abstract static class RuntimeAction extends Action { @Override public final int getActionTag() { return 0; @@ -796,15 +796,16 @@ public class RemoteViews implements Parcelable, Filter { for (int i = 0; i < mActions.size(); i++) { Action action = mActions.get(i); if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction - && itemsAction.viewId == viewId + && itemsAction.mViewId == viewId && itemsAction.mServiceIntent != null) { - mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId, - itemsAction.mServiceIntent)); + mActions.set(i, + new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId, + itemsAction.mServiceIntent)); isActionReplaced = true; } else if (action instanceof SetRemoteViewsAdapterIntent intentAction - && intentAction.viewId == viewId) { + && intentAction.mViewId == viewId) { mActions.set(i, new SetRemoteCollectionItemListAdapterAction( - intentAction.viewId, intentAction.intent)); + intentAction.mViewId, intentAction.mIntent)); isActionReplaced = true; } else if (action instanceof ViewGroupActionAdd groupAction && groupAction.mNestedViews != null) { @@ -838,7 +839,7 @@ public class RemoteViews implements Parcelable, Filter { if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction && itemsAction.mServiceIntent != null) || (action instanceof SetRemoteViewsAdapterIntent intentAction - && intentAction.intent != null) + && intentAction.mIntent != null) || (action instanceof ViewGroupActionAdd groupAction && groupAction.mNestedViews != null && groupAction.mNestedViews.hasLegacyLists())) { @@ -856,11 +857,7 @@ public class RemoteViews implements Parcelable, Filter { if (mLandscape != null && mLandscape.hasLegacyLists()) { return true; } - if (mPortrait != null && mPortrait.hasLegacyLists()) { - return true; - } - - return false; + return mPortrait != null && mPortrait.hasLegacyLists(); } private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) { @@ -913,31 +910,31 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetEmptyView extends Action { - int emptyViewId; + int mEmptyViewId; SetEmptyView(@IdRes int viewId, @IdRes int emptyViewId) { - this.viewId = viewId; - this.emptyViewId = emptyViewId; + this.mViewId = viewId; + this.mEmptyViewId = emptyViewId; } SetEmptyView(Parcel in) { - this.viewId = in.readInt(); - this.emptyViewId = in.readInt(); + this.mViewId = in.readInt(); + this.mEmptyViewId = in.readInt(); } public void writeToParcel(Parcel out, int flags) { - out.writeInt(this.viewId); - out.writeInt(this.emptyViewId); + out.writeInt(this.mViewId); + out.writeInt(this.mEmptyViewId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (!(view instanceof AdapterView<?>)) return; AdapterView<?> adapterView = (AdapterView<?>) view; - final View emptyView = root.findViewById(emptyViewId); + final View emptyView = root.findViewById(mEmptyViewId); if (emptyView == null) return; adapterView.setEmptyView(emptyView); @@ -950,24 +947,27 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetPendingIntentTemplate extends Action { + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + PendingIntent mPendingIntentTemplate; + public SetPendingIntentTemplate(@IdRes int id, PendingIntent pendingIntentTemplate) { - this.viewId = id; - this.pendingIntentTemplate = pendingIntentTemplate; + this.mViewId = id; + this.mPendingIntentTemplate = pendingIntentTemplate; } public SetPendingIntentTemplate(Parcel parcel) { - viewId = parcel.readInt(); - pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel); + mViewId = parcel.readInt(); + mPendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest); + dest.writeInt(mViewId); + PendingIntent.writePendingIntentOrNullToParcel(mPendingIntentTemplate, dest); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense @@ -981,10 +981,10 @@ public class RemoteViews implements Parcelable, Filter { } }; av.setOnItemClickListener(listener); - av.setTag(pendingIntentTemplate); + av.setTag(mPendingIntentTemplate); } else { Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" + - "an AdapterView (id: " + viewId + ")"); + "an AdapterView (id: " + mViewId + ")"); return; } } @@ -1015,65 +1015,65 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_PENDING_INTENT_TEMPLATE_TAG; } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - PendingIntent pendingIntentTemplate; } private static class SetRemoteViewsAdapterList extends Action { + int mViewTypeCount; + ArrayList<RemoteViews> mList; + public SetRemoteViewsAdapterList(@IdRes int id, ArrayList<RemoteViews> list, int viewTypeCount) { - this.viewId = id; - this.list = list; - this.viewTypeCount = viewTypeCount; + this.mViewId = id; + this.mList = list; + this.mViewTypeCount = viewTypeCount; } public SetRemoteViewsAdapterList(Parcel parcel) { - viewId = parcel.readInt(); - viewTypeCount = parcel.readInt(); - list = parcel.createTypedArrayList(RemoteViews.CREATOR); + mViewId = parcel.readInt(); + mViewTypeCount = parcel.readInt(); + mList = parcel.createTypedArrayList(RemoteViews.CREATOR); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(viewTypeCount); - dest.writeTypedList(list, flags); + dest.writeInt(mViewId); + dest.writeInt(mViewTypeCount); + dest.writeTypedList(mList, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " + - "AppWidgets (root id: " + viewId + ")"); + "AppWidgets (root id: " + mViewId + ")"); return; } // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " + - "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); + "an AbsListView or AdapterViewAnimator (id: " + mViewId + ")"); return; } if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; Adapter a = v.getAdapter(); - if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { - ((RemoteViewsListAdapter) a).setViewsList(list); + if (a instanceof RemoteViewsListAdapter && mViewTypeCount <= a.getViewTypeCount()) { + ((RemoteViewsListAdapter) a).setViewsList(mList); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), mList, mViewTypeCount, params.colorResources)); } } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; Adapter a = v.getAdapter(); - if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { - ((RemoteViewsListAdapter) a).setViewsList(list); + if (a instanceof RemoteViewsListAdapter && mViewTypeCount <= a.getViewTypeCount()) { + ((RemoteViewsListAdapter) a).setViewsList(mList); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), mList, mViewTypeCount, params.colorResources)); } } @@ -1086,11 +1086,8 @@ public class RemoteViews implements Parcelable, Filter { @Override public String getUniqueKey() { - return (SET_REMOTE_ADAPTER_TAG + "_" + viewId); + return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId); } - - int viewTypeCount; - ArrayList<RemoteViews> list; } /** @@ -1135,19 +1132,20 @@ public class RemoteViews implements Parcelable, Filter { } private class SetRemoteCollectionItemListAdapterAction extends Action { - private @NonNull CompletableFuture<RemoteCollectionItems> mItemsFuture; + @NonNull + private CompletableFuture<RemoteCollectionItems> mItemsFuture; final Intent mServiceIntent; SetRemoteCollectionItemListAdapterAction(@IdRes int id, @NonNull RemoteCollectionItems items) { - viewId = id; + mViewId = id; items.setHierarchyRootData(getHierarchyRootData()); mItemsFuture = CompletableFuture.completedFuture(items); mServiceIntent = null; } SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) { - viewId = id; + mViewId = id; mItemsFuture = getItemsFutureFromIntentWithTimeout(intent); setHierarchyRootData(getHierarchyRootData()); mServiceIntent = intent; @@ -1196,7 +1194,7 @@ public class RemoteViews implements Parcelable, Filter { } SetRemoteCollectionItemListAdapterAction(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mItemsFuture = CompletableFuture.completedFuture( new RemoteCollectionItems(parcel, getHierarchyRootData())); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); @@ -1226,7 +1224,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); items.writeToParcel(dest, flags, /* attached= */ true); dest.writeTypedObject(mServiceIntent, flags); @@ -1235,7 +1233,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - View target = root.findViewById(viewId); + View target = root.findViewById(mViewId); if (target == null) return; RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); @@ -1243,13 +1241,13 @@ public class RemoteViews implements Parcelable, Filter { // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "setRemoteAdapter can only be used for " - + "AppWidgets (root id: " + viewId + ")"); + + "AppWidgets (root id: " + mViewId + ")"); return; } if (!(target instanceof AdapterView)) { Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not " - + "an AdapterView (id: " + viewId + ")"); + + "an AdapterView (id: " + mViewId + ")"); return; } @@ -1289,59 +1287,62 @@ public class RemoteViews implements Parcelable, Filter { @Override public String getUniqueKey() { - return (SET_REMOTE_ADAPTER_TAG + "_" + viewId); + return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId); } } private class SetRemoteViewsAdapterIntent extends Action { + Intent mIntent; + boolean mIsAsync = false; + public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) { - this.viewId = id; - this.intent = intent; + this.mViewId = id; + this.mIntent = intent; } public SetRemoteViewsAdapterIntent(Parcel parcel) { - viewId = parcel.readInt(); - intent = parcel.readTypedObject(Intent.CREATOR); + mViewId = parcel.readInt(); + mIntent = parcel.readTypedObject(Intent.CREATOR); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeTypedObject(intent, flags); + dest.writeInt(mViewId); + dest.writeTypedObject(mIntent, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "setRemoteAdapter can only be used for " - + "AppWidgets (root id: " + viewId + ")"); + + "AppWidgets (root id: " + mViewId + ")"); return; } // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { Log.e(LOG_TAG, "Cannot setRemoteAdapter on a view which is not " - + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); + + "an AbsListView or AdapterViewAnimator (id: " + mViewId + ")"); return; } // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent // RemoteViewsService AppWidgetHostView host = (AppWidgetHostView) rootParent; - intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()) + mIntent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()) .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)); if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; - v.setRemoteViewsAdapter(intent, isAsync); + v.setRemoteViewsAdapter(mIntent, mIsAsync); v.setRemoteViewsInteractionHandler(params.handler); } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; - v.setRemoteViewsAdapter(intent, isAsync); + v.setRemoteViewsAdapter(mIntent, mIsAsync); v.setRemoteViewsOnClickHandler(params.handler); } } @@ -1349,8 +1350,8 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, ActionApplyParams params) { - SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent); - copy.isAsync = true; + SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(mViewId, mIntent); + copy.mIsAsync = true; return copy; } @@ -1358,9 +1359,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG; } - - Intent intent; - boolean isAsync = false; } /** @@ -1369,26 +1367,27 @@ public class RemoteViews implements Parcelable, Filter { * to launch the provided {@link PendingIntent}. */ private class SetOnClickResponse extends Action { + final RemoteResponse mResponse; SetOnClickResponse(@IdRes int id, RemoteResponse response) { - this.viewId = id; + this.mViewId = id; this.mResponse = response; } SetOnClickResponse(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mResponse = new RemoteResponse(); mResponse.readFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); mResponse.writeToParcel(dest, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (mResponse.mPendingIntent != null) { @@ -1397,7 +1396,7 @@ public class RemoteViews implements Parcelable, Filter { // AdapterView's children? if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item " - + "(id: " + viewId + ")"); + + "(id: " + mViewId + ")"); ApplicationInfo appInfo = root.getContext().getApplicationInfo(); // We let this slide for HC and ICS so as to not break compatibility. It should @@ -1435,8 +1434,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_CLICK_RESPONSE_TAG; } - - final RemoteResponse mResponse; } /** @@ -1446,32 +1443,31 @@ public class RemoteViews implements Parcelable, Filter { * to launch the provided {@link PendingIntent}. */ private class SetOnCheckedChangeResponse extends Action { - private final RemoteResponse mResponse; SetOnCheckedChangeResponse(@IdRes int id, RemoteResponse response) { - this.viewId = id; + this.mViewId = id; this.mResponse = response; } SetOnCheckedChangeResponse(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mResponse = new RemoteResponse(); mResponse.readFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); mResponse.writeToParcel(dest, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (!(target instanceof CompoundButton)) { Log.w(LOG_TAG, "setOnCheckedChange methods cannot be used on " - + "non-CompoundButton child (id: " + viewId + ")"); + + "non-CompoundButton child (id: " + mViewId + ")"); return; } CompoundButton button = (CompoundButton) target; @@ -1481,7 +1477,7 @@ public class RemoteViews implements Parcelable, Filter { // must use setOnCheckedChangeFillInIntent instead. if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.w(LOG_TAG, "Cannot setOnCheckedChangePendingIntent for collection item " - + "(id: " + viewId + ")"); + + "(id: " + mViewId + ")"); return; } target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent); @@ -1648,36 +1644,40 @@ public class RemoteViews implements Parcelable, Filter { * <p> */ private static class SetDrawableTint extends Action { + boolean mTargetBackground; + @ColorInt int mColorFilter; + PorterDuff.Mode mFilterMode; + SetDrawableTint(@IdRes int id, boolean targetBackground, @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) { - this.viewId = id; - this.targetBackground = targetBackground; - this.colorFilter = colorFilter; - this.filterMode = mode; + this.mViewId = id; + this.mTargetBackground = targetBackground; + this.mColorFilter = colorFilter; + this.mFilterMode = mode; } SetDrawableTint(Parcel parcel) { - viewId = parcel.readInt(); - targetBackground = parcel.readInt() != 0; - colorFilter = parcel.readInt(); - filterMode = PorterDuff.intToMode(parcel.readInt()); + mViewId = parcel.readInt(); + mTargetBackground = parcel.readInt() != 0; + mColorFilter = parcel.readInt(); + mFilterMode = PorterDuff.intToMode(parcel.readInt()); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(targetBackground ? 1 : 0); - dest.writeInt(colorFilter); - dest.writeInt(PorterDuff.modeToInt(filterMode)); + dest.writeInt(mViewId); + dest.writeInt(mTargetBackground ? 1 : 0); + dest.writeInt(mColorFilter); + dest.writeInt(PorterDuff.modeToInt(mFilterMode)); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Pick the correct drawable to modify for this view Drawable targetDrawable = null; - if (targetBackground) { + if (mTargetBackground) { targetDrawable = target.getBackground(); } else if (target instanceof ImageView) { ImageView imageView = (ImageView) target; @@ -1685,7 +1685,7 @@ public class RemoteViews implements Parcelable, Filter { } if (targetDrawable != null) { - targetDrawable.mutate().setColorFilter(colorFilter, filterMode); + targetDrawable.mutate().setColorFilter(mColorFilter, mFilterMode); } } @@ -1693,10 +1693,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_DRAWABLE_TINT_TAG; } - - boolean targetBackground; - @ColorInt int colorFilter; - PorterDuff.Mode filterMode; } /** @@ -1709,27 +1705,26 @@ public class RemoteViews implements Parcelable, Filter { * <p> */ private class SetRippleDrawableColor extends Action { - ColorStateList mColorStateList; SetRippleDrawableColor(@IdRes int id, ColorStateList colorStateList) { - this.viewId = id; + this.mViewId = id; this.mColorStateList = colorStateList; } SetRippleDrawableColor(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mColorStateList = parcel.readParcelable(null, android.content.res.ColorStateList.class); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeParcelable(mColorStateList, 0); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; // Pick the correct drawable to modify for this view @@ -1756,23 +1751,23 @@ public class RemoteViews implements Parcelable, Filter { final boolean mNext; ViewContentNavigation(@IdRes int viewId, boolean next) { - this.viewId = viewId; + this.mViewId = viewId; this.mNext = next; } ViewContentNavigation(Parcel in) { - this.viewId = in.readInt(); + this.mViewId = in.readInt(); this.mNext = in.readBoolean(); } public void writeToParcel(Parcel out, int flags) { - out.writeInt(this.viewId); + out.writeInt(this.mViewId); out.writeBoolean(this.mNext); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (view == null) return; try { @@ -1794,7 +1789,6 @@ public class RemoteViews implements Parcelable, Filter { } private static class BitmapCache { - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) ArrayList<Bitmap> mBitmaps; SparseIntArray mBitmapHashes; @@ -1861,45 +1855,45 @@ public class RemoteViews implements Parcelable, Filter { } private class BitmapReflectionAction extends Action { - int bitmapId; + int mBitmapId; @UnsupportedAppUsage - Bitmap bitmap; + Bitmap mBitmap; @UnsupportedAppUsage - String methodName; + String mMethodName; BitmapReflectionAction(@IdRes int viewId, String methodName, Bitmap bitmap) { - this.bitmap = bitmap; - this.viewId = viewId; - this.methodName = methodName; - bitmapId = mBitmapCache.getBitmapId(bitmap); + this.mBitmap = bitmap; + this.mViewId = viewId; + this.mMethodName = methodName; + mBitmapId = mBitmapCache.getBitmapId(bitmap); } BitmapReflectionAction(Parcel in) { - viewId = in.readInt(); - methodName = in.readString8(); - bitmapId = in.readInt(); - bitmap = mBitmapCache.getBitmapForId(bitmapId); + mViewId = in.readInt(); + mMethodName = in.readString8(); + mBitmapId = in.readInt(); + mBitmap = mBitmapCache.getBitmapForId(mBitmapId); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeString8(methodName); - dest.writeInt(bitmapId); + dest.writeInt(mViewId); + dest.writeString8(mMethodName); + dest.writeInt(mBitmapId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - ReflectionAction ra = new ReflectionAction(viewId, methodName, + ReflectionAction ra = new ReflectionAction(mViewId, mMethodName, BaseReflectionAction.BITMAP, - bitmap); + mBitmap); ra.apply(root, rootParent, params); } @Override public void setHierarchyRootData(HierarchyRootData rootData) { - bitmapId = rootData.mBitmapCache.getBitmapId(bitmap); + mBitmapId = rootData.mBitmapCache.getBitmapId(mBitmap); } @Override @@ -1933,30 +1927,30 @@ public class RemoteViews implements Parcelable, Filter { static final int BLEND_MODE = 17; @UnsupportedAppUsage - String methodName; - int type; + String mMethodName; + int mType; BaseReflectionAction(@IdRes int viewId, String methodName, int type) { - this.viewId = viewId; - this.methodName = methodName; - this.type = type; + this.mViewId = viewId; + this.mMethodName = methodName; + this.mType = type; } BaseReflectionAction(Parcel in) { - this.viewId = in.readInt(); - this.methodName = in.readString8(); - this.type = in.readInt(); + this.mViewId = in.readInt(); + this.mMethodName = in.readString8(); + this.mType = in.readInt(); //noinspection ConstantIfStatement if (false) { - Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId) - + " methodName=" + this.methodName + " type=" + this.type); + Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.mViewId) + + " methodName=" + this.mMethodName + " type=" + this.mType); } } public void writeToParcel(Parcel out, int flags) { - out.writeInt(this.viewId); - out.writeString8(this.methodName); - out.writeInt(this.type); + out.writeInt(this.mViewId); + out.writeString8(this.mMethodName); + out.writeInt(this.mType); } /** @@ -1971,16 +1965,16 @@ public class RemoteViews implements Parcelable, Filter { @Override public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (view == null) return; - Class<?> param = getParameterType(this.type); + Class<?> param = getParameterType(this.mType); if (param == null) { - throw new ActionException("bad type: " + this.type); + throw new ActionException("bad type: " + this.mType); } Object value = getParameterValue(view); try { - getMethod(view, this.methodName, param, false /* async */).invoke(view, value); + getMethod(view, this.mMethodName, param, false /* async */).invoke(view, value); } catch (Throwable ex) { throw new ActionException(ex); } @@ -1989,17 +1983,17 @@ public class RemoteViews implements Parcelable, Filter { @Override public final Action initActionAsync(ViewTree root, ViewGroup rootParent, ActionApplyParams params) { - final View view = root.findViewById(viewId); + final View view = root.findViewById(mViewId); if (view == null) return ACTION_NOOP; - Class<?> param = getParameterType(this.type); + Class<?> param = getParameterType(this.mType); if (param == null) { - throw new ActionException("bad type: " + this.type); + throw new ActionException("bad type: " + this.mType); } Object value = getParameterValue(view); try { - MethodHandle method = getMethod(view, this.methodName, param, true /* async */); + MethodHandle method = getMethod(view, this.mMethodName, param, true /* async */); // Upload the bitmap to GPU if the parameter is of type Bitmap or Icon. // Since bitmaps in framework are seldomly modified, this is supposed to accelerate // the operations. @@ -2025,7 +2019,7 @@ public class RemoteViews implements Parcelable, Filter { if (endAction instanceof ViewStub.ViewReplaceRunnable) { root.createTree(); // Replace child tree - root.findViewTreeById(viewId).replaceView( + root.findViewTreeById(mViewId).replaceView( ((ViewStub.ViewReplaceRunnable) endAction).view); } return new RunnableAction(endAction); @@ -2039,7 +2033,7 @@ public class RemoteViews implements Parcelable, Filter { public final int mergeBehavior() { // smoothScrollBy is cumulative, everything else overwites. - if (methodName.equals("smoothScrollBy")) { + if (mMethodName.equals("smoothScrollBy")) { return MERGE_APPEND; } else { return MERGE_REPLACE; @@ -2050,17 +2044,17 @@ public class RemoteViews implements Parcelable, Filter { public final String getUniqueKey() { // Each type of reflection action corresponds to a setter, so each should be seen as // unique from the standpoint of merging. - return super.getUniqueKey() + this.methodName + this.type; + return super.getUniqueKey() + this.mMethodName + this.mType; } @Override public final boolean prefersAsyncApply() { - return this.type == URI || this.type == ICON; + return this.mType == URI || this.mType == ICON; } @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - switch (this.type) { + switch (this.mType) { case URI: final Uri uri = (Uri) getParameterValue(null); if (uri != null) visitor.accept(uri); @@ -2076,53 +2070,53 @@ public class RemoteViews implements Parcelable, Filter { /** Class for the reflection actions. */ private static final class ReflectionAction extends BaseReflectionAction { @UnsupportedAppUsage - Object value; + Object mValue; ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) { super(viewId, methodName, type); - this.value = value; + this.mValue = value; } ReflectionAction(Parcel in) { super(in); // For some values that may have been null, we first check a flag to see if they were // written to the parcel. - switch (this.type) { + switch (this.mType) { case BOOLEAN: - this.value = in.readBoolean(); + this.mValue = in.readBoolean(); break; case BYTE: - this.value = in.readByte(); + this.mValue = in.readByte(); break; case SHORT: - this.value = (short) in.readInt(); + this.mValue = (short) in.readInt(); break; case INT: - this.value = in.readInt(); + this.mValue = in.readInt(); break; case LONG: - this.value = in.readLong(); + this.mValue = in.readLong(); break; case FLOAT: - this.value = in.readFloat(); + this.mValue = in.readFloat(); break; case DOUBLE: - this.value = in.readDouble(); + this.mValue = in.readDouble(); break; case CHAR: - this.value = (char) in.readInt(); + this.mValue = (char) in.readInt(); break; case STRING: - this.value = in.readString8(); + this.mValue = in.readString8(); break; case CHAR_SEQUENCE: - this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + this.mValue = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); break; case URI: - this.value = in.readTypedObject(Uri.CREATOR); + this.mValue = in.readTypedObject(Uri.CREATOR); break; case BITMAP: - this.value = in.readTypedObject(Bitmap.CREATOR); + this.mValue = in.readTypedObject(Bitmap.CREATOR); break; case BUNDLE: // Because we use Parcel.allowSquashing() when writing, and that affects @@ -2131,24 +2125,24 @@ public class RemoteViews implements Parcelable, Filter { // just happens to have that effect on Bundle.readFromParcel(). // TODO(b/212731590): build this state tracking into Bundle if (in.hasReadWriteHelper()) { - this.value = in.readBundle(); + this.mValue = in.readBundle(); } else { in.setReadWriteHelper(ALTERNATIVE_DEFAULT); - this.value = in.readBundle(); + this.mValue = in.readBundle(); in.setReadWriteHelper(null); } break; case INTENT: - this.value = in.readTypedObject(Intent.CREATOR); + this.mValue = in.readTypedObject(Intent.CREATOR); break; case COLOR_STATE_LIST: - this.value = in.readTypedObject(ColorStateList.CREATOR); + this.mValue = in.readTypedObject(ColorStateList.CREATOR); break; case ICON: - this.value = in.readTypedObject(Icon.CREATOR); + this.mValue = in.readTypedObject(Icon.CREATOR); break; case BLEND_MODE: - this.value = BlendMode.fromValue(in.readInt()); + this.mValue = BlendMode.fromValue(in.readInt()); break; default: break; @@ -2159,49 +2153,49 @@ public class RemoteViews implements Parcelable, Filter { super.writeToParcel(out, flags); // For some values which are null, we record an integer flag to indicate whether // we have written a valid value to the parcel. - switch (this.type) { + switch (this.mType) { case BOOLEAN: - out.writeBoolean((Boolean) this.value); + out.writeBoolean((Boolean) this.mValue); break; case BYTE: - out.writeByte((Byte) this.value); + out.writeByte((Byte) this.mValue); break; case SHORT: - out.writeInt((Short) this.value); + out.writeInt((Short) this.mValue); break; case INT: - out.writeInt((Integer) this.value); + out.writeInt((Integer) this.mValue); break; case LONG: - out.writeLong((Long) this.value); + out.writeLong((Long) this.mValue); break; case FLOAT: - out.writeFloat((Float) this.value); + out.writeFloat((Float) this.mValue); break; case DOUBLE: - out.writeDouble((Double) this.value); + out.writeDouble((Double) this.mValue); break; case CHAR: - out.writeInt((int) ((Character) this.value).charValue()); + out.writeInt((int) ((Character) this.mValue).charValue()); break; case STRING: - out.writeString8((String) this.value); + out.writeString8((String) this.mValue); break; case CHAR_SEQUENCE: - TextUtils.writeToParcel((CharSequence) this.value, out, flags); + TextUtils.writeToParcel((CharSequence) this.mValue, out, flags); break; case BUNDLE: - out.writeBundle((Bundle) this.value); + out.writeBundle((Bundle) this.mValue); break; case BLEND_MODE: - out.writeInt(BlendMode.toValue((BlendMode) this.value)); + out.writeInt(BlendMode.toValue((BlendMode) this.mValue)); break; case URI: case BITMAP: case INTENT: case COLOR_STATE_LIST: case ICON: - out.writeTypedObject((Parcelable) this.value, flags); + out.writeTypedObject((Parcelable) this.mValue, flags); break; default: break; @@ -2211,7 +2205,7 @@ public class RemoteViews implements Parcelable, Filter { @Nullable @Override protected Object getParameterValue(@Nullable View view) throws ActionException { - return this.value; + return this.mValue; } @Override @@ -2221,7 +2215,6 @@ public class RemoteViews implements Parcelable, Filter { } private static final class ResourceReflectionAction extends BaseReflectionAction { - static final int DIMEN_RESOURCE = 1; static final int COLOR_RESOURCE = 2; static final int STRING_RESOURCE = 3; @@ -2258,7 +2251,7 @@ public class RemoteViews implements Parcelable, Filter { try { switch (this.mResourceType) { case DIMEN_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return mResId == 0 ? 0 : resources.getDimensionPixelSize(mResId); case BaseReflectionAction.FLOAT: @@ -2266,10 +2259,10 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException( "dimen resources must be used as INT or FLOAT, " - + "not " + this.type); + + "not " + this.mType); } case COLOR_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return mResId == 0 ? 0 : view.getContext().getColor(mResId); case BaseReflectionAction.COLOR_STATE_LIST: @@ -2278,10 +2271,10 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException( "color resources must be used as INT or COLOR_STATE_LIST," - + " not " + this.type); + + " not " + this.mType); } case STRING_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.CHAR_SEQUENCE: return mResId == 0 ? null : resources.getText(mResId); case BaseReflectionAction.STRING: @@ -2289,7 +2282,7 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException( "string resources must be used as STRING or CHAR_SEQUENCE," - + " not " + this.type); + + " not " + this.mType); } default: throw new ActionException("unknown resource type: " + this.mResourceType); @@ -2308,7 +2301,6 @@ public class RemoteViews implements Parcelable, Filter { } private static final class AttributeReflectionAction extends BaseReflectionAction { - static final int DIMEN_RESOURCE = 1; static final int COLOR_RESOURCE = 2; static final int STRING_RESOURCE = 3; @@ -2347,7 +2339,7 @@ public class RemoteViews implements Parcelable, Filter { } switch (this.mResourceType) { case DIMEN_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return typedArray.getDimensionPixelSize(0, 0); case BaseReflectionAction.FLOAT: @@ -2356,10 +2348,10 @@ public class RemoteViews implements Parcelable, Filter { throw new ActionException( "dimen attribute 0x" + Integer.toHexString(this.mAttrId) + " must be used as INT or FLOAT," - + " not " + this.type); + + " not " + this.mType); } case COLOR_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.INT: return typedArray.getColor(0, 0); case BaseReflectionAction.COLOR_STATE_LIST: @@ -2368,10 +2360,10 @@ public class RemoteViews implements Parcelable, Filter { throw new ActionException( "color attribute 0x" + Integer.toHexString(this.mAttrId) + " must be used as INT or COLOR_STATE_LIST," - + " not " + this.type); + + " not " + this.mType); } case STRING_RESOURCE: - switch (this.type) { + switch (this.mType) { case BaseReflectionAction.CHAR_SEQUENCE: return typedArray.getText(0); case BaseReflectionAction.STRING: @@ -2380,7 +2372,7 @@ public class RemoteViews implements Parcelable, Filter { throw new ActionException( "string attribute 0x" + Integer.toHexString(this.mAttrId) + " must be used as STRING or CHAR_SEQUENCE," - + " not " + this.type); + + " not " + this.mType); } default: // Note: This can only be an implementation error. @@ -2402,7 +2394,6 @@ public class RemoteViews implements Parcelable, Filter { } } private static final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction { - private final float mValue; @ComplexDimensionUnit private final int mUnit; @@ -2435,14 +2426,14 @@ public class RemoteViews implements Parcelable, Filter { DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics(); try { int data = TypedValue.createComplexDimension(this.mValue, this.mUnit); - switch (this.type) { + switch (this.mType) { case ReflectionAction.INT: return TypedValue.complexToDimensionPixelSize(data, dm); case ReflectionAction.FLOAT: return TypedValue.complexToDimension(data, dm); default: throw new ActionException( - "parameter type must be INT or FLOAT, not " + this.type); + "parameter type must be INT or FLOAT, not " + this.mType); } } catch (ActionException ex) { throw ex; @@ -2458,7 +2449,6 @@ public class RemoteViews implements Parcelable, Filter { } private static final class NightModeReflectionAction extends BaseReflectionAction { - private final Object mLightValue; private final Object mDarkValue; @@ -2475,7 +2465,7 @@ public class RemoteViews implements Parcelable, Filter { NightModeReflectionAction(Parcel in) { super(in); - switch (this.type) { + switch (this.mType) { case ICON: mLightValue = in.readTypedObject(Icon.CREATOR); mDarkValue = in.readTypedObject(Icon.CREATOR); @@ -2489,14 +2479,14 @@ public class RemoteViews implements Parcelable, Filter { mDarkValue = in.readInt(); break; default: - throw new ActionException("Unexpected night mode action type: " + this.type); + throw new ActionException("Unexpected night mode action type: " + this.mType); } } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); - switch (this.type) { + switch (this.mType) { case ICON: case COLOR_STATE_LIST: out.writeTypedObject((Parcelable) mLightValue, flags); @@ -2525,7 +2515,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - if (this.type == ICON) { + if (this.mType == ICON) { visitIconUri((Icon) mDarkValue, visitor); visitIconUri((Icon) mLightValue, visitor); } @@ -2617,7 +2607,7 @@ public class RemoteViews implements Parcelable, Filter { } ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) { - this.viewId = viewId; + this.mViewId = viewId; mNestedViews = nestedViews; mIndex = index; mStableId = stableId; @@ -2625,7 +2615,7 @@ public class RemoteViews implements Parcelable, Filter { } ViewGroupActionAdd(Parcel parcel, ApplicationInfo info, int depth) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mIndex = parcel.readInt(); mStableId = parcel.readInt(); mNestedViews = new RemoteViews(parcel, getHierarchyRootData(), info, depth); @@ -2633,7 +2623,7 @@ public class RemoteViews implements Parcelable, Filter { } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mIndex); dest.writeInt(mStableId); mNestedViews.writeToParcel(dest, flags); @@ -2658,7 +2648,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { final Context context = root.getContext(); - final ViewGroup target = root.findViewById(viewId); + final ViewGroup target = root.findViewById(mViewId); if (target == null) { return; @@ -2713,7 +2703,7 @@ public class RemoteViews implements Parcelable, Filter { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); - ViewTree target = root.findViewTreeById(viewId); + ViewTree target = root.findViewTreeById(mViewId); if ((target == null) || !(target.mRoot instanceof ViewGroup)) { return ACTION_NOOP; } @@ -2845,23 +2835,23 @@ public class RemoteViews implements Parcelable, Filter { } ViewGroupActionRemove(@IdRes int viewId, @IdRes int viewIdToKeep) { - this.viewId = viewId; + this.mViewId = viewId; mViewIdToKeep = viewIdToKeep; } ViewGroupActionRemove(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mViewIdToKeep = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mViewIdToKeep); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final ViewGroup target = root.findViewById(viewId); + final ViewGroup target = root.findViewById(mViewId); if (target == null) { return; @@ -2888,7 +2878,7 @@ public class RemoteViews implements Parcelable, Filter { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); - ViewTree target = root.findViewTreeById(viewId); + ViewTree target = root.findViewTreeById(mViewId); if ((target == null) || !(target.mRoot instanceof ViewGroup)) { return ACTION_NOOP; @@ -2953,22 +2943,21 @@ public class RemoteViews implements Parcelable, Filter { * Action to remove a view from its parent. */ private static class RemoveFromParentAction extends Action { - RemoveFromParentAction(@IdRes int viewId) { - this.viewId = viewId; + this.mViewId = viewId; } RemoveFromParentAction(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null || target == root) { return; @@ -2986,7 +2975,7 @@ public class RemoteViews implements Parcelable, Filter { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the correct view. root.createTree(); - ViewTree target = root.findViewTreeById(viewId); + ViewTree target = root.findViewTreeById(mViewId); if (target == null || target == root) { return ACTION_NOOP; @@ -3023,88 +3012,96 @@ public class RemoteViews implements Parcelable, Filter { * (s/t/e/b) or cardinal (l/t/r/b) arrangement. */ private static class TextViewDrawableAction extends Action { + boolean mIsRelative = false; + boolean mUseIcons = false; + int mD1, mD2, mD3, mD4; + Icon mI1, mI2, mI3, mI4; + + boolean mDrawablesLoaded = false; + Drawable mId1, mId2, mId3, mId4; + public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, @DrawableRes int d1, @DrawableRes int d2, @DrawableRes int d3, @DrawableRes int d4) { - this.viewId = viewId; - this.isRelative = isRelative; - this.useIcons = false; - this.d1 = d1; - this.d2 = d2; - this.d3 = d3; - this.d4 = d4; + this.mViewId = viewId; + this.mIsRelative = isRelative; + this.mUseIcons = false; + this.mD1 = d1; + this.mD2 = d2; + this.mD3 = d3; + this.mD4 = d4; } public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, Icon i1, Icon i2, Icon i3, Icon i4) { - this.viewId = viewId; - this.isRelative = isRelative; - this.useIcons = true; - this.i1 = i1; - this.i2 = i2; - this.i3 = i3; - this.i4 = i4; + this.mViewId = viewId; + this.mIsRelative = isRelative; + this.mUseIcons = true; + this.mI1 = i1; + this.mI2 = i2; + this.mI3 = i3; + this.mI4 = i4; } public TextViewDrawableAction(Parcel parcel) { - viewId = parcel.readInt(); - isRelative = (parcel.readInt() != 0); - useIcons = (parcel.readInt() != 0); - if (useIcons) { - i1 = parcel.readTypedObject(Icon.CREATOR); - i2 = parcel.readTypedObject(Icon.CREATOR); - i3 = parcel.readTypedObject(Icon.CREATOR); - i4 = parcel.readTypedObject(Icon.CREATOR); + mViewId = parcel.readInt(); + mIsRelative = (parcel.readInt() != 0); + mUseIcons = (parcel.readInt() != 0); + if (mUseIcons) { + mI1 = parcel.readTypedObject(Icon.CREATOR); + mI2 = parcel.readTypedObject(Icon.CREATOR); + mI3 = parcel.readTypedObject(Icon.CREATOR); + mI4 = parcel.readTypedObject(Icon.CREATOR); } else { - d1 = parcel.readInt(); - d2 = parcel.readInt(); - d3 = parcel.readInt(); - d4 = parcel.readInt(); + mD1 = parcel.readInt(); + mD2 = parcel.readInt(); + mD3 = parcel.readInt(); + mD4 = parcel.readInt(); } } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(isRelative ? 1 : 0); - dest.writeInt(useIcons ? 1 : 0); - if (useIcons) { - dest.writeTypedObject(i1, 0); - dest.writeTypedObject(i2, 0); - dest.writeTypedObject(i3, 0); - dest.writeTypedObject(i4, 0); + dest.writeInt(mViewId); + dest.writeInt(mIsRelative ? 1 : 0); + dest.writeInt(mUseIcons ? 1 : 0); + if (mUseIcons) { + dest.writeTypedObject(mI1, 0); + dest.writeTypedObject(mI2, 0); + dest.writeTypedObject(mI3, 0); + dest.writeTypedObject(mI4, 0); } else { - dest.writeInt(d1); - dest.writeInt(d2); - dest.writeInt(d3); - dest.writeInt(d4); + dest.writeInt(mD1); + dest.writeInt(mD2); + dest.writeInt(mD3); + dest.writeInt(mD4); } } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final TextView target = root.findViewById(viewId); + final TextView target = root.findViewById(mViewId); if (target == null) return; - if (drawablesLoaded) { - if (isRelative) { - target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4); + if (mDrawablesLoaded) { + if (mIsRelative) { + target.setCompoundDrawablesRelativeWithIntrinsicBounds(mId1, mId2, mId3, mId4); } else { - target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4); + target.setCompoundDrawablesWithIntrinsicBounds(mId1, mId2, mId3, mId4); } - } else if (useIcons) { + } else if (mUseIcons) { final Context ctx = target.getContext(); - final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx); - final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx); - final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx); - final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx); - if (isRelative) { + final Drawable id1 = mI1 == null ? null : mI1.loadDrawable(ctx); + final Drawable id2 = mI2 == null ? null : mI2.loadDrawable(ctx); + final Drawable id3 = mI3 == null ? null : mI3.loadDrawable(ctx); + final Drawable id4 = mI4 == null ? null : mI4.loadDrawable(ctx); + if (mIsRelative) { target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4); } else { target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4); } } else { - if (isRelative) { - target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4); + if (mIsRelative) { + target.setCompoundDrawablesRelativeWithIntrinsicBounds(mD1, mD2, mD3, mD4); } else { - target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4); + target.setCompoundDrawablesWithIntrinsicBounds(mD1, mD2, mD3, mD4); } } } @@ -3112,34 +3109,34 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, ActionApplyParams params) { - final TextView target = root.findViewById(viewId); + final TextView target = root.findViewById(mViewId); if (target == null) return ACTION_NOOP; - TextViewDrawableAction copy = useIcons ? - new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) : - new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4); + TextViewDrawableAction copy = mUseIcons + ? new TextViewDrawableAction(mViewId, mIsRelative, mI1, mI2, mI3, mI4) + : new TextViewDrawableAction(mViewId, mIsRelative, mD1, mD2, mD3, mD4); // Load the drawables on the background thread. - copy.drawablesLoaded = true; + copy.mDrawablesLoaded = true; final Context ctx = target.getContext(); - if (useIcons) { - copy.id1 = i1 == null ? null : i1.loadDrawable(ctx); - copy.id2 = i2 == null ? null : i2.loadDrawable(ctx); - copy.id3 = i3 == null ? null : i3.loadDrawable(ctx); - copy.id4 = i4 == null ? null : i4.loadDrawable(ctx); + if (mUseIcons) { + copy.mId1 = mI1 == null ? null : mI1.loadDrawable(ctx); + copy.mId2 = mI2 == null ? null : mI2.loadDrawable(ctx); + copy.mId3 = mI3 == null ? null : mI3.loadDrawable(ctx); + copy.mId4 = mI4 == null ? null : mI4.loadDrawable(ctx); } else { - copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1); - copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2); - copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3); - copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4); + copy.mId1 = mD1 == 0 ? null : ctx.getDrawable(mD1); + copy.mId2 = mD2 == 0 ? null : ctx.getDrawable(mD2); + copy.mId3 = mD3 == 0 ? null : ctx.getDrawable(mD3); + copy.mId4 = mD4 == 0 ? null : ctx.getDrawable(mD4); } return copy; } @Override public boolean prefersAsyncApply() { - return useIcons; + return mUseIcons; } @Override @@ -3149,110 +3146,101 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - if (useIcons) { - visitIconUri(i1, visitor); - visitIconUri(i2, visitor); - visitIconUri(i3, visitor); - visitIconUri(i4, visitor); + if (mUseIcons) { + visitIconUri(mI1, visitor); + visitIconUri(mI2, visitor); + visitIconUri(mI3, visitor); + visitIconUri(mI4, visitor); } } - - boolean isRelative = false; - boolean useIcons = false; - int d1, d2, d3, d4; - Icon i1, i2, i3, i4; - - boolean drawablesLoaded = false; - Drawable id1, id2, id3, id4; } /** * Helper action to set text size on a TextView in any supported units. */ private static class TextViewSizeAction extends Action { + int mUnits; + float mSize; + TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) { - this.viewId = viewId; - this.units = units; - this.size = size; + this.mViewId = viewId; + this.mUnits = units; + this.mSize = size; } TextViewSizeAction(Parcel parcel) { - viewId = parcel.readInt(); - units = parcel.readInt(); - size = parcel.readFloat(); + mViewId = parcel.readInt(); + mUnits = parcel.readInt(); + mSize = parcel.readFloat(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(units); - dest.writeFloat(size); + dest.writeInt(mViewId); + dest.writeInt(mUnits); + dest.writeFloat(mSize); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final TextView target = root.findViewById(viewId); + final TextView target = root.findViewById(mViewId); if (target == null) return; - target.setTextSize(units, size); + target.setTextSize(mUnits, mSize); } @Override public int getActionTag() { return TEXT_VIEW_SIZE_ACTION_TAG; } - - int units; - float size; } /** * Helper action to set padding on a View. */ private static class ViewPaddingAction extends Action { + @Px int mLeft, mTop, mRight, mBottom; + public ViewPaddingAction(@IdRes int viewId, @Px int left, @Px int top, @Px int right, @Px int bottom) { - this.viewId = viewId; - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; + this.mViewId = viewId; + this.mLeft = left; + this.mTop = top; + this.mRight = right; + this.mBottom = bottom; } public ViewPaddingAction(Parcel parcel) { - viewId = parcel.readInt(); - left = parcel.readInt(); - top = parcel.readInt(); - right = parcel.readInt(); - bottom = parcel.readInt(); + mViewId = parcel.readInt(); + mLeft = parcel.readInt(); + mTop = parcel.readInt(); + mRight = parcel.readInt(); + mBottom = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeInt(left); - dest.writeInt(top); - dest.writeInt(right); - dest.writeInt(bottom); + dest.writeInt(mViewId); + dest.writeInt(mLeft); + dest.writeInt(mTop); + dest.writeInt(mRight); + dest.writeInt(mBottom); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; - target.setPadding(left, top, right, bottom); + target.setPadding(mLeft, mTop, mRight, mBottom); } @Override public int getActionTag() { return VIEW_PADDING_ACTION_TAG; } - - @Px int left, top, right, bottom; } /** * Helper action to set layout params on a View. */ private static class LayoutParamAction extends Action { - static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT; static final int LAYOUT_MARGIN_TOP = MARGIN_TOP; static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT; @@ -3274,7 +3262,7 @@ public class RemoteViews implements Parcelable, Filter { */ LayoutParamAction(@IdRes int viewId, int property, float value, @ComplexDimensionUnit int units) { - this.viewId = viewId; + this.mViewId = viewId; this.mProperty = property; this.mValueType = VALUE_TYPE_COMPLEX_UNIT; this.mValue = TypedValue.createComplexDimension(value, units); @@ -3289,21 +3277,21 @@ public class RemoteViews implements Parcelable, Filter { * {@link #VALUE_TYPE_RAW}. */ LayoutParamAction(@IdRes int viewId, int property, int value, @ValueType int valueType) { - this.viewId = viewId; + this.mViewId = viewId; this.mProperty = property; this.mValueType = valueType; this.mValue = value; } public LayoutParamAction(Parcel parcel) { - viewId = parcel.readInt(); + mViewId = parcel.readInt(); mProperty = parcel.readInt(); mValueType = parcel.readInt(); mValue = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mProperty); dest.writeInt(mValueType); dest.writeInt(mValue); @@ -3311,7 +3299,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) { return; } @@ -3438,55 +3426,53 @@ public class RemoteViews implements Parcelable, Filter { * Helper action to add a view tag with RemoteInputs. */ private static class SetRemoteInputsAction extends Action { + final Parcelable[] mRemoteInputs; public SetRemoteInputsAction(@IdRes int viewId, RemoteInput[] remoteInputs) { - this.viewId = viewId; - this.remoteInputs = remoteInputs; + this.mViewId = viewId; + this.mRemoteInputs = remoteInputs; } public SetRemoteInputsAction(Parcel parcel) { - viewId = parcel.readInt(); - remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR); + mViewId = parcel.readInt(); + mRemoteInputs = parcel.createTypedArray(RemoteInput.CREATOR); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); - dest.writeTypedArray(remoteInputs, flags); + dest.writeInt(mViewId); + dest.writeTypedArray(mRemoteInputs, flags); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; - target.setTagInternal(R.id.remote_input_tag, remoteInputs); + target.setTagInternal(R.id.remote_input_tag, mRemoteInputs); } @Override public int getActionTag() { return SET_REMOTE_INPUTS_ACTION_TAG; } - - final Parcelable[] remoteInputs; } /** * Helper action to override all textViewColors */ private static class OverrideTextColorsAction extends Action { - - private final int textColor; + private final int mTextColor; public OverrideTextColorsAction(int textColor) { - this.textColor = textColor; + this.mTextColor = textColor; } public OverrideTextColorsAction(Parcel parcel) { - textColor = parcel.readInt(); + mTextColor = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(textColor); + dest.writeInt(mTextColor); } @Override @@ -3499,7 +3485,7 @@ public class RemoteViews implements Parcelable, Filter { if (v instanceof TextView) { TextView textView = (TextView) v; textView.setText(ContrastColorUtil.clearColorSpans(textView.getText())); - textView.setTextColor(textColor); + textView.setTextColor(mTextColor); } if (v instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) v; @@ -3554,34 +3540,33 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetCompoundButtonCheckedAction extends Action { - private final boolean mChecked; SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) { - this.viewId = viewId; + this.mViewId = viewId; mChecked = checked; } SetCompoundButtonCheckedAction(Parcel in) { - viewId = in.readInt(); + mViewId = in.readInt(); mChecked = in.readBoolean(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeBoolean(mChecked); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (!(target instanceof CompoundButton)) { Log.w(LOG_TAG, "Cannot set checked to view " - + viewId + " because it is not a CompoundButton"); + + mViewId + " because it is not a CompoundButton"); return; } @@ -3605,33 +3590,32 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetRadioGroupCheckedAction extends Action { - @IdRes private final int mCheckedId; SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) { - this.viewId = viewId; + this.mViewId = viewId; mCheckedId = checkedId; } SetRadioGroupCheckedAction(Parcel in) { - viewId = in.readInt(); + mViewId = in.readInt(); mCheckedId = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mCheckedId); } @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; if (!(target instanceof RadioGroup)) { - Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup"); + Log.w(LOG_TAG, "Cannot check " + mViewId + " because it's not a RadioGroup"); return; } @@ -3670,35 +3654,34 @@ public class RemoteViews implements Parcelable, Filter { } private static class SetViewOutlinePreferredRadiusAction extends Action { - @ValueType private final int mValueType; private final int mValue; SetViewOutlinePreferredRadiusAction(@IdRes int viewId, int value, @ValueType int valueType) { - this.viewId = viewId; + this.mViewId = viewId; this.mValueType = valueType; this.mValue = value; } SetViewOutlinePreferredRadiusAction( @IdRes int viewId, float radius, @ComplexDimensionUnit int units) { - this.viewId = viewId; + this.mViewId = viewId; this.mValueType = VALUE_TYPE_COMPLEX_UNIT; this.mValue = TypedValue.createComplexDimension(radius, units); } SetViewOutlinePreferredRadiusAction(Parcel in) { - viewId = in.readInt(); + mViewId = in.readInt(); mValueType = in.readInt(); mValue = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(viewId); + dest.writeInt(mViewId); dest.writeInt(mValueType); dest.writeInt(mValue); } @@ -3706,7 +3689,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, ActionApplyParams params) throws ActionException { - final View target = root.findViewById(viewId); + final View target = root.findViewById(mViewId); if (target == null) return; try { @@ -3748,7 +3731,6 @@ public class RemoteViews implements Parcelable, Filter { * {@link #setViewOutlinePreferredRadius(int, float, int)}. */ public static final class RemoteViewOutlineProvider extends ViewOutlineProvider { - private final float mRadius; public RemoteViewOutlineProvider(float radius) { @@ -3821,7 +3803,8 @@ public class RemoteViews implements Parcelable, Filter { return mSizedRemoteViews != null; } - private @Nullable SizeF getIdealSize() { + @Nullable + private SizeF getIdealSize() { return mIdealSize; } @@ -4174,7 +4157,7 @@ public class RemoteViews implements Parcelable, Filter { default: throw new ActionException("Tag " + tag + " not found"); } - }; + } /** * Returns a deep copy of the RemoteViews object. The RemoteView may not be @@ -5907,7 +5890,7 @@ public class RemoteViews implements Parcelable, Filter { * Callback when the RemoteView has finished inflating, * but no actions have been applied yet. */ - default void onViewInflated(View v) {}; + default void onViewInflated(View v) {} void onViewApplied(View v); @@ -6288,7 +6271,6 @@ public class RemoteViews implements Parcelable, Filter { * @hide */ public class ActionApplyParams { - public InteractionHandler handler; public ColorResources colorResources; public Executor executor; @@ -6575,15 +6557,17 @@ public class RemoteViews implements Parcelable, Filter { /** * Parcelable.Creator that instantiates RemoteViews objects */ - public static final @android.annotation.NonNull Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() { - public RemoteViews createFromParcel(Parcel parcel) { - return new RemoteViews(parcel); - } + @NonNull + public static final Parcelable.Creator<RemoteViews> CREATOR = + new Parcelable.Creator<RemoteViews>() { + public RemoteViews createFromParcel(Parcel parcel) { + return new RemoteViews(parcel); + } - public RemoteViews[] newArray(int size) { - return new RemoteViews[size]; - } - }; + public RemoteViews[] newArray(int size) { + return new RemoteViews[size]; + } + }; /** * A representation of the view hierarchy. Only views which have a valid ID are added @@ -7284,7 +7268,8 @@ public class RemoteViews implements Parcelable, Filter { * Get the ID of the top-level view of the XML layout, if set using * {@link RemoteViews#RemoteViews(String, int, int)}. */ - public @IdRes int getViewId() { + @IdRes + public int getViewId() { return mViewId; } diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig new file mode 100644 index 000000000000..1b98806a0f01 --- /dev/null +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -0,0 +1,11 @@ +package: "com.android.window.flags" + +# Project link: https://gantry.corp.google.com/projects/android_platform_window_surfaces/changes + +flag { + namespace: "window_surfaces" + name: "surface_trusted_overlay" + description: "Whether to add trusted overlay flag on the SurfaceControl or the InputWindow" + is_fixed_read_only: true + bug: "292032926" +} diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 1bfb51cc4b82..6e836e077e44 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -837,25 +837,41 @@ public class InteractionJankMonitor { @WorkerThread private void updateProperties(DeviceConfig.Properties properties) { - mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, - DEFAULT_SAMPLING_INTERVAL); - mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY, - DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); - mTraceThresholdFrameTimeMillis = properties.getInt( - SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY, - DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); - // Never allow the debug overlay to be used on user builds - boolean debugOverlayEnabled = Build.IS_DEBUGGABLE && properties.getBoolean( - SETTINGS_DEBUG_OVERLAY_ENABLED_KEY, - DEFAULT_DEBUG_OVERLAY_ENABLED); - if (debugOverlayEnabled && mDebugOverlay == null) { - mDebugOverlay = new InteractionMonitorDebugOverlay(mLock, mDebugBgColor, mDebugYOffset); - } else if (!debugOverlayEnabled && mDebugOverlay != null) { - mDebugOverlay.dispose(); - mDebugOverlay = null; + for (String property : properties.getKeyset()) { + switch (property) { + case SETTINGS_SAMPLING_INTERVAL_KEY: + mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL); + break; + case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY: + mTraceThresholdMissedFrames = + properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); + break; + case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY: + mTraceThresholdFrameTimeMillis = + properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); + break; + case SETTINGS_ENABLED_KEY: + mEnabled = properties.getBoolean(property, DEFAULT_ENABLED); + break; + case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY: + // Never allow the debug overlay to be used on user builds + boolean debugOverlayEnabled = Build.IS_DEBUGGABLE + && properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED); + if (debugOverlayEnabled && mDebugOverlay == null) { + mDebugOverlay = new InteractionMonitorDebugOverlay( + mLock, mDebugBgColor, mDebugYOffset); + } else if (!debugOverlayEnabled && mDebugOverlay != null) { + mDebugOverlay.dispose(); + mDebugOverlay = null; + } + break; + default: + if (DEBUG) { + Log.d(TAG, "Got a change event for an unknown property: " + + property + " => " + properties.getString(property, "")); + } + } } - // The memory visibility is powered by the volatile field, mEnabled. - mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); } @VisibleForTesting diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java index 06e69f2d9859..fd435d0595c2 100644 --- a/core/java/com/android/internal/view/FloatingActionMode.java +++ b/core/java/com/android/internal/view/FloatingActionMode.java @@ -222,6 +222,7 @@ public final class FloatingActionMode extends ActionMode { private boolean isContentRectWithinBounds() { mContext.getDisplayNoVerify().getRealSize(mDisplaySize); mScreenRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); + mScreenRect.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]); return intersectsClosed(mContentRectOnScreen, mScreenRect) && intersectsClosed(mContentRectOnScreen, mViewRectOnScreen); diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 6c936801de18..473270229bdb 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -98,6 +98,7 @@ message SecureSettingsProto { // Settings for font scaling optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_magnification_gesture = 53 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Accessibility accessibility = 2; diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml index 8b6c90141bb7..27f8138ac5e3 100644 --- a/core/res/res/layout/autofill_save.xml +++ b/core/res/res/layout/autofill_save.xml @@ -60,10 +60,11 @@ android:gravity="center" android:textAppearance="@style/AutofillSaveUiTitle"> </TextView> - <LinearLayout + <FrameLayout android:id="@+id/autofill_save_custom_subtitle" android:layout_width="match_parent" android:layout_height="wrap_content" + android:minHeight="0dp" android:visibility="gone"/> </LinearLayout> diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml index 63fe47110c8b..2c6e0a7d3ef4 100644 --- a/core/res/res/layout/notification_expand_button.xml +++ b/core/res/res/layout/notification_expand_button.xml @@ -52,7 +52,7 @@ android:id="@+id/expand_button_icon" android:layout_width="@dimen/notification_expand_button_pill_height" android:layout_height="@dimen/notification_expand_button_pill_height" - android:padding="2dp" + android:padding="@dimen/notification_expand_button_icon_padding" android:scaleType="fitCenter" android:importantForAccessibility="no" /> diff --git a/core/res/res/values-watch/dimens_material.xml b/core/res/res/values-watch/dimens_material.xml index 2ab2d91058e2..8becb0880f97 100644 --- a/core/res/res/values-watch/dimens_material.xml +++ b/core/res/res/values-watch/dimens_material.xml @@ -47,11 +47,12 @@ <dimen name="progress_bar_height">24dp</dimen> <!-- Progress bar message dimens --> - <dimen name="message_progress_dialog_text_size">18sp</dimen> + <dimen name="message_progress_dialog_text_size">14sp</dimen> <dimen name="message_progress_dialog_bottom_padding">80px</dimen> <dimen name="message_progress_dialog_top_padding">0dp</dimen> <dimen name="message_progress_dialog_start_padding">0dp</dimen> <dimen name="message_progress_dialog_end_padding">0dp</dimen> + <item name="message_progress_dialog_letter_spacing" format="float" type="dimen">0.021</item> <!-- fallback for screen percentage widths --> <dimen name="screen_percentage_05">0dp</dimen> diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml index 8698e86ba5f9..f3e412dc948e 100644 --- a/core/res/res/values-watch/styles_material.xml +++ b/core/res/res/values-watch/styles_material.xml @@ -100,7 +100,9 @@ please see styles_device_defaults.xml. <style name="ProgressDialogMessage"> <item name="android:textAlignment">center</item> - <item name="textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> + <item name="android:fontFamily">google-sans-text</item> + <item name="android:letterSpacing">@dimen/message_progress_dialog_letter_spacing</item> + <item name="textColor">?attr/textColorPrimary</item> <item name="textSize">@dimen/message_progress_dialog_text_size</item> <item name="paddingBottom">@dimen/message_progress_dialog_bottom_padding</item> <item name="paddingEnd">@dimen/message_progress_dialog_end_padding</item> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3c296de780a2..7ef81abb8f0d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1151,6 +1151,14 @@ <!-- Allows activities to be launched on a long press on power during device setup. --> <bool name="config_allowStartActivityForLongPressOnPowerInSetup">false</bool> + <!-- Control the behavior when the user short presses the settings button. + 0 - Nothing + 1 - Launch notification panel + This needs to match the constants in + com/android/server/policy/PhoneWindowManager.java + --> + <integer name="config_shortPressOnSettingsBehavior">0</integer> + <!-- Control the behavior when the user short presses the power button. 0 - Nothing 1 - Go to sleep (doze) @@ -5394,6 +5402,7 @@ <item>1,1,1.0,.4,1</item> <item>1,1,1.0,.15,15</item> <item>0,0,0.7,0,1</item> + <item>0,0,0.83333,0,1</item> </string-array> <!-- The integer index of the selected option in config_udfps_touch_detection_options --> diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml index 98a5ff9c4a79..bc9ca3decec3 100644 --- a/core/res/res/values/config_device_idle.xml +++ b/core/res/res/values/config_device_idle.xml @@ -42,6 +42,15 @@ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FACTOR --> <item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item> + <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_INCREASE_LINEARLY --> + <bool name="device_idle_light_idle_increase_linearly">false</bool> + + <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS --> + <integer name="device_idle_light_idle_linear_increase_factor_ms">300000</integer> + + <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS --> + <integer name="device_idle_light_idle_flex_linear_increase_factor_ms">60000</integer> + <!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT --> <integer name="device_idle_light_max_idle_to_ms">900000</integer> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 71d696ea4554..3ba150bcdd44 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -73,7 +73,7 @@ CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY. If 0, the device always switch to the higher score SIM. If < 0, the network type and signal strength based auto switch is disabled. --> - <integer name="auto_data_switch_score_tolerance">-1</integer> + <integer name="auto_data_switch_score_tolerance">4000</integer> <java-symbol type="integer" name="auto_data_switch_score_tolerance" /> <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec @@ -219,4 +219,8 @@ <bool name="telephony_analytics_switch">true</bool> <java-symbol type="bool" name="telephony_analytics_switch" /> + <!-- Whether to enable modem on boot if behavior is not defined --> + <bool name="config_enable_cellular_on_boot_default">true</bool> + <java-symbol type="bool" name="config_enable_cellular_on_boot_default" /> + </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 49295fd235ae..96c4bf432c05 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -349,6 +349,9 @@ <!-- the height of the expand button pill --> <dimen name="notification_expand_button_pill_height">24dp</dimen> + <!-- the padding of the expand icon in the notification header --> + <dimen name="notification_expand_button_icon_padding">2dp</dimen> + <!-- Vertical margin for the headerless notification content, when content has 1 line --> <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) --> <dimen name="notification_headerless_margin_oneline">16dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 193f3adeedea..643f4b148d74 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1816,6 +1816,7 @@ <java-symbol type="integer" name="config_lidNavigationAccessibility" /> <java-symbol type="integer" name="config_lidOpenRotation" /> <java-symbol type="integer" name="config_longPressOnHomeBehavior" /> + <java-symbol type="integer" name="config_shortPressOnSettingsBehavior" /> <java-symbol type="layout" name="global_actions" /> <java-symbol type="layout" name="global_actions_item" /> <java-symbol type="layout" name="global_actions_silent_mode" /> @@ -4537,7 +4538,10 @@ <java-symbol type="integer" name="device_idle_light_idle_to_init_flex_ms" /> <java-symbol type="integer" name="device_idle_light_idle_to_max_flex_ms" /> <java-symbol type="integer" name="device_idle_light_idle_factor" /> + <java-symbol type="bool" name="device_idle_light_idle_increase_linearly" /> <java-symbol type="integer" name="device_idle_light_max_idle_to_ms" /> + <java-symbol type="integer" name="device_idle_light_idle_linear_increase_factor_ms" /> + <java-symbol type="integer" name="device_idle_light_idle_flex_linear_increase_factor_ms" /> <java-symbol type="integer" name="device_idle_light_idle_maintenance_min_budget_ms" /> <java-symbol type="integer" name="device_idle_light_idle_maintenance_max_budget_ms" /> <java-symbol type="integer" name="device_idle_min_light_maintenance_time_ms" /> diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp index 85d54e02d318..054d10c336e6 100644 --- a/core/tests/BroadcastRadioTests/Android.bp +++ b/core/tests/BroadcastRadioTests/Android.bp @@ -38,7 +38,7 @@ android_test { static_libs: [ "services.core", "androidx.test.rules", - "truth-prebuilt", + "truth", "testng", "mockito-target-extended", ], diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index a1952282dd0b..824f5910f96c 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -16,20 +16,20 @@ package com.android.server.broadcastradio.aidl; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.after; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.compat.CompatChanges; import android.graphics.Bitmap; @@ -81,8 +81,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { private static final int USER_ID_1 = 11; private static final int USER_ID_2 = 12; - private static final VerificationWithTimeout CALLBACK_TIMEOUT = - timeout(/* millis= */ 200); + private static final int CALLBACK_TIMEOUT_MS = 200; + private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS); private static final int SIGNAL_QUALITY = 90; private static final long AM_FM_FREQUENCY_SPACING = 500; private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100}; @@ -166,12 +166,12 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { @Before public void setup() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1); doReturn(true).when(() -> CompatChanges.isChangeEnabled( eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier(); + doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser()); - doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); mRadioModule = new RadioModule(mBroadcastRadioMock, AidlTestUtils.makeDefaultModuleProperties()); @@ -222,7 +222,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { return Result.OK; }).when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean()); - when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null); + doReturn(null).when(mBroadcastRadioMock).getImage(anyInt()); doAnswer(invocation -> { int configFlag = (int) invocation.getArguments()[0]; @@ -275,7 +275,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setConfiguration(FM_BAND_CONFIG); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onConfigurationChanged(FM_BAND_CONFIG); } @@ -446,26 +446,11 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].tune(initialSel); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(tuneInfo); } @Test - public void tune_forSystemUser() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM); - doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); - doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); - ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); - RadioManager.ProgramInfo tuneInfo = - AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); - openAidlClients(/* numClients= */ 1); - - mTunerSessions[0].tune(initialSel); - - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); - } - - @Test public void tune_withUnknownErrorFromHal_fails() throws Exception { openAidlClients(/* numClients= */ 1); ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); @@ -525,7 +510,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } @@ -604,7 +589,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(seekUpInfo); } @@ -638,6 +623,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { openAidlClients(/* numClients= */ 1); ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any()); doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); mTunerSessions[0].cancel(); @@ -686,8 +672,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { public void getImage_whenHalThrowsException_fails() throws Exception { openAidlClients(/* numClients= */ 1); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.getImage(anyInt())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock) + .getImage(anyInt()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getImage(/* id= */ 1); @@ -713,7 +699,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].startBackgroundScan(); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete(); + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) + .onBackgroundScanComplete(); } @Test @@ -905,7 +892,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>())); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) + .onProgramListUpdated(any()); } @Test @@ -1160,8 +1148,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1", "mockParam2", "mockValue2"); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.setParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock) + .setParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].setParameters(parametersSet); @@ -1186,8 +1174,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { openAidlClients(/* numClients= */ 1); List<String> parameterKeys = List.of("mockKey1", "mockKey2"); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.getParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock) + .getParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getParameters(parameterKeys); @@ -1198,7 +1186,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test - public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback() + public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback() throws Exception { openAidlClients(1); doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser()); @@ -1206,7 +1194,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo( AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY)); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java index fac9eaafe94c..3b9d7ba5de3e 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java @@ -16,22 +16,22 @@ package com.android.server.broadcastradio.hal2; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.after; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.graphics.Bitmap; import android.hardware.broadcastradio.V2_0.Constants; @@ -78,8 +78,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { private static final int USER_ID_1 = 11; private static final int USER_ID_2 = 12; - private static final VerificationWithTimeout CALLBACK_TIMEOUT = - timeout(/* millis= */ 200); + private static final int CALLBACK_TIMEOUT_MS = 200; + private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS); private static final int SIGNAL_QUALITY = 1; private static final long AM_FM_FREQUENCY_SPACING = 500; private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100}; @@ -113,7 +113,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { @Before public void setup() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1); + doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier(); doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser()); @@ -170,7 +170,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { return Result.OK; }).when(mHalTunerSessionMock).scan(anyBoolean(), anyBoolean()); - when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0)); + doReturn(new ArrayList<Byte>(0)).when(mBroadcastRadioMock).getImage(anyInt()); doAnswer(invocation -> { int configFlag = (int) invocation.getArguments()[0]; @@ -227,7 +227,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setConfiguration(FM_BAND_CONFIG); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onConfigurationChanged(FM_BAND_CONFIG); } @@ -379,7 +379,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].tune(initialSel); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(tuneInfo); } @@ -398,20 +398,6 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void tune_forSystemUser() throws Exception { - when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM); - doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); - doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); - ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); - RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); - openAidlClients(/* numClients= */ 1); - - mTunerSessions[0].tune(initialSel); - - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); - } - - @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq); @@ -455,7 +441,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } @@ -533,7 +519,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(seekUpInfo); } @@ -563,18 +549,6 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void cancel_forNonCurrentUser() throws Exception { - openAidlClients(/* numClients= */ 1); - ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); - mTunerSessions[0].tune(initialSel); - doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); - - mTunerSessions[0].cancel(); - - verify(mHalTunerSessionMock, never()).cancel(); - } - - @Test public void cancel_forNonCurrentUser_doesNotCancel() throws Exception { openAidlClients(/* numClients= */ 1); ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); @@ -627,8 +601,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { public void getImage_whenHalThrowsException_fails() throws Exception { openAidlClients(/* numClients= */ 1); String exceptionMessage = "HAL service died."; - when(mBroadcastRadioMock.getImage(anyInt())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock).getImage(anyInt()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getImage(/* id= */ 1); @@ -654,7 +627,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].startBackgroundScan(); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete(); + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) + .onBackgroundScanComplete(); } @Test @@ -845,8 +819,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1", "mockParam2", "mockValue2"); String exceptionMessage = "HAL service died."; - when(mHalTunerSessionMock.setParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock) + .setParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].setParameters(parametersSet); @@ -871,8 +845,8 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { openAidlClients(/* numClients= */ 1); List<String> parameterKeys = List.of("mockKey1", "mockKey2"); String exceptionMessage = "HAL service died."; - when(mHalTunerSessionMock.getParameters(any())) - .thenThrow(new RemoteException(exceptionMessage)); + doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock) + .getParameters(any()); RuntimeException thrown = assertThrows(RuntimeException.class, () -> { mTunerSessions[0].getParameters(parameterKeys); @@ -883,7 +857,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback() + public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback() throws Exception { openAidlClients(1); doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser()); @@ -891,7 +865,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mHalTunerCallback.onCurrentProgramInfoChanged(TestUtils.makeHalProgramInfo( TestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY)); - verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0)) .onCurrentProgramInfoChanged(any()); } diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp index 8c5d6d5a76a1..0e3bc6562edb 100644 --- a/core/tests/GameManagerTests/Android.bp +++ b/core/tests/GameManagerTests/Android.bp @@ -30,7 +30,7 @@ android_test { "frameworks-base-testutils", "junit", "platform-test-annotations", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp index 6f2366e32574..b631df1fcf57 100644 --- a/core/tests/PackageInstallerSessions/Android.bp +++ b/core/tests/PackageInstallerSessions/Android.bp @@ -35,7 +35,7 @@ android_test { "frameworks-base-testutils", "platform-test-annotations", "testng", - "truth-prebuilt", + "truth", ], libs: [ diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp index 52608351775c..1fb5f2c0789b 100644 --- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp +++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp @@ -18,7 +18,7 @@ android_test { "platform-test-annotations", "platformprotosnano", "statsdprotolite", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp index 2b34ee2646f3..7c1ac487bfdb 100644 --- a/core/tests/bugreports/Android.bp +++ b/core/tests/bugreports/Android.bp @@ -32,7 +32,7 @@ android_test { static_libs: [ "androidx.test.rules", "androidx.test.uiautomator_uiautomator", - "truth-prebuilt", + "truth", ], test_suites: ["general-tests"], sdk_version: "test_current", diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 04622fda75df..81dab0833af1 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -58,7 +58,7 @@ android_test { "androidx.test.uiautomator_uiautomator", "platform-test-annotations", "platform-compat-test-rules", - "truth-prebuilt", + "truth", "print-test-util-lib", "testng", "servicestests-utils", @@ -149,7 +149,7 @@ android_library { "androidx.test.runner", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: [ diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp index 3d04937c9195..5f6eaf96a846 100644 --- a/core/tests/hdmitests/Android.bp +++ b/core/tests/hdmitests/Android.bp @@ -30,7 +30,7 @@ android_test { "frameworks-base-testutils", "guava-android-testlib", "platform-test-annotations", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp index fde7c08715c7..2d778b1218d2 100644 --- a/core/tests/mockingcoretests/Android.bp +++ b/core/tests/mockingcoretests/Android.bp @@ -38,7 +38,7 @@ android_test { "androidx.test.ext.junit", "mockito-target-extended-minus-junit4", "platform-test-annotations", - "truth-prebuilt", + "truth", "testables", ], diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp index c74600bbab22..f81be494ad18 100644 --- a/core/tests/nfctests/Android.bp +++ b/core/tests/nfctests/Android.bp @@ -27,7 +27,7 @@ android_test { "androidx.test.ext.junit", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: [ "android.test.runner", diff --git a/core/tests/overlaytests/device_self_targeting/Android.bp b/core/tests/overlaytests/device_self_targeting/Android.bp index 063c5694ab5b..931eac515e31 100644 --- a/core/tests/overlaytests/device_self_targeting/Android.bp +++ b/core/tests/overlaytests/device_self_targeting/Android.bp @@ -30,7 +30,7 @@ android_test { "androidx.test.runner", "androidx.test.ext.junit", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], optimize: { diff --git a/core/tests/packagemonitortests/Android.bp b/core/tests/packagemonitortests/Android.bp index 7b5d7dff0a85..b08850e90d28 100644 --- a/core/tests/packagemonitortests/Android.bp +++ b/core/tests/packagemonitortests/Android.bp @@ -32,7 +32,7 @@ android_test { "compatibility-device-util-axt", "frameworks-base-testutils", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp index bc3dd810be8c..4e24cd5d91cb 100644 --- a/core/tests/privacytests/Android.bp +++ b/core/tests/privacytests/Android.bp @@ -14,7 +14,7 @@ android_test { "junit", "rappor-tests", "androidx.test.rules", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp index 3798da592cd5..580e73c331a1 100644 --- a/core/tests/utiltests/Android.bp +++ b/core/tests/utiltests/Android.bp @@ -33,7 +33,7 @@ android_test { "frameworks-base-testutils", "mockito-target-minus-junit4", "androidx.test.ext.junit", - "truth-prebuilt", + "truth", "servicestests-utils", ], diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp index 829409a36986..09608e9bf507 100644 --- a/core/tests/vibrator/Android.bp +++ b/core/tests/vibrator/Android.bp @@ -18,7 +18,7 @@ android_test { "androidx.test.runner", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", "testng", ], diff --git a/errorprone/Android.bp b/errorprone/Android.bp index ad08026622d1..c1d2235e3324 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -41,7 +41,7 @@ java_test_host { java_resource_dirs: ["tests/res"], java_resources: [":error_prone_android_framework_testdata"], static_libs: [ - "truth-prebuilt", + "truth", "kxml2-2.3.0", "compile-testing-prebuilt", "error_prone_android_framework_lib", diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index e7814cbd67e7..d1aceafc4e2c 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -114,6 +114,7 @@ public class Canvas extends BaseCanvas { throw new IllegalStateException("Immutable bitmap passed to Canvas constructor"); } throwIfCannotDraw(bitmap); + bitmap.setGainmap(null); mNativeCanvasWrapper = nInitRaster(bitmap.getNativeInstance()); mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation( this, mNativeCanvasWrapper); @@ -178,7 +179,7 @@ public class Canvas extends BaseCanvas { throw new IllegalStateException(); } throwIfCannotDraw(bitmap); - + bitmap.setGainmap(null); nSetBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance()); mDensity = bitmap.mDensity; } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index c55a781ce2a4..11278e84ceaa 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -43,6 +43,7 @@ import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; import javax.crypto.Mac; import javax.crypto.SecretKey; @@ -181,6 +182,8 @@ public class AndroidKeyStoreProvider extends Provider { spi = ((Mac) cryptoPrimitive).getCurrentSpi(); } else if (cryptoPrimitive instanceof Cipher) { spi = ((Cipher) cryptoPrimitive).getCurrentSpi(); + } else if (cryptoPrimitive instanceof KeyAgreement) { + spi = ((KeyAgreement) cryptoPrimitive).getCurrentSpi(); } else { throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive + ". Supported: Signature, Mac, Cipher"); diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp index b6e743a2b7e1..ed2ff2de245b 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp +++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp @@ -37,7 +37,7 @@ android_test { "androidx.test.rules", "androidx.test.ext.junit", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", "testables", "platform-test-annotations", ], diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java index f1ee8fa38485..a67821b7e819 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java @@ -318,7 +318,7 @@ public class BadgedImageView extends ConstraintLayout { /** * Animates the dot to the given scale, running the optional callback when the animation ends. */ - private void animateDotScale(float toScale, @Nullable Runnable after) { + public void animateDotScale(float toScale, @Nullable Runnable after) { mDotIsAnimating = true; // Don't restart the animation if we're already animating to the given value. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index df19757203eb..dc099d9abda4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -27,6 +27,7 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable import android.util.PathParser import android.view.LayoutInflater +import android.view.View.VISIBLE import android.widget.FrameLayout import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.R @@ -156,7 +157,9 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl fun setShowDot(show: Boolean) { showDot = show - overflowBtn?.updateDotVisibility(true /* animate */) + if (overflowBtn?.visibility == VISIBLE) { + overflowBtn?.updateDotVisibility(true /* animate */) + } } /** Creates the expanded view for bubbles showing in the stack view. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index c124b532b89d..2241c343a208 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1864,6 +1864,14 @@ public class BubbleStackView extends FrameLayout : GONE); } + private void updateOverflowDotVisibility(boolean expanding) { + if (mBubbleOverflow.showDot()) { + mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> { + mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE); + }); + } + } + // via BubbleData.Listener void updateBubble(Bubble bubble) { animateInFlyoutForBubble(bubble); @@ -2274,6 +2282,7 @@ public class BubbleStackView extends FrameLayout if (mIsExpanded && mExpandedBubble.getExpandedView() != null) { maybeShowManageEdu(); } + updateOverflowDotVisibility(true /* expanding */); } /* after */); int index; if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) { @@ -2405,11 +2414,15 @@ public class BubbleStackView extends FrameLayout // since we're about to animate collapsed. mExpandedAnimationController.notifyPreparingToCollapse(); + updateOverflowDotVisibility(false /* expanding */); final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack( mStackAnimationController .getStackPositionAlongNearestHorizontalEdge() /* collapseTo */, - () -> mBubbleContainer.setActiveController(mStackAnimationController)); + () -> { + mBubbleContainer.setActiveController(mStackAnimationController); + updateOverflowVisibility(); + }); final Runnable after = () -> { final BubbleViewProvider previouslySelected = mExpandedBubble; @@ -2424,7 +2437,6 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(), mExpandedBubble)); } - updateOverflowVisibility(); updateZOrder(); updateBadges(true /* setBadgeForCollapsedStack */); afterExpandedViewAnimation(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 4d7042bbb3d2..738c94e82a95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -34,6 +34,8 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.BadgedImageView; +import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleStackView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -63,6 +65,12 @@ public class ExpandedAnimationController /** Damping ratio for expand/collapse spring. */ private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f; + /** + * Damping ratio for the overflow bubble spring; this is less bouncy so it doesn't bounce behind + * the top bubble when it goes to disappear. + */ + private static final float DAMPING_RATIO_OVERFLOW_BOUNCY = 0.90f; + /** Stiffness for the expand/collapse path-following animation. */ private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 400; @@ -274,9 +282,14 @@ public class ExpandedAnimationController // of the screen where the bubble will be stacked. path.lineTo(stackedX, p.y); + // The overflow should animate to the collapse point, so 0 offset. + final boolean isOverflow = bubble instanceof BadgedImageView + && BubbleOverflow.KEY.equals(((BadgedImageView) bubble).getKey()); + final float offsetY = isOverflow + ? 0 + : Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx; // Then, draw a line down to the stack position. - path.lineTo(stackedX, mCollapsePoint.y - + Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx); + path.lineTo(stackedX, mCollapsePoint.y + offsetY); } // The lead bubble should be the bubble with the longest distance to travel when we're @@ -505,8 +518,12 @@ public class ExpandedAnimationController @Override SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) { + boolean isOverflow = (view instanceof BadgedImageView) + && BubbleOverflow.KEY.equals(((BadgedImageView) view).getKey()); return new SpringForce() - .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY) + .setDampingRatio(isOverflow + ? DAMPING_RATIO_OVERFLOW_BOUNCY + : DAMPING_RATIO_MEDIUM_LOW_BOUNCY) .setStiffness(SpringForce.STIFFNESS_LOW); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 5e42782431fd..e9344ffcce0c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -203,7 +203,7 @@ public class SystemWindows { + "SystemWindow:" + view); return null; } - return root.getFocusGrantToken(); + return root.getInputTransferToken(); } private class PerDisplay { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 11aa054676cb..5dfba5e7ff1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -494,13 +494,14 @@ public abstract class WMShellModule { ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, LaunchAdjacentController launchAdjacentController, + RecentsTransitionHandler recentsTransitionHandler, @ShellMainThread ShellExecutor mainExecutor ) { return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController, displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, - launchAdjacentController, mainExecutor); + launchAdjacentController, recentsTransitionHandler, mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 09ba4f79326e..412a5b5a6997 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -60,6 +60,8 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP import com.android.wm.shell.sysui.ShellCommandHandler @@ -68,7 +70,6 @@ import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions -import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.util.KtProtoLog import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration import com.android.wm.shell.windowdecor.MoveToDesktopAnimator @@ -93,6 +94,7 @@ class DesktopTasksController( ToggleResizeDesktopTaskTransitionHandler, private val desktopModeTaskRepository: DesktopModeTaskRepository, private val launchAdjacentController: LaunchAdjacentController, + private val recentsTransitionHandler: RecentsTransitionHandler, @ShellMainThread private val mainExecutor: ShellExecutor ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler { @@ -119,6 +121,8 @@ class DesktopTasksController( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width ) + private var recentsAnimationRunning = false + // This is public to avoid cyclic dependency; it is set by SplitScreenController lateinit var splitScreenController: SplitScreenController @@ -139,6 +143,19 @@ class DesktopTasksController( ) transitions.addHandler(this) desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor) + + recentsTransitionHandler.addTransitionStateListener( + object : RecentsTransitionStateListener { + override fun onAnimationStateChanged(running: Boolean) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: recents animation state changed running=%b", + running + ) + recentsAnimationRunning = running + } + } + ) } /** Show all tasks, that are part of the desktop, on top of launcher */ @@ -644,6 +661,10 @@ class DesktopTasksController( val triggerTask = request.triggerTask val shouldHandleRequest = when { + recentsAnimationRunning -> { + reason = "recents animation is running" + false + } // Only handle open or to front transitions request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> { reason = "transition type not handled (${request.type})" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index ead2f9cbd1ad..d31476c63890 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -54,6 +54,7 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.IResultReceiver; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -279,7 +280,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mDeathHandler = () -> { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.DeathRecipient: binder died", mInstanceId); - finish(mWillFinishToHome, false /* leaveHint */); + finish(mWillFinishToHome, false /* leaveHint */, null /* finishCb */); }; try { mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */); @@ -313,7 +314,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } if (mFinishCB != null) { - finishInner(toHome, false /* userLeave */); + finishInner(toHome, false /* userLeave */, null /* finishCb */); } else { cleanUp(); } @@ -670,7 +671,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // now and let it do its animation (since recents is going to be occluded). sendCancelWithSnapshots(); mExecutor.executeDelayed( - () -> finishInner(true /* toHome */, false /* userLeaveHint */), 0); + () -> finishInner(true /* toHome */, false /* userLeaveHint */, + null /* finishCb */), 0); return; } if (recentsOpening != null) { @@ -899,11 +901,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @Override @SuppressLint("NewApi") - public void finish(boolean toHome, boolean sendUserLeaveHint) { - mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint)); + public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) { + mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint, finishCb)); } - private void finishInner(boolean toHome, boolean sendUserLeaveHint) { + private void finishInner(boolean toHome, boolean sendUserLeaveHint, + IResultReceiver runnerFinishCb) { if (mFinishCB == null) { Slog.e(TAG, "Duplicate call to finish"); return; @@ -993,6 +996,16 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } cleanUp(); finishCB.onTransitionFinished(wct.isEmpty() ? null : wct); + if (runnerFinishCb != null) { + try { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: calling finish callback", + mInstanceId); + runnerFinishCb.send(0, null); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report transition finished", e); + } + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 83dc7fa5e869..e828eedc275c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; + import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; @@ -693,9 +694,19 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + Transitions.TransitionFinishCallback finishCB = wct -> { + mixed.mInFlightSubAnimations--; + if (mixed.mInFlightSubAnimations == 0) { + mActiveTransitions.remove(mixed); + finishCallback.onTransitionFinished(wct); + } + }; + + mixed.mInFlightSubAnimations++; boolean consumed = mRecentsHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCallback); + mixed.mTransition, info, startTransaction, finishTransaction, finishCB); if (!consumed) { + mixed.mInFlightSubAnimations--; return false; } if (mDesktopTasksController != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index e0635ac2e19a..de03f5826925 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -322,7 +322,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; mAnimations.remove(transition); - info.releaseAllSurfaces(); finishCallback.onTransitionFinished(null /* wct */); }; diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index 54f94986d90c..d09a90cd7dc7 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -45,7 +45,7 @@ android_test { "kotlinx-coroutines-core", "mockito-kotlin2", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", "testables", "platform-test-annotations", "servicestests-utils", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index dea161786da2..ebcb6407a6fd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -54,6 +54,8 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreef import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController @@ -101,11 +103,13 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var launchAdjacentController: LaunchAdjacentController @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration @Mock lateinit var splitScreenController: SplitScreenController + @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController private lateinit var shellInit: ShellInit private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener private val shellExecutor = TestShellExecutor() // Mock running tasks are registered here so we can get the list from mock shell task organizer @@ -126,6 +130,10 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.splitScreenController = splitScreenController shellInit.init() + + val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java) + verify(recentsTransitionHandler).addTransitionStateListener(captor.capture()) + recentsTransitionStateListener = captor.value } private fun createController(): DesktopTasksController { @@ -144,6 +152,7 @@ class DesktopTasksControllerTest : ShellTestCase() { mToggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, launchAdjacentController, + recentsTransitionHandler, shellExecutor ) } @@ -355,7 +364,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToDesktop_splitTaskExitsSplit() { - var task = setUpSplitScreenTask() + val task = setUpSplitScreenTask() controller.moveToDesktop(desktopModeWindowDecoration, task) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -367,7 +376,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToDesktop_fullscreenTaskDoesNotExitSplit() { - var task = setUpFullscreenTask() + val task = setUpFullscreenTask() controller.moveToDesktop(desktopModeWindowDecoration, task) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -666,6 +675,20 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun handleRequest_recentsAnimationRunning_returnNull() { + // Set up a visible freeform task so a fullscreen task should be converted to freeform + val freeformTask = setUpFreeformTask() + markTaskVisible(freeformTask) + + // Mark recents animation running + recentsTransitionStateListener.onAnimationStateChanged(true) + + // Open a fullscreen task, check that it does not result in a WCT with changes to it + val fullscreenTask = createFullscreenTask() + assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull() + } + + @Test fun stashDesktopApps_stateUpdates() { whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true) diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp index 64b53cbb5c5a..4dafd0aa6df4 100644 --- a/libs/dream/lowlight/tests/Android.bp +++ b/libs/dream/lowlight/tests/Android.bp @@ -34,7 +34,7 @@ android_test { "mockito-target-extended-minus-junit4", "platform-test-annotations", "testables", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/libs/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp index 7df546ae0ff6..80b501da1aa5 100644 --- a/libs/securebox/tests/Android.bp +++ b/libs/securebox/tests/Android.bp @@ -32,7 +32,7 @@ android_test { "platform-test-annotations", "testables", "testng", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 0319f32521c1..8800dc865a55 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -29,7 +29,6 @@ import android.database.Cursor; import android.media.audiofx.HapticGenerator; import android.net.Uri; import android.os.RemoteException; -import android.os.SystemProperties; import android.os.Trace; import android.os.VibrationEffect; import android.os.Vibrator; @@ -767,15 +766,4 @@ public class Ringtone { @RingtoneMedia int getEnabledMedia(); VibrationEffect getVibrationEffect(); } - - /** - * Switch for using the new ringtone implementation (RingtoneV1 vs RingtoneV2). This may be - * called from both system server and app-side sdk. - * - * @hide - */ - public static boolean useRingtoneV2() { - // TODO(b/293846645): chang eto new flagging infra - return SystemProperties.getBoolean("persist.audio.ringtone.use_v2", false); - } } diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 12db8c0824db..b5a9ae27263f 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -42,6 +42,7 @@ import android.os.FileUtils; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.vibrator.Flags; import android.os.vibrator.persistence.VibrationXmlParser; import android.provider.BaseColumns; import android.provider.MediaStore; @@ -607,7 +608,7 @@ public class RingtoneManager { Ringtone ringtone; Uri positionUri = getRingtoneUri(position); - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { mPreviousRingtone = new Ringtone.Builder( mContext, mMediaType, getDefaultAudioAttributes(mType)) .setUri(positionUri) @@ -953,7 +954,7 @@ public class RingtoneManager { * @return A {@link Ringtone} for the given URI, or null. */ public static Ringtone getRingtone(final Context context, Uri ringtoneUri) { - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { return new Ringtone.Builder( context, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(-1)) .setUri(ringtoneUri) @@ -970,7 +971,7 @@ public class RingtoneManager { @Nullable VolumeShaper.Configuration volumeShaperConfig, AudioAttributes audioAttributes) { // TODO: move caller(s) away from this method: inline the builder call. - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { return new Ringtone.Builder(context, Ringtone.MEDIA_SOUND, audioAttributes) .setUri(ringtoneUri) .setVolumeShaperConfig(volumeShaperConfig) diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java index 74f72767c3f8..b74b6a3dbac9 100644 --- a/media/java/android/media/RingtoneSelection.java +++ b/media/java/android/media/RingtoneSelection.java @@ -18,16 +18,23 @@ package android.media; import static java.util.Objects.requireNonNull; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.content.ContentProvider; import android.content.ContentResolver; import android.net.Uri; +import android.os.UserHandle; +import android.os.vibrator.Flags; import android.provider.MediaStore; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Immutable representation a desired ringtone, usually originating from a user preference. @@ -46,7 +53,7 @@ import java.lang.annotation.RetentionPolicy; * to be internally consistent and reflect effective values - with the exception of not verifying * the actual URI content. For example, loading a selection Uri that sets a sound source to * {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class - * instead returning {@link #SOUND_SOURCE_DEFAULT} from {@link #getSoundSource}. + * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}. * * <h2>Storing preferences</h2> * @@ -57,6 +64,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @TestApi +@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED) public final class RingtoneSelection { /** @@ -70,7 +78,7 @@ public final class RingtoneSelection { * The sound source is not explicitly specified, so it can follow default behavior for its * context. */ - public static final int SOUND_SOURCE_DEFAULT = 0; + public static final int SOUND_SOURCE_UNSPECIFIED = 0; /** * Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker. @@ -83,15 +91,25 @@ public final class RingtoneSelection { public static final int SOUND_SOURCE_URI = 2; /** + * The sound should explicitly use the system default. + * + * <p>This value isn't valid within the system default itself. + */ + public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; + + // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT. + + /** * Directive for how to make sound. * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "SOUND_SOURCE_", value = { SOUND_SOURCE_UNKNOWN, - SOUND_SOURCE_DEFAULT, + SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_OFF, SOUND_SOURCE_URI, + SOUND_SOURCE_SYSTEM_DEFAULT, }) public @interface SoundSource {} @@ -106,9 +124,9 @@ public final class RingtoneSelection { /** * Vibration source is not explicitly specified. If vibration is enabled, this will use the * first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL}, - * {@link #VIBRATION_SOURCE_APPLICATION_PROVIDED}, or system default vibration. + * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. */ - public static final int VIBRATION_SOURCE_DEFAULT = 0; + public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; /** Specifies that vibration is explicitly disabled for this ringtone. */ public static final int VIBRATION_SOURCE_OFF = 1; @@ -117,22 +135,31 @@ public final class RingtoneSelection { public static final int VIBRATION_SOURCE_URI = 2; /** + * The vibration should explicitly use the system default. + * + * <p>This value isn't valid within the system default itself. + */ + public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; + + /** * Specifies that vibration should use the vibration provided by the application. This is * typically the application's own default for the use-case, provided via * {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration * effect saved on the notification channel. * * <p>If no vibration is specified by the application, this value behaves if the source was - * {@link #VIBRATION_SOURCE_DEFAULT}. + * {@link #VIBRATION_SOURCE_UNSPECIFIED}. + * + * <p>This value isn't valid within the system default. */ - public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3; + public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; /** * Specifies that vibration should use haptic audio channels from the * sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified - * by {@link #VIBRATION_SOURCE_DEFAULT}. + * by {@link #VIBRATION_SOURCE_UNSPECIFIED}. */ - // Numeric gap from VIBRATION_SOURCE_APPLICATION_PROVIDED in case we want other common elements. + // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements. public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; /** @@ -151,10 +178,10 @@ public final class RingtoneSelection { @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "VIBRATION_SOURCE_", value = { VIBRATION_SOURCE_UNKNOWN, - VIBRATION_SOURCE_DEFAULT, + VIBRATION_SOURCE_UNSPECIFIED, VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_URI, - VIBRATION_SOURCE_APPLICATION_PROVIDED, + VIBRATION_SOURCE_APPLICATION_DEFAULT, VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR, }) @@ -162,9 +189,12 @@ public final class RingtoneSelection { /** * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri - * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF}. - * This behavior is particularly suited to loading values from older settings that may contain - * a raw sound Uri or null for silent. + * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF}, + * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning + * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}. + * + * <p>This behavior is particularly suited to loading values from older settings that may + * contain a raw sound Uri or null for silent. * * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false. */ @@ -173,7 +203,8 @@ public final class RingtoneSelection { /** * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration * Uri for the returned {@link RingtoneSelection}, with null meaning - * {@link #VIBRATION_SOURCE_OFF}. + * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs + * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. * * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false. */ @@ -182,7 +213,9 @@ public final class RingtoneSelection { /** * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid * value. Null or an invalid values will revert to default behavior correspnoding to - * {@link #DEFAULT_SELECTION_URI_STRING}. + * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs + * ({@link RingtoneManager#getDefaultUri}) will set both + * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. * * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false, * which include {@code null}. @@ -218,10 +251,11 @@ public final class RingtoneSelection { /* Common param values */ private static final String SOURCE_OFF_STRING = "off"; + private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys"; /* Vibration source param values. */ private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac"; - private static final String VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING = "app"; + private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app"; private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg"; @Nullable @@ -236,53 +270,45 @@ public final class RingtoneSelection { private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource, @Nullable Uri vibrationUri, @VibrationSource int vibrationSource) { - // Enforce guarantees on the source values: revert to unset if they depend on something - // that's not set. - switch (soundSource) { - case SOUND_SOURCE_URI: - case SOUND_SOURCE_UNKNOWN: // Allow unknown to revert to URI before default. - mSoundSource = soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_DEFAULT; - break; - default: - mSoundSource = soundSource; - break; - } - switch (vibrationSource) { - case VIBRATION_SOURCE_AUDIO_CHANNEL: - case VIBRATION_SOURCE_HAPTIC_GENERATOR: - mVibrationSource = soundUri != null ? vibrationSource : VIBRATION_SOURCE_DEFAULT; - break; - case VIBRATION_SOURCE_URI: - case VIBRATION_SOURCE_UNKNOWN: // Allow unknown to revert to URI. - mVibrationSource = - vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_DEFAULT; - break; - default: - mVibrationSource = vibrationSource; - break; - } + // Enforce guarantees on the source values: revert to unspecified if they depend on + // something that's not set. + // + // The non-public "unknown" value can't appear in a getter result, it's just a reserved + // "null" value and should be treated the same as an unrecognized value. This can be seen + // in Uri parsing. For this and other unrecognized values, we either revert them to the URI + // source, if a Uri was included, or the "unspecified" source otherwise. This can be + // seen in action in the Uri parsing. + // + // The "unspecified" source is a public value meaning that there is no specific + // behavior indicated, and the defaults and fallbacks should be applied. For example, an + // vibration source value of "system default" means to explicitly use the system default + // vibration. However, an "unspecified" vibration source will first see if audio coupled + // or application-default vibrations are available. + mSoundSource = switch (soundSource) { + // Supported explicit values that don't have a Uri. + case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT -> + soundSource; + // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to + // unspecified. + default -> + soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED; + }; + mVibrationSource = switch (vibrationSource) { + // Enforce vibration sources that require a sound Uri. + case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR -> + soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED; + // Supported explicit values that don't rely on any Uri. + case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED, + VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT -> + vibrationSource; + // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to + // unspecified. + default -> + vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED; + }; // Clear Uri values if they're un-used by the source. - switch (mSoundSource) { - case SOUND_SOURCE_OFF: - mSoundUri = null; - break; - default: - // Unset case isn't handled here: the defaulting behavior is left to the player. - mSoundUri = soundUri; - break; - } - switch (mVibrationSource) { - case VIBRATION_SOURCE_OFF: - case VIBRATION_SOURCE_APPLICATION_PROVIDED: - case VIBRATION_SOURCE_AUDIO_CHANNEL: - case VIBRATION_SOURCE_HAPTIC_GENERATOR: - mVibrationUri = null; - break; - default: - // Unset case isn't handled here: the defaulting behavior is left to the player. - mVibrationUri = vibrationUri; - break; - } + mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null; + mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null; } /** @@ -360,18 +386,83 @@ public final class RingtoneSelection { } // Any URI content://media/ringtone return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) - && MediaStore.AUTHORITY.equals(uri.getAuthority()) + && MediaStore.AUTHORITY.equals( + ContentProvider.getAuthorityWithoutUserId(uri.getAuthority())) && MEDIA_URI_RINGTONE_PATH.equals(uri.getPath()); } + /** + * Strip the specified userId from internal Uris. Non-stripped userIds will typically be + * for work profiles referencing system ringtones from the host user. + * + * This is only for use in RingtoneManager. + * @hide + */ + @VisibleForTesting + public RingtoneSelection getWithoutUserId(int userIdToStrip) { + if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) { + return this; + } + // Ok if uri is null. We only replace explicit references to the specified (current) userId. + int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL); + int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL); + boolean needToChangeSound = + soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip; + boolean needToChangeVibration = + vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip; + + // Anything to do? + if (!needToChangeSound && !needToChangeVibration) { + return this; + } + + RingtoneSelection.Builder updated = new Builder(this); + // The relevant uris can't be null, because they contain userIdToStrip. + if (needToChangeSound) { + // mSoundUri is not null, so the result of getUriWithoutUserId won't be null. + updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri)); + } + if (needToChangeVibration) { + updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri)); + } + return updated.build(); + } + + @Override + public String toString() { + return toUri().toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof RingtoneSelection other)) { + return false; + } + return this.mSoundSource == other.mSoundSource + && this.mVibrationSource == other.mVibrationSource + && Objects.equals(this.mSoundUri, other.mSoundUri) + && Objects.equals(this.mVibrationUri, other.mVibrationUri); + } + + @Override + public int hashCode() { + return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri); + } /** * Converts a Uri into a RingtoneSelection. * - * <p>Null values and Uris that {@link #isRingtoneSelectionUri(Uri)} returns false will be - * treated according to the behaviour specified by the {@code unrecognizedValueBehavior} - * parameter. + * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for + * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated + * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter. + * + * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1, + * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and + * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}. * * @param uri The Uri to convert, potentially null. * @param unrecognizedValueBehavior indicates how to treat values for which @@ -384,21 +475,35 @@ public final class RingtoneSelection { if (isRingtoneSelectionUri(uri)) { return parseRingtoneSelectionUri(uri); } + // Old symbolic default URIs map to the sources suggested by the unrecognized behavior. + // It doesn't always map to both sources because the app may have its own default behavior + // to apply, so non-primary sources are left as unspecified, which will revert to the + // system default in the absence of an app default. + boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0; RingtoneSelection.Builder builder = new RingtoneSelection.Builder(); switch (unrecognizedValueBehavior) { case FROM_URI_RINGTONE_SELECTION_ONLY: - // Always return use-defaults for unrecognized ringtone selection Uris. + if (isDefaultUri) { + builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT); + builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT); + } + // Always return unspecified (defaults) for unrecognized ringtone selection Uris. return builder.build(); case FROM_URI_RINGTONE_SELECTION_OR_SOUND: if (uri == null) { return builder.setSoundSource(SOUND_SOURCE_OFF).build(); + } else if (isDefaultUri) { + return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build(); } else { return builder.setSoundSource(uri).build(); } case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION: if (uri == null) { return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build(); + } else if (isDefaultUri) { + return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build(); } else { + // Unlike sound, there's no legacy settings alias uri for the default. return builder.setVibrationSource(uri).build(); } default: @@ -407,7 +512,12 @@ public final class RingtoneSelection { } } - /** Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}. */ + /** + * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}. + * As a special case to be more compatible, if the RingtoneSelection has a userId specified in + * the authority, then this is pushed down into the sound and vibration Uri, as the selection + * itself is agnostic. + */ @NonNull private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) { RingtoneSelection.Builder builder = new RingtoneSelection.Builder(); @@ -416,19 +526,39 @@ public final class RingtoneSelection { uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE)); Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI); Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI); + + // Add userId if necessary. This won't override an existing one in the sound/vib Uris. + int userIdFromAuthority = ContentProvider.getUserIdFromAuthority( + uri.getAuthority(), UserHandle.USER_NULL); + if (userIdFromAuthority != UserHandle.USER_NULL) { + // Won't override existing user id. + if (soundUri != null) { + soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority); + } + if (vibrationUri != null) { + vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority); + } + } + + // Set sound uri if present, but map system default Uris to the system default source. if (soundUri != null) { - builder.setSoundSource(soundUri); + if (RingtoneManager.getDefaultType(uri) >= 0) { + builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT); + } else { + builder.setSoundSource(soundUri); + } } if (vibrationUri != null) { builder.setVibrationSource(vibrationUri); } + // Don't set the source if there's a URI and the source is default, because that will // override the Uri source set above. In effect, we prioritise "explicit" sources over // an implicit Uri source - except for "default", which isn't really explicit. - if (soundSource != SOUND_SOURCE_DEFAULT || soundUri == null) { + if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) { builder.setSoundSource(soundSource); } - if (vibrationSource != VIBRATION_SOURCE_DEFAULT || vibrationUri == null) { + if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) { builder.setVibrationSource(vibrationSource); } return builder.build(); @@ -450,26 +580,28 @@ public final class RingtoneSelection { */ @Nullable private static String soundSourceToString(@SoundSource int soundSource) { - switch (soundSource) { - case SOUND_SOURCE_OFF: return SOURCE_OFF_STRING; - default: return null; - } + return switch (soundSource) { + case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING; + case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING; + default -> null; + }; } /** * Returns the sound source int corresponding to the query string value. Returns * {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and - * {@link #SOUND_SOURCE_DEFAULT} if the value is {@code null} (not in the Uri). + * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri). */ @SoundSource private static int stringToSoundSource(@Nullable String soundSource) { if (soundSource == null) { - return SOUND_SOURCE_DEFAULT; - } - switch (soundSource) { - case SOURCE_OFF_STRING: return SOUND_SOURCE_OFF; - default: return SOUND_SOURCE_UNKNOWN; + return SOUND_SOURCE_UNSPECIFIED; } + return switch (soundSource) { + case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF; + case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT; + default -> SOUND_SOURCE_UNKNOWN; + }; } /** @@ -478,30 +610,31 @@ public final class RingtoneSelection { */ @Nullable private static String vibrationSourceToString(@VibrationSource int vibrationSource) { - switch (vibrationSource) { - case VIBRATION_SOURCE_OFF: return SOURCE_OFF_STRING; - case VIBRATION_SOURCE_AUDIO_CHANNEL: return VIBRATION_SOURCE_AUDIO_CHANNEL_STRING; - case VIBRATION_SOURCE_HAPTIC_GENERATOR: - return VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING; - case VIBRATION_SOURCE_APPLICATION_PROVIDED: - return VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING; - default: return null; - } + return switch (vibrationSource) { + case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING; + case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING; + case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING; + case VIBRATION_SOURCE_APPLICATION_DEFAULT -> + VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING; + case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING; + default -> null; + }; } @VibrationSource private static int stringToVibrationSource(@Nullable String vibrationSource) { if (vibrationSource == null) { - return VIBRATION_SOURCE_DEFAULT; - } - switch (vibrationSource) { - case SOURCE_OFF_STRING: return VIBRATION_SOURCE_OFF; - case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING: return VIBRATION_SOURCE_AUDIO_CHANNEL; - case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING: return VIBRATION_SOURCE_HAPTIC_GENERATOR; - case VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING: - return VIBRATION_SOURCE_APPLICATION_PROVIDED; - default: return VIBRATION_SOURCE_UNKNOWN; + return VIBRATION_SOURCE_UNSPECIFIED; } + return switch (vibrationSource) { + case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF; + case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT; + case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL; + case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR; + case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING -> + VIBRATION_SOURCE_APPLICATION_DEFAULT; + default -> VIBRATION_SOURCE_UNKNOWN; + }; } /** @@ -512,12 +645,13 @@ public final class RingtoneSelection { public static final class Builder { private Uri mSoundUri; private Uri mVibrationUri; - @SoundSource private int mSoundSource = SOUND_SOURCE_DEFAULT; - @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_DEFAULT; + @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED; + @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED; /** * Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its - * sound and vibration source unset, which means they would fall back to system defaults. + * sound and vibration source unspecified, which means they would fall back to app/system + * defaults. */ public Builder() {} @@ -559,7 +693,9 @@ public final class RingtoneSelection { */ @NonNull public Builder setSoundSource(@NonNull Uri soundUri) { - mSoundUri = requireNonNull(soundUri); + // getCanonicalUri shouldn't return null. If it somehow did, then the + // RingtoneSelection constructor will revert this to unspecified. + mSoundUri = requireNonNull(soundUri).getCanonicalUri(); mSoundSource = SOUND_SOURCE_URI; return this; } @@ -587,7 +723,9 @@ public final class RingtoneSelection { */ @NonNull public Builder setVibrationSource(@NonNull Uri vibrationUri) { - mVibrationUri = requireNonNull(vibrationUri); + // getCanonicalUri shouldn't return null. If it somehow did, then the + // RingtoneSelection constructor will revert this to unspecified. + mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri(); mVibrationSource = VIBRATION_SOURCE_URI; return this; } diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp index ca20225e8885..bdd7afeb2f65 100644 --- a/media/tests/MediaFrameworkTest/Android.bp +++ b/media/tests/MediaFrameworkTest/Android.bp @@ -22,7 +22,7 @@ android_test { "android-ex-camera2", "testables", "testng", - "truth-prebuilt", + "truth", ], jni_libs: [ "libdexmakerjvmtiagent", diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp index 4cccf8972798..61b18c88e734 100644 --- a/media/tests/MediaRouter/Android.bp +++ b/media/tests/MediaRouter/Android.bp @@ -24,7 +24,7 @@ android_test { "compatibility-device-util-axt", "mockito-target-minus-junit4", "testng", - "truth-prebuilt", + "truth", ], test_suites: ["general-tests"], platform_apis: true, diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp index e313c46d1973..48cd8b69ade8 100644 --- a/media/tests/projection/Android.bp +++ b/media/tests/projection/Android.bp @@ -30,7 +30,7 @@ android_test { "platform-test-annotations", "testng", "testables", - "truth-prebuilt", + "truth", "platform-compat-test-rules", ], diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index ba88484518aa..2318bb95dabb 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -18,21 +18,23 @@ package com.android.credentialmanager.autofill import android.app.assist.AssistStructure import android.content.Context -import android.credentials.GetCredentialRequest import android.credentials.CredentialManager -import android.credentials.GetCandidateCredentialsResponse import android.credentials.CredentialOption import android.credentials.GetCandidateCredentialsException +import android.credentials.GetCandidateCredentialsResponse +import android.credentials.GetCredentialRequest import android.os.Bundle import android.os.CancellationSignal import android.os.OutcomeReceiver -import android.service.autofill.FillRequest import android.service.autofill.AutofillService -import android.service.autofill.FillResponse import android.service.autofill.FillCallback -import android.service.autofill.SaveRequest +import android.service.autofill.FillRequest +import android.service.autofill.FillResponse import android.service.autofill.SaveCallback +import android.service.autofill.SaveRequest +import android.service.credentials.CredentialProviderService import android.util.Log +import android.view.autofill.AutofillId import org.json.JSONObject import java.util.concurrent.Executors @@ -129,27 +131,31 @@ class CredentialAutofillService : AutofillService() { } private fun traverseNode( - viewNode: AssistStructure.ViewNode?, + viewNode: AssistStructure.ViewNode, cmRequests: MutableList<CredentialOption> ) { - val options = getCredentialOptionsFromViewNode(viewNode) - cmRequests.addAll(options) + viewNode.autofillId?.let { + val options = getCredentialOptionsFromViewNode(viewNode, it) + cmRequests.addAll(options) + } - val children: List<AssistStructure.ViewNode>? = - viewNode?.run { + val children: List<AssistStructure.ViewNode> = + viewNode.run { (0 until childCount).map { getChildAt(it) } } - children?.forEach { childNode: AssistStructure.ViewNode -> + children.forEach { childNode: AssistStructure.ViewNode -> traverseNode(childNode, cmRequests) } } - private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?): - List<CredentialOption> { + private fun getCredentialOptionsFromViewNode( + viewNode: AssistStructure.ViewNode, + autofillId: AutofillId + ): List<CredentialOption> { // TODO(b/293945193) Replace with isCredential check from viewNode val credentialHints: MutableList<String> = mutableListOf() - if (viewNode != null && viewNode.autofillHints != null) { + if (viewNode.autofillHints != null) { for (hint in viewNode.autofillHints!!) { if (hint.startsWith(CRED_HINT_PREFIX)) { credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX)) @@ -159,12 +165,14 @@ class CredentialAutofillService : AutofillService() { val credentialOptions: MutableList<CredentialOption> = mutableListOf() for (credentialHint in credentialHints) { - convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) } + convertJsonToCredentialOption(credentialHint, autofillId) + .let { credentialOptions.addAll(it) } } return credentialOptions } - private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> { + private fun convertJsonToCredentialOption(jsonString: String, autofillId: AutofillId): + List<CredentialOption> { // TODO(b/302000646) Move this logic to jetpack so that is consistent // with building the json val credentialOptions: MutableList<CredentialOption> = mutableListOf() @@ -173,11 +181,14 @@ class CredentialAutofillService : AutofillService() { val options = json.getJSONArray(CRED_OPTIONS_KEY) for (i in 0 until options.length()) { val option = options.getJSONObject(i) - + val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)) + candidateBundle.putParcelable( + CredentialProviderService.EXTRA_AUTOFILL_ID, + autofillId) credentialOptions.add(CredentialOption( option.getString(TYPE_KEY), convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)), - convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)), + candidateBundle, option.getBoolean(SYS_PROVIDER_REQ_KEY), )) } diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp index 633f18610921..86c62ef299e4 100644 --- a/packages/ExternalStorageProvider/tests/Android.bp +++ b/packages/ExternalStorageProvider/tests/Android.bp @@ -25,7 +25,7 @@ android_test { static_libs: [ "androidx.test.rules", "mockito-target", - "truth-prebuilt", + "truth", ], certificate: "platform", diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp index 64b4c54e74ad..61a82701d155 100644 --- a/packages/FusedLocation/Android.bp +++ b/packages/FusedLocation/Android.bp @@ -47,7 +47,7 @@ android_test { test_config: "test/AndroidTest.xml", srcs: [ "test/src/**/*.java", - "src/**/*.java", // include real sources because we're forced to test this directly + "src/**/*.java", // include real sources because we're forced to test this directly ], libs: [ "android.test.base", @@ -60,9 +60,9 @@ android_test { "androidx.test.ext.junit", "androidx.test.ext.truth", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], platform_apis: true, certificate: "platform", - test_suites: ["device-tests"] + test_suites: ["device-tests"], } diff --git a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm index ad3199ff2dc8..d9338b1fdab7 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm @@ -337,7 +337,7 @@ key M { label: 'M' base: 'm' shift, capslock: 'M' - shift+capslock: 'n' + shift+capslock: 'm' } key COMMA { diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm index 67449220b189..fc53cbad91db 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm @@ -12,9 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - - -type FULL +type OVERLAY ### Basic QWERTY keys ### diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml index 88bb30b34ede..7f23f747090b 100644 --- a/packages/InputDevices/res/xml/keyboard_layouts.xml +++ b/packages/InputDevices/res/xml/keyboard_layouts.xml @@ -227,13 +227,6 @@ android:label="@string/keyboard_layout_turkish" android:keyboardLayout="@raw/keyboard_layout_turkish" android:keyboardLocale="tr-Latn" - android:keyboardLayoutType="qwerty" /> - - <keyboard-layout - android:name="keyboard_layout_turkish" - android:label="@string/keyboard_layout_turkish" - android:keyboardLayout="@raw/keyboard_layout_turkish" - android:keyboardLocale="tr-Latn" android:keyboardLayoutType="turkish_q" /> <keyboard-layout diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt index d0a61882593c..0757df347d68 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt @@ -29,8 +29,8 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag @@ -46,6 +46,7 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme fun SettingsTextFieldPassword( value: String, label: String, + enabled: Boolean = true, onTextChange: (String) -> Unit, ) { var visibility by remember { mutableStateOf(false) } @@ -60,6 +61,7 @@ fun SettingsTextFieldPassword( keyboardType = KeyboardType.Password, imeAction = ImeAction.Send ), + enabled = enabled, trailingIcon = { Icon( imageVector = if (visibility) Icons.Outlined.VisibilityOff diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp index 4031cd7f7a6f..639d1a7a7f55 100644 --- a/packages/SettingsLib/Spa/testutils/Android.bp +++ b/packages/SettingsLib/Spa/testutils/Android.bp @@ -31,7 +31,7 @@ android_library { "androidx.compose.ui_ui-test-manifest", "androidx.lifecycle_lifecycle-runtime-testing", "mockito-kotlin2", - "truth-prebuilt", + "truth", ], kotlincflags: [ "-Xjvm-default=all", diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java index 815293e9c2a8..09f34b3c5a5d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java +++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/SupervisedDeviceActionDisabledByAdminController.java @@ -16,17 +16,20 @@ package com.android.settingslib.enterprise; + import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.provider.Settings; -import android.util.Log; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.settingslib.RestrictedLockUtils; -import org.jetbrains.annotations.Nullable; final class SupervisedDeviceActionDisabledByAdminController extends BaseActionDisabledByAdminController { @@ -57,8 +60,13 @@ final class SupervisedDeviceActionDisabledByAdminController @Nullable @Override - public DialogInterface.OnClickListener getPositiveButtonListener(Context context, - RestrictedLockUtils.EnforcedAdmin enforcedAdmin) { + public DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context, + @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) { + if (enforcedAdmin.component == null + || TextUtils.isEmpty(enforcedAdmin.component.getPackageName())) { + return null; + } + final Intent intent = new Intent(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING) .setData(new Uri.Builder() .scheme("policy") @@ -72,7 +80,6 @@ final class SupervisedDeviceActionDisabledByAdminController return null; } return (dialog, which) -> { - Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component); context.startActivity(intent); }; } diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index b03c43cbdee5..4b4caf5a620e 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -52,7 +52,7 @@ android_test { "flag-junit", "mockito-target-minus-junit4", "platform-test-annotations", - "truth-prebuilt", + "truth", "SettingsLibDeviceStateRotationLock", "SettingsLibSettingsSpinner", "SettingsLibUsageProgressBarPreference", diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp index dd9cb9cf7abe..2d875cf7244d 100644 --- a/packages/SettingsLib/tests/robotests/Android.bp +++ b/packages/SettingsLib/tests/robotests/Android.bp @@ -96,6 +96,6 @@ java_library { libs: [ "Robolectric_all-target_upstream", "mockito-robolectric-prebuilt", - "truth-prebuilt", + "truth", ], } diff --git a/packages/SettingsLib/tests/unit/Android.bp b/packages/SettingsLib/tests/unit/Android.bp index 19ab1c69e98c..6d6e2ff8e59b 100644 --- a/packages/SettingsLib/tests/unit/Android.bp +++ b/packages/SettingsLib/tests/unit/Android.bp @@ -31,6 +31,6 @@ android_test { "SettingsLib", "androidx.test.ext.junit", "androidx.test.runner", - "truth-prebuilt", + "truth", ], } diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 92ebe09fa441..f4ca260d4d89 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -64,7 +64,7 @@ android_test { "SettingsLibDeviceStateRotationLock", "SettingsLibDisplayUtils", "platform-test-annotations", - "truth-prebuilt", + "truth", ], libs: [ "android.test.base", diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 91d2d1bb58e5..ba4ad365e1b3 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -218,6 +218,7 @@ public class SecureSettings { Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE, Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, Settings.Secure.NOTIFICATION_BUBBLES, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index bec144766438..19fde758da5d 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -308,6 +308,10 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE, + new InclusiveIntegerRangeValidator( + Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE, + Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL)); VALIDATORS.put( Secure.ACCESSIBILITY_BUTTON_TARGETS, ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 3c8d4bca2394..f06b31c4e965 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1850,6 +1850,10 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Accessibility .ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED); dumpSetting(s, p, + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE, + SecureSettingsProto.Accessibility + .ACCESSIBILITY_MAGNIFICATION_GESTURE); + dumpSetting(s, p, Settings.Secure.HEARING_AID_RINGTONE_ROUTING, SecureSettingsProto.Accessibility.HEARING_AID_RINGTONE_ROUTING); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 40f7ba667376..34d3d446530b 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2054,12 +2054,14 @@ public class SettingsProvider extends ContentProvider { final Uri ringtoneUri = Uri.parse(value); // Stream selected ringtone into cache, so it's available for playback // when CE storage is still locked - try (InputStream in = openRingtone(getContext(), ringtoneUri); - OutputStream out = new FileOutputStream(cacheFile)) { - FileUtils.copy(in, out); - } catch (IOException e) { - Slog.w(LOG_TAG, "Failed to cache ringtone: " + e); - } + Binder.withCleanCallingIdentity(() -> { + try (InputStream in = openRingtone(getContext(), ringtoneUri); + OutputStream out = new FileOutputStream(cacheFile)) { + FileUtils.copy(in, out); + } catch (IOException e) { + Slog.w(LOG_TAG, "Failed to cache ringtone: " + e); + } + }); } return true; } diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index ee05f2d9101b..e40fcb2a633b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -319,6 +319,8 @@ filegroup { "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt", "tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt", "tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt", + "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt", + "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt", /* Bouncer UI tests */ "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt", @@ -365,6 +367,42 @@ filegroup { "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt", "tests/src/com/android/systemui/qs/tiles/base/**/*.kt", "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt", + + /* Authentication */ + "tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt", + "tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt", + + /* Device entry */ + "tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt", + "tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt", + + /* Bouncer scene */ + "tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt", + "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt", + + /* Lockscreen scene */ + "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt", + + /* Shade scene */ + "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt", + "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt", + + /* Quick Settings scene */ + "tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt", + + /* Flexiglass / Scene framework tests */ + "tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt", + "tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt", + "tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt", + "tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt", + "tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt", + "tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt", + "tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt", ], path: "tests/src", } @@ -428,7 +466,7 @@ android_library { "hamcrest-library", "androidx.test.rules", "testables", - "truth-prebuilt", + "truth", "monet", "libmonet", "dagger2", @@ -545,7 +583,7 @@ android_robolectric_test { "android.test.runner", "android.test.base", "android.test.mock", - "truth-prebuilt", + "truth", ], upstream: true, diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java index 008732e787f1..96e1e3fa68a7 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java @@ -63,6 +63,7 @@ public class AccessibilityMenuService extends AccessibilityService private static final String TAG = "A11yMenuService"; private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L; + private static final long TAKE_SCREENSHOT_DELAY_MS = 100L; private static final int BRIGHTNESS_UP_INCREMENT_GAMMA = (int) Math.ceil(BrightnessUtils.GAMMA_SPACE_MAX * 0.11f); @@ -301,7 +302,14 @@ public class AccessibilityMenuService extends AccessibilityService } else if (viewTag == ShortcutId.ID_NOTIFICATION_VALUE.ordinal()) { performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS); } else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) { - performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT); + if (Flags.a11yMenuHideBeforeTakingAction()) { + // Delay before taking a screenshot to give time for the UI to close. + mHandler.postDelayed( + () -> performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT), + TAKE_SCREENSHOT_DELAY_MS); + } else { + performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT); + } } if (!Flags.a11yMenuHideBeforeTakingAction()) { diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp index 538ecb3d438d..3fc351c32ec1 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp @@ -32,7 +32,7 @@ android_test { "androidx.test.ext.junit", "compatibility-device-util-axt", "platform-test-annotations", - "truth-prebuilt", + "truth", ], srcs: [ "src/**/*.java", diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 437f8afa8d70..18117a890298 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -13,3 +13,11 @@ flag { description: "Enables all the sysui classic flags that are marked as being in teamfood" bug: "302578396" } + +flag { + name: "notifications_footer_view_refactor" + namespace: "systemui" + description: "Enables the refactored version of the footer view in the notification shade " + "(containing the \"Clear all\" button). Should not bring any behavior changes" + bug: "293167744" +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 0f2e4bace46d..4aac27932924 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -39,6 +39,7 @@ import android.view.ViewGroup import android.view.WindowManager import android.view.animation.Interpolator import android.view.animation.PathInterpolator +import androidx.annotation.AnyThread import androidx.annotation.BinderThread import androidx.annotation.UiThread import com.android.app.animation.Interpolators @@ -149,6 +150,10 @@ class ActivityLaunchAnimator( override fun onLaunchAnimationProgress(linearProgress: Float) { listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } } + + override fun onLaunchAnimationCancelled() { + listeners.forEach { it.onLaunchAnimationCancelled() } + } } /** @@ -191,6 +196,7 @@ class ActivityLaunchAnimator( "ActivityLaunchAnimator.callback must be set before using this animator" ) val runner = createRunner(controller) + val runnerDelegate = runner.delegate!! val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the @@ -241,12 +247,15 @@ class ActivityLaunchAnimator( // If we expect an animation, post a timeout to cancel it in case the remote animation is // never started. if (willAnimate) { - runner.delegate.postTimeout() + runnerDelegate.postTimeout() // Hide the keyguard using the launch animation instead of the default unlock animation. if (hideKeyguardWithAnimation) { callback.hideKeyguardWithAnimation(runner) } + } else { + // We need to make sure delegate references are dropped to avoid memory leaks. + runner.dispose() } } @@ -344,6 +353,13 @@ class ActivityLaunchAnimator( */ fun onLaunchAnimationEnd() {} + /** + * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after + * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called + * before the cancellation. + */ + fun onLaunchAnimationCancelled() {} + /** Called when an activity launch animation made progress. */ fun onLaunchAnimationProgress(linearProgress: Float) {} } @@ -426,6 +442,39 @@ class ActivityLaunchAnimator( fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {} } + /** + * Invokes [onAnimationComplete] when animation is either cancelled or completed. Delegates all + * events to the passed [delegate]. + */ + @VisibleForTesting + inner class DelegatingAnimationCompletionListener( + private val delegate: Listener?, + private val onAnimationComplete: () -> Unit + ) : Listener { + var cancelled = false + + override fun onLaunchAnimationStart() { + delegate?.onLaunchAnimationStart() + } + + override fun onLaunchAnimationProgress(linearProgress: Float) { + delegate?.onLaunchAnimationProgress(linearProgress) + } + + override fun onLaunchAnimationEnd() { + delegate?.onLaunchAnimationEnd() + if (!cancelled) { + onAnimationComplete.invoke() + } + } + + override fun onLaunchAnimationCancelled() { + cancelled = true + delegate?.onLaunchAnimationCancelled() + onAnimationComplete.invoke() + } + } + @VisibleForTesting inner class Runner( controller: Controller, @@ -436,11 +485,21 @@ class ActivityLaunchAnimator( listener: Listener? = null ) : IRemoteAnimationRunner.Stub() { private val context = controller.launchContainer.context - internal val delegate: AnimationDelegate + + // This is being passed across IPC boundaries and cycles (through PendingIntentRecords, + // etc.) are possible. So we need to make sure we drop any references that might + // transitively cause leaks when we're done with animation. + @VisibleForTesting var delegate: AnimationDelegate? init { delegate = - AnimationDelegate(controller, callback, listener, launchAnimator, disableWmTimeout) + AnimationDelegate( + controller, + callback, + DelegatingAnimationCompletionListener(listener, this::dispose), + launchAnimator, + disableWmTimeout + ) } @BinderThread @@ -451,14 +510,33 @@ class ActivityLaunchAnimator( nonApps: Array<out RemoteAnimationTarget>?, finishedCallback: IRemoteAnimationFinishedCallback? ) { + val delegate = delegate context.mainExecutor.execute { - delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback) + if (delegate == null) { + Log.i(TAG, "onAnimationStart called after completion") + // Animation started too late and timed out already. We need to still + // signal back that we're done with it. + finishedCallback?.onAnimationFinished() + } else { + delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback) + } } } @BinderThread override fun onAnimationCancelled() { - context.mainExecutor.execute { delegate.onAnimationCancelled() } + val delegate = delegate + context.mainExecutor.execute { + delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion") + delegate?.onAnimationCancelled() + } + } + + @AnyThread + fun dispose() { + // Drop references to animation controller once we're done with the animation + // to avoid leaking. + context.mainExecutor.execute { delegate = null } } } @@ -584,6 +662,7 @@ class ActivityLaunchAnimator( ) } controller.onLaunchAnimationCancelled() + listener?.onLaunchAnimationCancelled() return } @@ -821,6 +900,7 @@ class ActivityLaunchAnimator( Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]") } controller.onLaunchAnimationCancelled() + listener?.onLaunchAnimationCancelled() } @UiThread @@ -842,6 +922,7 @@ class ActivityLaunchAnimator( ) } controller.onLaunchAnimationCancelled() + listener?.onLaunchAnimationCancelled() } private fun IRemoteAnimationFinishedCallback.invoke() { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index bd3706e1aff3..00d905652987 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -70,8 +70,10 @@ class ViewHierarchyAnimator { * If a new layout change happens while an animation is already in progress, the animation * is updated to continue from the current values to the new end state. * - * A set of [excludedViews] can be passed. If any dependent view from [rootView] matches an - * entry in this set, changes to that view will not be animated. + * By default, child views whole layout changes are animated as well. However, this can be + * controlled by [animateChildren]. If children are included, a set of [excludedViews] can + * be passed. If any dependent view from [rootView] matches an entry in this set, changes to + * that view will not be animated. * * The animator continues to respond to layout changes until [stopAnimating] is called. * @@ -86,6 +88,7 @@ class ViewHierarchyAnimator { rootView: View, interpolator: Interpolator = DEFAULT_INTERPOLATOR, duration: Long = DEFAULT_DURATION, + animateChildren: Boolean = true, excludedViews: Set<View> = emptySet() ): Boolean { return animate( @@ -93,6 +96,7 @@ class ViewHierarchyAnimator { interpolator, duration, ephemeral = false, + animateChildren = animateChildren, excludedViews = excludedViews ) } @@ -106,6 +110,7 @@ class ViewHierarchyAnimator { rootView: View, interpolator: Interpolator = DEFAULT_INTERPOLATOR, duration: Long = DEFAULT_DURATION, + animateChildren: Boolean = true, excludedViews: Set<View> = emptySet() ): Boolean { return animate( @@ -113,6 +118,7 @@ class ViewHierarchyAnimator { interpolator, duration, ephemeral = true, + animateChildren = animateChildren, excludedViews = excludedViews ) } @@ -122,6 +128,7 @@ class ViewHierarchyAnimator { interpolator: Interpolator, duration: Long, ephemeral: Boolean, + animateChildren: Boolean, excludedViews: Set<View> = emptySet() ): Boolean { if ( @@ -137,7 +144,13 @@ class ViewHierarchyAnimator { } val listener = createUpdateListener(interpolator, duration, ephemeral) - addListener(rootView, listener, recursive = true, excludedViews = excludedViews) + addListener( + rootView, + listener, + recursive = true, + animateChildren = animateChildren, + excludedViews = excludedViews + ) return true } @@ -940,6 +953,7 @@ class ViewHierarchyAnimator { view: View, listener: View.OnLayoutChangeListener, recursive: Boolean = false, + animateChildren: Boolean = true, excludedViews: Set<View> = emptySet() ) { if (excludedViews.contains(view)) return @@ -952,12 +966,13 @@ class ViewHierarchyAnimator { view.addOnLayoutChangeListener(listener) view.setTag(R.id.tag_layout_listener, listener) - if (view is ViewGroup && recursive) { + if (animateChildren && view is ViewGroup && recursive) { for (i in 0 until view.childCount) { addListener( view.getChildAt(i), listener, recursive = true, + animateChildren = animateChildren, excludedViews = excludedViews ) } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt index 88944f10eab9..60c3fd3c4cdb 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -108,7 +108,7 @@ private fun CoroutineScope.animate( ) { val fromScene = layoutImpl.state.transitionState.currentScene val isUserInput = - (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven + (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput ?: false val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec @@ -119,9 +119,23 @@ private fun CoroutineScope.animate( val targetProgress = if (reversed) 0f else 1f val transition = if (reversed) { - OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable) + OneOffTransition( + fromScene = target, + toScene = fromScene, + currentScene = target, + isUserInput, + isUserInputOngoing = false, + animatable, + ) } else { - OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable) + OneOffTransition( + fromScene = fromScene, + toScene = target, + currentScene = target, + isUserInput, + isUserInputOngoing = false, + animatable, + ) } // Change the current layout state to use this new transition. @@ -142,7 +156,8 @@ private class OneOffTransition( override val fromScene: SceneKey, override val toScene: SceneKey, override val currentScene: SceneKey, - override val isUserInputDriven: Boolean, + override val isInitiatedByUserInput: Boolean, + override val isUserInputOngoing: Boolean, private val animatable: Animatable<Float, AnimationVector1D>, ) : TransitionState.Transition { override val progress: Float diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt index a62c9840add1..3bcd920fb02b 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt @@ -48,28 +48,40 @@ import com.android.compose.ui.util.lerp /** An element on screen, that can be composed in one or more scenes. */ internal class Element(val key: ElementKey) { /** - * The last offset assigned to this element, relative to the SceneTransitionLayout containing - * it. + * The last values of this element, coming from any scene. Note that this value will be unstable + * if this element is present in multiple scenes but the shared element animation is disabled, + * given that multiple instances of the element with different states will write to these + * values. You should prefer using [TargetValues.lastValues] in the current scene if it is + * defined. */ - var lastOffset = Offset.Unspecified - - /** The last size assigned to this element. */ - var lastSize = SizeUnspecified - - /** The last alpha assigned to this element. */ - var lastAlpha = 1f + val lastSharedValues = Values() /** The mapping between a scene and the values/state this element has in that scene, if any. */ - val sceneValues = SnapshotStateMap<SceneKey, SceneValues>() + val sceneValues = SnapshotStateMap<SceneKey, TargetValues>() override fun toString(): String { return "Element(key=$key)" } + /** The current values of this element, either in a specific scene or in a shared context. */ + class Values { + /** The offset of the element, relative to the SceneTransitionLayout containing it. */ + var offset = Offset.Unspecified + + /** The size of this element. */ + var size = SizeUnspecified + + /** The alpha of this element. */ + var alpha = AlphaUnspecified + } + /** The target values of this element in a given scene. */ - class SceneValues { - var size by mutableStateOf(SizeUnspecified) - var offset by mutableStateOf(Offset.Unspecified) + class TargetValues { + val lastValues = Values() + + var targetSize by mutableStateOf(SizeUnspecified) + var targetOffset by mutableStateOf(Offset.Unspecified) + val sharedValues = SnapshotStateMap<ValueKey, SharedValue<*>>() } @@ -80,6 +92,7 @@ internal class Element(val key: ElementKey) { companion object { val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE) + val AlphaUnspecified = Float.MIN_VALUE } } @@ -91,7 +104,7 @@ internal fun Modifier.element( scene: Scene, key: ElementKey, ): Modifier { - val sceneValues = remember(scene, key) { Element.SceneValues() } + val sceneValues = remember(scene, key) { Element.TargetValues() } val element = // Get the element associated to [key] if it was already composed in another scene, // otherwise create it and add it to our Map<ElementKey, Element>. This is done inside a @@ -108,6 +121,8 @@ internal fun Modifier.element( element } + val lastSharedValues = element.lastSharedValues + val lastSceneValues = sceneValues.lastValues DisposableEffect(scene, sceneValues, element) { onDispose { @@ -121,13 +136,14 @@ internal fun Modifier.element( } val alpha = - remember(layoutImpl, element, scene) { - derivedStateOf { elementAlpha(layoutImpl, element, scene) } + remember(layoutImpl, element, scene, sceneValues) { + derivedStateOf { elementAlpha(layoutImpl, element, scene, sceneValues) } } val isOpaque by remember(alpha) { derivedStateOf { alpha.value == 1f } } SideEffect { - if (isOpaque && element.lastAlpha != 1f) { - element.lastAlpha = 1f + if (isOpaque) { + lastSharedValues.alpha = 1f + lastSceneValues.alpha = 1f } } @@ -148,7 +164,8 @@ internal fun Modifier.element( Modifier.graphicsLayer { val alpha = alpha.value this.alpha = alpha - element.lastAlpha = alpha + lastSharedValues.alpha = alpha + lastSceneValues.alpha = alpha } } .testTag(key.testTag) @@ -220,7 +237,7 @@ private fun Modifier.modifierTransformations( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, ): Modifier { when (val state = layoutImpl.state.transitionState) { is TransitionState.Idle -> return this @@ -248,7 +265,8 @@ private fun Modifier.modifierTransformations( private fun elementAlpha( layoutImpl: SceneTransitionLayoutImpl, element: Element, - scene: Scene + scene: Scene, + sceneValues: Element.TargetValues, ): Float { return computeValue( layoutImpl, @@ -258,7 +276,11 @@ private fun elementAlpha( transformation = { it.alpha }, idleValue = 1f, currentValue = { 1f }, - lastValue = { element.lastAlpha }, + lastValue = { + sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified } + ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified } + ?: 1f + }, ::lerp, ) .coerceIn(0f, 1f) @@ -269,15 +291,15 @@ private fun IntermediateMeasureScope.measure( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, measurable: Measurable, constraints: Constraints, ): Placeable { // Update the size this element has in this scene when idle. val targetSizeInScene = lookaheadSize - if (targetSizeInScene != sceneValues.size) { + if (targetSizeInScene != sceneValues.targetSize) { // TODO(b/290930950): Better handle when this changes to avoid instant size jumps. - sceneValues.size = targetSizeInScene + sceneValues.targetSize = targetSizeInScene } // Some lambdas called (max once) by computeValue() will need to measure [measurable], in which @@ -292,17 +314,14 @@ private fun IntermediateMeasureScope.measure( layoutImpl, scene, element, - sceneValue = { it.size }, + sceneValue = { it.targetSize }, transformation = { it.size }, idleValue = lookaheadSize, currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() }, lastValue = { - val lastSize = element.lastSize - if (lastSize != Element.SizeUnspecified) { - lastSize - } else { - measurable.measure(constraints).also { maybePlaceable = it }.size() - } + sceneValues.lastValues.size.takeIf { it != Element.SizeUnspecified } + ?: element.lastSharedValues.size.takeIf { it != Element.SizeUnspecified } + ?: measurable.measure(constraints).also { maybePlaceable = it }.size() }, ::lerp, ) @@ -316,7 +335,9 @@ private fun IntermediateMeasureScope.measure( ) ) - element.lastSize = placeable.size() + val size = placeable.size() + element.lastSharedValues.size = size + sceneValues.lastValues.size = size return placeable } @@ -325,7 +346,7 @@ private fun IntermediateMeasureScope.place( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, placeable: Placeable, placementScope: Placeable.PlacementScope, ) { @@ -334,9 +355,9 @@ private fun IntermediateMeasureScope.place( // when idle. val coords = coordinates!! val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords) - if (targetOffsetInScene != sceneValues.offset) { + if (targetOffsetInScene != sceneValues.targetOffset) { // TODO(b/290930950): Better handle when this changes to avoid instant offset jumps. - sceneValues.offset = targetOffsetInScene + sceneValues.targetOffset = targetOffsetInScene } val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero) @@ -345,22 +366,20 @@ private fun IntermediateMeasureScope.place( layoutImpl, scene, element, - sceneValue = { it.offset }, + sceneValue = { it.targetOffset }, transformation = { it.offset }, idleValue = targetOffsetInScene, currentValue = { currentOffset }, lastValue = { - val lastValue = element.lastOffset - if (lastValue.isSpecified) { - lastValue - } else { - currentOffset - } + sceneValues.lastValues.offset.takeIf { it.isSpecified } + ?: element.lastSharedValues.offset.takeIf { it.isSpecified } + ?: currentOffset }, ::lerp, ) - element.lastOffset = targetOffset + element.lastSharedValues.offset = targetOffset + sceneValues.lastValues.offset = targetOffset placeable.place((targetOffset - currentOffset).round()) } } @@ -391,7 +410,7 @@ private inline fun <T> computeValue( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValue: (Element.SceneValues) -> T, + sceneValue: (Element.TargetValues) -> T, transformation: (ElementTransformations) -> PropertyTransformation<T>?, idleValue: T, currentValue: () -> T, diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt index ccdec6ea8c5e..1b79dbdee510 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -52,7 +52,14 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isUserInputDriven: Boolean, + val isInitiatedByUserInput: Boolean, + + /** + * Whether user input is currently driving the transition. For example, if a user is + * dragging a pointer, this emits true. Once they lift their finger, this emits false while + * the transition completes/settles. + */ + val isUserInputOngoing: Flow<Boolean>, ) : ObservableTransitionState() } @@ -73,7 +80,8 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans fromScene = state.fromScene, toScene = state.toScene, progress = snapshotFlow { state.progress }, - isUserInputDriven = state.isUserInputDriven, + isInitiatedByUserInput = state.isInitiatedByUserInput, + isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, ) } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 39173d98538f..58c7bdbf3d71 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -130,10 +130,23 @@ interface SceneScope { sealed interface UserAction /** The user navigated back, either using a gesture or by triggering a KEYCODE_BACK event. */ -object Back : UserAction +data object Back : UserAction /** The user swiped on the container. */ -enum class Swipe : UserAction { +data class Swipe( + val direction: SwipeDirection, + val pointerCount: Int = 1, + val fromEdge: Edge? = null, +) : UserAction { + companion object { + val Left = Swipe(SwipeDirection.Left) + val Up = Swipe(SwipeDirection.Up) + val Right = Swipe(SwipeDirection.Right) + val Down = Swipe(SwipeDirection.Down) + } +} + +enum class SwipeDirection { Up, Down, Left, diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 7a21211c3dde..b9f83c545122 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -70,6 +70,9 @@ sealed interface TransitionState { val progress: Float /** Whether the transition was triggered by user input rather than being programmatic. */ - val isUserInputDriven: Boolean + val isInitiatedByUserInput: Boolean + + /** Whether user input is currently driving the transition. */ + val isUserInputOngoing: Boolean } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt index 1cbfe3057ff0..e275fcaf4572 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -66,7 +66,7 @@ internal fun Modifier.swipeToScene( // swipe in the other direction. val startDragImmediately = state == transition && - transition.isAnimatingOffset && + !transition.isUserInputOngoing && !currentScene.shouldEnableSwipes(orientation.opposite()) // The velocity threshold at which the intent of the user is to swipe up or down. It is the same @@ -126,7 +126,7 @@ private class SwipeTransition(initialScene: Scene) : TransitionState.Transition override val progress: Float get() { - val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset + val offset = if (isUserInputOngoing) dragOffset else offsetAnimatable.value if (distance == 0f) { // This can happen only if fromScene == toScene. error( @@ -137,16 +137,15 @@ private class SwipeTransition(initialScene: Scene) : TransitionState.Transition return offset / distance } - override val isUserInputDriven = true + override val isInitiatedByUserInput = true + + var _isUserInputOngoing by mutableStateOf(false) + override val isUserInputOngoing: Boolean + get() = _isUserInputOngoing /** The current offset caused by the drag gesture. */ var dragOffset by mutableFloatStateOf(0f) - /** - * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture. - */ - var isAnimatingOffset by mutableStateOf(false) - /** The animatable used to animate the offset once the user lifted its finger. */ val offsetAnimatable = Animatable(0f, visibilityThreshold = OffsetVisibilityThreshold) @@ -209,9 +208,11 @@ private fun onDragStarted( transition: SwipeTransition, orientation: Orientation, ) { + transition._isUserInputOngoing = true + if (layoutImpl.state.transitionState == transition) { // This [transition] was already driving the animation: simply take over it. - if (transition.isAnimatingOffset) { + if (!transition.isUserInputOngoing) { // Stop animating and start from where the current offset. Setting the animation job to // `null` will effectively cancel the animation. transition.stopOffsetAnimation() @@ -456,30 +457,29 @@ private fun CoroutineScope.animateOffset( ) { transition.startOffsetAnimation { launch { - if (!transition.isAnimatingOffset) { - transition.offsetAnimatable.snapTo(transition.dragOffset) - } - transition.isAnimatingOffset = true - - transition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) + if (transition.isUserInputOngoing) { + transition.offsetAnimatable.snapTo(transition.dragOffset) + } + transition._isUserInputOngoing = false + + transition.offsetAnimatable.animateTo( + targetOffset, + // TODO(b/290184746): Make this spring spec configurable. + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ), + initialVelocity = initialVelocity, + ) - // Now that the animation is done, the state should be idle. Note that if the state - // was changed since this animation started, some external code changed it and we - // shouldn't do anything here. Note also that this job will be cancelled in the case - // where the user intercepts this swipe. - if (layoutImpl.state.transitionState == transition) { - layoutImpl.state.transitionState = TransitionState.Idle(targetScene) - } + // Now that the animation is done, the state should be idle. Note that if the state + // was changed since this animation started, some external code changed it and we + // shouldn't do anything here. Note also that this job will be cancelled in the case + // where the user intercepts this swipe. + if (layoutImpl.state.transitionState == transition) { + layoutImpl.state.transitionState = TransitionState.Idle(targetScene) } - .also { it.invokeOnCompletion { transition.isAnimatingOffset = false } } + } } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt index d4ed697f1757..95385d51cb25 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt @@ -34,12 +34,12 @@ internal class AnchoredSize( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: IntSize, ): IntSize { fun anchorSizeIn(scene: SceneKey): IntSize { - val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.size + val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.targetSize return if (size != null && size != Element.SizeUnspecified) { size } else { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt index 8a5bd746dced..a1d63193bc73 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt @@ -35,13 +35,13 @@ internal class AnchoredTranslate( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Offset, ): Offset { val anchor = layoutImpl.elements[anchor] ?: return value fun anchorOffsetIn(scene: SceneKey): Offset? { - return anchor.sceneValues[scene]?.offset?.takeIf { it.isSpecified } + return anchor.sceneValues[scene]?.targetOffset?.takeIf { it.isSpecified } } // [element] will move the same amount as [anchor] does. diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt index 5cdce9489772..840800d838db 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt @@ -34,12 +34,12 @@ internal class EdgeTranslate( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Offset ): Offset { val sceneSize = scene.size - val elementSize = sceneValues.size + val elementSize = sceneValues.targetSize if (elementSize == Element.SizeUnspecified) { return value } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt index 0a5ac5413b38..17032dc288e0 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt @@ -30,7 +30,7 @@ internal class Fade( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Float ): Float { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt index 73b366ef57bc..62d67f03f1d0 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt @@ -47,14 +47,14 @@ internal class PunchHole( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, ): Modifier { return drawWithContent { val bounds = layoutImpl.elements[bounds] if ( bounds == null || - bounds.lastSize == Element.SizeUnspecified || - bounds.lastOffset == Offset.Unspecified + bounds.lastSharedValues.size == Element.SizeUnspecified || + bounds.lastSharedValues.offset == Offset.Unspecified ) { drawContent() return@drawWithContent @@ -64,7 +64,7 @@ internal class PunchHole( canvas.withSaveLayer(size.toRect(), Paint()) { drawContent() - val offset = bounds.lastOffset - element.lastOffset + val offset = bounds.lastSharedValues.offset - element.lastSharedValues.offset translate(offset.x, offset.y) { drawHole(bounds) } } } @@ -72,7 +72,7 @@ internal class PunchHole( } private fun DrawScope.drawHole(bounds: Element) { - val boundsSize = bounds.lastSize.toSize() + val boundsSize = bounds.lastSharedValues.size.toSize() if (shape == RectangleShape) { drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut) return diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt index ce754dc76adc..233ae597090b 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt @@ -37,7 +37,7 @@ internal class ScaleSize( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: IntSize, ): IntSize { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt index a65025423aee..2ef8d56c6bc6 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt @@ -59,7 +59,7 @@ internal interface ModifierTransformation : Transformation { layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, ): Modifier } @@ -74,7 +74,7 @@ internal sealed interface PropertyTransformation<T> : Transformation { layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: T, ): T diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt index 8abca61bab20..864b937a3fe0 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt @@ -35,7 +35,7 @@ internal class Translate( layoutImpl: SceneTransitionLayoutImpl, scene: Scene, element: Element, - sceneValues: Element.SceneValues, + sceneValues: Element.TargetValues, transition: TransitionState.Transition, value: Offset, ): Offset { diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp index 52c63854f62f..8e9c5864ce70 100644 --- a/packages/SystemUI/compose/core/tests/Android.bp +++ b/packages/SystemUI/compose/core/tests/Android.bp @@ -43,7 +43,7 @@ android_test { "androidx.compose.ui_ui-test-junit4", "androidx.compose.ui_ui-test-manifest", - "truth-prebuilt", + "truth", ], kotlincflags: ["-Xjvm-default=all"], diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 2232370f3dc0..53ed2b5d3317 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -122,7 +122,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isTrue() // Release the finger. We should now be animating back to A (currentScene = SceneA) given // that 55dp < positional threshold. @@ -134,7 +135,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isFalse() // Wait for the animation to finish. We should now be in scene A. rule.waitForIdle() @@ -156,7 +158,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isTrue() // Release the finger. We should now be animating to C (currentScene = SceneC) given // that 56dp >= positional threshold. @@ -168,7 +171,8 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() + assertThat(transition.isUserInputOngoing).isFalse() // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index c3a3752db374..ee310ab41373 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -36,13 +36,14 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.viewinterop.AndroidView import androidx.core.view.isVisible import com.android.compose.animation.scene.SceneScope -import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.qualifiers.KeyguardRootView import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel +import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.UserAction @@ -99,7 +100,9 @@ constructor( return buildMap { up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) } left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) } - this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade) + this[UserAction.Swipe(fromEdge = Edge.TOP, direction = Direction.DOWN)] = + SceneModel(SceneKey.QuickSettings) + this[UserAction.Swipe(direction = Direction.DOWN)] = SceneModel(SceneKey.Shade) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 1f9c3e6d1ea1..a33eac55ac3e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -23,11 +23,13 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.SceneScope +import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.footer.ui.compose.QuickSettings @@ -37,6 +39,7 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.UserAction import com.android.systemui.scene.ui.composable.ComposableScene +import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.ExpandedShadeHeader import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager @@ -98,12 +101,22 @@ private fun SceneScope.QuickSettingsScene( .clickable(onClick = { viewModel.onContentClicked() }) .padding(start = 16.dp, end = 16.dp, bottom = 48.dp) ) { - ExpandedShadeHeader( - viewModel = viewModel.shadeHeaderViewModel, - createTintedIconManager = createTintedIconManager, - createBatteryMeterViewController = createBatteryMeterViewController, - statusBarIconController = statusBarIconController, - ) + when (LocalWindowSizeClass.current.widthSizeClass) { + WindowWidthSizeClass.Compact -> + ExpandedShadeHeader( + viewModel = viewModel.shadeHeaderViewModel, + createTintedIconManager = createTintedIconManager, + createBatteryMeterViewController = createBatteryMeterViewController, + statusBarIconController = statusBarIconController, + ) + else -> + CollapsedShadeHeader( + viewModel = viewModel.shadeHeaderViewModel, + createTintedIconManager = createTintedIconManager, + createBatteryMeterViewController = createBatteryMeterViewController, + statusBarIconController = statusBarIconController, + ) + } Spacer(modifier = Modifier.height(16.dp)) QuickSettings() } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index 2ee461fca042..f35ea8373db7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier import com.android.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.UserAction @@ -41,7 +42,12 @@ class GoneScene @Inject constructor() : ComposableScene { override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = MutableStateFlow<Map<UserAction, SceneModel>>( mapOf( - UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade), + UserAction.Swipe( + pointerCount = 2, + fromEdge = Edge.TOP, + direction = Direction.DOWN, + ) to SceneModel(SceneKey.QuickSettings), + UserAction.Swipe(direction = Direction.DOWN) to SceneModel(SceneKey.Shade), ) ) .asStateFlow() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index ef012660ad71..0da562bcb3bb 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -35,15 +35,18 @@ import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.motionEventSpy import androidx.compose.ui.input.pointer.pointerInput import com.android.compose.animation.scene.Back +import com.android.compose.animation.scene.Edge as SceneTransitionEdge import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.Edge import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -158,7 +161,8 @@ private fun SceneTransitionObservableTransitionState.toModel(): ObservableTransi fromScene = fromScene.toModel().key, toScene = toScene.toModel().key, progress = progress, - isUserInputDriven = isUserInputDriven, + isInitiatedByUserInput = isInitiatedByUserInput, + isUserInputOngoing = isUserInputOngoing, ) } } @@ -180,12 +184,24 @@ private fun SceneTransitionSceneKey.toModel(): SceneModel { private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction { return when (this) { is UserAction.Swipe -> - when (this.direction) { - Direction.LEFT -> Swipe.Left - Direction.UP -> Swipe.Up - Direction.RIGHT -> Swipe.Right - Direction.DOWN -> Swipe.Down - } + Swipe( + pointerCount = pointerCount, + fromEdge = + when (this.fromEdge) { + null -> null + Edge.LEFT -> SceneTransitionEdge.Left + Edge.TOP -> SceneTransitionEdge.Top + Edge.RIGHT -> SceneTransitionEdge.Right + Edge.BOTTOM -> SceneTransitionEdge.Bottom + }, + direction = + when (this.direction) { + Direction.LEFT -> SwipeDirection.Left + Direction.UP -> SwipeDirection.Up + Direction.RIGHT -> SwipeDirection.Right + Direction.DOWN -> SwipeDirection.Down + } + ) is UserAction.Back -> Back } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index 6629a2598587..591fa76f423f 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf @@ -50,6 +51,7 @@ import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.ValueKey import com.android.compose.animation.scene.animateSharedFloatAsState +import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.settingslib.Utils import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController @@ -98,12 +100,12 @@ fun SceneScope.CollapsedShadeHeader( ShadeHeader.Keys.transitionProgress, ShadeHeader.Elements.FormatPlaceholder ) - val useExpandedFormat by - remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } } val cutoutWidth = LocalDisplayCutout.current.width() val cutoutLocation = LocalDisplayCutout.current.location + val useExpandedFormat = formatProgress.value > 0.5f || cutoutLocation != CutoutLocation.CENTER + // This layout assumes it is globally positioned at (0, 0) and is the // same size as the screen. Layout( @@ -131,6 +133,14 @@ fun SceneScope.CollapsedShadeHeader( { Row(horizontalArrangement = Arrangement.End) { SystemIconContainer { + when (LocalWindowSizeClass.current.widthSizeClass) { + WindowWidthSizeClass.Medium, + WindowWidthSizeClass.Expanded -> + ShadeCarrierGroup( + viewModel = viewModel, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, diff --git a/packages/SystemUI/customization/res/values-af/strings.xml b/packages/SystemUI/customization/res/values-af/strings.xml new file mode 100644 index 000000000000..4c2c6275e6d8 --- /dev/null +++ b/packages/SystemUI/customization/res/values-af/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Verstek vir digitaal"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-am/strings.xml b/packages/SystemUI/customization/res/values-am/strings.xml new file mode 100644 index 000000000000..847d7a541c95 --- /dev/null +++ b/packages/SystemUI/customization/res/values-am/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ዲጂታል ነባሪ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ar/strings.xml b/packages/SystemUI/customization/res/values-ar/strings.xml new file mode 100644 index 000000000000..57d1612b337d --- /dev/null +++ b/packages/SystemUI/customization/res/values-ar/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"رقمية تلقائية"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-as/strings.xml b/packages/SystemUI/customization/res/values-as/strings.xml new file mode 100644 index 000000000000..2f3b64b9ceb6 --- /dev/null +++ b/packages/SystemUI/customization/res/values-as/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটেল ডিফ’ল্ট"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-az/strings.xml b/packages/SystemUI/customization/res/values-az/strings.xml new file mode 100644 index 000000000000..eb52f95a4525 --- /dev/null +++ b/packages/SystemUI/customization/res/values-az/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Rəqəmsal defolt"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..90e6678bdcd6 --- /dev/null +++ b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalni podrazumevani"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-be/strings.xml b/packages/SystemUI/customization/res/values-be/strings.xml new file mode 100644 index 000000000000..f327da2a40a4 --- /dev/null +++ b/packages/SystemUI/customization/res/values-be/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"электронны, стандартны шрыфт"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-bg/strings.xml b/packages/SystemUI/customization/res/values-bg/strings.xml new file mode 100644 index 000000000000..6e3754a62a29 --- /dev/null +++ b/packages/SystemUI/customization/res/values-bg/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Стандартно дигитално"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-bn/strings.xml b/packages/SystemUI/customization/res/values-bn/strings.xml new file mode 100644 index 000000000000..adf1256b6c82 --- /dev/null +++ b/packages/SystemUI/customization/res/values-bn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটাল ডিফল্ট"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-bs/strings.xml b/packages/SystemUI/customization/res/values-bs/strings.xml new file mode 100644 index 000000000000..8de04ab06503 --- /dev/null +++ b/packages/SystemUI/customization/res/values-bs/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalno zadano"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ca/strings.xml b/packages/SystemUI/customization/res/values-ca/strings.xml new file mode 100644 index 000000000000..967bb3f6d2f5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ca/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminat"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-cs/strings.xml b/packages/SystemUI/customization/res/values-cs/strings.xml new file mode 100644 index 000000000000..45da4d759ad4 --- /dev/null +++ b/packages/SystemUI/customization/res/values-cs/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitální výchozí"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-da/strings.xml b/packages/SystemUI/customization/res/values-da/strings.xml new file mode 100644 index 000000000000..3ffaa8b167c8 --- /dev/null +++ b/packages/SystemUI/customization/res/values-da/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Standard (digital)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-de/strings.xml b/packages/SystemUI/customization/res/values-de/strings.xml new file mode 100644 index 000000000000..64f95e05b245 --- /dev/null +++ b/packages/SystemUI/customization/res/values-de/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital (Standard)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-el/strings.xml b/packages/SystemUI/customization/res/values-el/strings.xml new file mode 100644 index 000000000000..57134b566d08 --- /dev/null +++ b/packages/SystemUI/customization/res/values-el/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Ψηφιακή προεπιλογή"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rAU/strings.xml b/packages/SystemUI/customization/res/values-en-rAU/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rAU/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rCA/strings.xml b/packages/SystemUI/customization/res/values-en-rCA/strings.xml new file mode 100644 index 000000000000..79919c07d189 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rCA/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for clock_default_description (5309401440896597541) --> + <skip /> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rGB/strings.xml b/packages/SystemUI/customization/res/values-en-rGB/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rGB/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rIN/strings.xml b/packages/SystemUI/customization/res/values-en-rIN/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rIN/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-en-rXC/strings.xml b/packages/SystemUI/customization/res/values-en-rXC/strings.xml new file mode 100644 index 000000000000..7c540dab2cfe --- /dev/null +++ b/packages/SystemUI/customization/res/values-en-rXC/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-es-rUS/strings.xml b/packages/SystemUI/customization/res/values-es-rUS/strings.xml new file mode 100644 index 000000000000..59be786849e6 --- /dev/null +++ b/packages/SystemUI/customization/res/values-es-rUS/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Configuración predeterminada digital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-es/strings.xml b/packages/SystemUI/customization/res/values-es/strings.xml new file mode 100644 index 000000000000..03ef0e5a8cea --- /dev/null +++ b/packages/SystemUI/customization/res/values-es/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminada"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-et/strings.xml b/packages/SystemUI/customization/res/values-et/strings.xml new file mode 100644 index 000000000000..fec793e99619 --- /dev/null +++ b/packages/SystemUI/customization/res/values-et/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitaalne vaikimisi"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-eu/strings.xml b/packages/SystemUI/customization/res/values-eu/strings.xml new file mode 100644 index 000000000000..a70c8085ccf0 --- /dev/null +++ b/packages/SystemUI/customization/res/values-eu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital lehenetsia"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fa/strings.xml b/packages/SystemUI/customization/res/values-fa/strings.xml new file mode 100644 index 000000000000..6426d5112528 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fa/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"دیجیتال پیشفرض"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fi/strings.xml b/packages/SystemUI/customization/res/values-fi/strings.xml new file mode 100644 index 000000000000..9b19373a3765 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitaalinen (oletus)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fr-rCA/strings.xml b/packages/SystemUI/customization/res/values-fr-rCA/strings.xml new file mode 100644 index 000000000000..bbd1208b1922 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fr-rCA/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Numérique, par défaut"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-fr/strings.xml b/packages/SystemUI/customization/res/values-fr/strings.xml new file mode 100644 index 000000000000..5579a427fe71 --- /dev/null +++ b/packages/SystemUI/customization/res/values-fr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Numérique par défaut"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-gl/strings.xml b/packages/SystemUI/customization/res/values-gl/strings.xml new file mode 100644 index 000000000000..2da93c6f1e34 --- /dev/null +++ b/packages/SystemUI/customization/res/values-gl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Predeterminada dixital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-gu/strings.xml b/packages/SystemUI/customization/res/values-gu/strings.xml new file mode 100644 index 000000000000..c578a2e2d322 --- /dev/null +++ b/packages/SystemUI/customization/res/values-gu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ડિજિટલ ડિફૉલ્ટ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hi/strings.xml b/packages/SystemUI/customization/res/values-hi/strings.xml new file mode 100644 index 000000000000..6080f802af04 --- /dev/null +++ b/packages/SystemUI/customization/res/values-hi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफ़ॉल्ट"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hr/strings.xml b/packages/SystemUI/customization/res/values-hr/strings.xml new file mode 100644 index 000000000000..0a1440fb683b --- /dev/null +++ b/packages/SystemUI/customization/res/values-hr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalni zadani"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hu/strings.xml b/packages/SystemUI/customization/res/values-hu/strings.xml new file mode 100644 index 000000000000..32618a869a1f --- /dev/null +++ b/packages/SystemUI/customization/res/values-hu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitális, alapértelmezett"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-hy/strings.xml b/packages/SystemUI/customization/res/values-hy/strings.xml new file mode 100644 index 000000000000..d45afbf256ab --- /dev/null +++ b/packages/SystemUI/customization/res/values-hy/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Թվային, կանխադրված"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-in/strings.xml b/packages/SystemUI/customization/res/values-in/strings.xml new file mode 100644 index 000000000000..a6110d5d5b09 --- /dev/null +++ b/packages/SystemUI/customization/res/values-in/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-is/strings.xml b/packages/SystemUI/customization/res/values-is/strings.xml new file mode 100644 index 000000000000..5a370d614f0f --- /dev/null +++ b/packages/SystemUI/customization/res/values-is/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Stafræn, sjálfgefið"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-it/strings.xml b/packages/SystemUI/customization/res/values-it/strings.xml new file mode 100644 index 000000000000..2a5087d30669 --- /dev/null +++ b/packages/SystemUI/customization/res/values-it/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitale - predefinito"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-iw/strings.xml b/packages/SystemUI/customization/res/values-iw/strings.xml new file mode 100644 index 000000000000..ddd28f21ad99 --- /dev/null +++ b/packages/SystemUI/customization/res/values-iw/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"דיגיטלי ברירת מחדל"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ja/strings.xml b/packages/SystemUI/customization/res/values-ja/strings.xml new file mode 100644 index 000000000000..744604a17efa --- /dev/null +++ b/packages/SystemUI/customization/res/values-ja/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"デジタル デフォルト"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ka/strings.xml b/packages/SystemUI/customization/res/values-ka/strings.xml new file mode 100644 index 000000000000..88ba1dfc0976 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ka/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ციფრული ნაგულისხმევი"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-kk/strings.xml b/packages/SystemUI/customization/res/values-kk/strings.xml new file mode 100644 index 000000000000..9ee6522c49ce --- /dev/null +++ b/packages/SystemUI/customization/res/values-kk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Цифрлық әдепкі"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-km/strings.xml b/packages/SystemUI/customization/res/values-km/strings.xml new file mode 100644 index 000000000000..bbc438a69bcd --- /dev/null +++ b/packages/SystemUI/customization/res/values-km/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"លំនាំដើមឌីជីថល"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-kn/strings.xml b/packages/SystemUI/customization/res/values-kn/strings.xml new file mode 100644 index 000000000000..e67319d4021b --- /dev/null +++ b/packages/SystemUI/customization/res/values-kn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ಡಿಜಿಟಲ್ ಡೀಫಾಲ್ಟ್"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ko/strings.xml b/packages/SystemUI/customization/res/values-ko/strings.xml new file mode 100644 index 000000000000..fa9103b01e66 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ko/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"디지털 기본"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ky/strings.xml b/packages/SystemUI/customization/res/values-ky/strings.xml new file mode 100644 index 000000000000..76cc5e211a40 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ky/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Демейки санариптик"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-lo/strings.xml b/packages/SystemUI/customization/res/values-lo/strings.xml new file mode 100644 index 000000000000..28f50008bd73 --- /dev/null +++ b/packages/SystemUI/customization/res/values-lo/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ດິຈິຕອນຕາມຄ່າເລີ່ມຕົ້ນ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-lt/strings.xml b/packages/SystemUI/customization/res/values-lt/strings.xml new file mode 100644 index 000000000000..2fe731547762 --- /dev/null +++ b/packages/SystemUI/customization/res/values-lt/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Skaitmeninis numatytasis"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-lv/strings.xml b/packages/SystemUI/customization/res/values-lv/strings.xml new file mode 100644 index 000000000000..e0b904a8a1c9 --- /dev/null +++ b/packages/SystemUI/customization/res/values-lv/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitālais pulkstenis — noklusējums"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-mk/strings.xml b/packages/SystemUI/customization/res/values-mk/strings.xml new file mode 100644 index 000000000000..9b95a6e32b31 --- /dev/null +++ b/packages/SystemUI/customization/res/values-mk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Дигитален стандарден приказ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ml/strings.xml b/packages/SystemUI/customization/res/values-ml/strings.xml new file mode 100644 index 000000000000..7f6be8a59904 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ml/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ഡിജിറ്റൽ ഡിഫോൾട്ട്"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-mn/strings.xml b/packages/SystemUI/customization/res/values-mn/strings.xml new file mode 100644 index 000000000000..38369b6f527d --- /dev/null +++ b/packages/SystemUI/customization/res/values-mn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Дижитал өгөгдмөл"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-mr/strings.xml b/packages/SystemUI/customization/res/values-mr/strings.xml new file mode 100644 index 000000000000..821ff100ab13 --- /dev/null +++ b/packages/SystemUI/customization/res/values-mr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डीफॉल्टसह क्लॉक फेस"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ms/strings.xml b/packages/SystemUI/customization/res/values-ms/strings.xml new file mode 100644 index 000000000000..2f61b47a2324 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ms/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital lalai"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-my/strings.xml b/packages/SystemUI/customization/res/values-my/strings.xml new file mode 100644 index 000000000000..3d137ebc3abb --- /dev/null +++ b/packages/SystemUI/customization/res/values-my/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ဒစ်ဂျစ်တယ်နာရီ မူရင်း"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-nb/strings.xml b/packages/SystemUI/customization/res/values-nb/strings.xml new file mode 100644 index 000000000000..6eb4373c448c --- /dev/null +++ b/packages/SystemUI/customization/res/values-nb/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital – standard"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ne/strings.xml b/packages/SystemUI/customization/res/values-ne/strings.xml new file mode 100644 index 000000000000..c5b087744a18 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ne/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफल्ट"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-nl/strings.xml b/packages/SystemUI/customization/res/values-nl/strings.xml new file mode 100644 index 000000000000..4f46ab8b2ba5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-nl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Standaard digitaal"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-or/strings.xml b/packages/SystemUI/customization/res/values-or/strings.xml new file mode 100644 index 000000000000..a74017f6b689 --- /dev/null +++ b/packages/SystemUI/customization/res/values-or/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ଡିଜିଟାଲ ଡିଫଲ୍ଟ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pa/strings.xml b/packages/SystemUI/customization/res/values-pa/strings.xml new file mode 100644 index 000000000000..a77661a8f56b --- /dev/null +++ b/packages/SystemUI/customization/res/values-pa/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ਡਿਜੀਟਲ ਡਿਫਾਲਟ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pl/strings.xml b/packages/SystemUI/customization/res/values-pl/strings.xml new file mode 100644 index 000000000000..6f5b6f280dd1 --- /dev/null +++ b/packages/SystemUI/customization/res/values-pl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Cyfrowa domyślna"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pt-rPT/strings.xml b/packages/SystemUI/customization/res/values-pt-rPT/strings.xml new file mode 100644 index 000000000000..c6c3cc046965 --- /dev/null +++ b/packages/SystemUI/customization/res/values-pt-rPT/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Predefinição digital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-pt/strings.xml b/packages/SystemUI/customization/res/values-pt/strings.xml new file mode 100644 index 000000000000..bbe435543211 --- /dev/null +++ b/packages/SystemUI/customization/res/values-pt/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital padrão"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ro/strings.xml b/packages/SystemUI/customization/res/values-ro/strings.xml new file mode 100644 index 000000000000..ef163e96f79f --- /dev/null +++ b/packages/SystemUI/customization/res/values-ro/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Implicit digital"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ru/strings.xml b/packages/SystemUI/customization/res/values-ru/strings.xml new file mode 100644 index 000000000000..5ee928e0fa37 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ru/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Цифровые часы, стандартный"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-si/strings.xml b/packages/SystemUI/customization/res/values-si/strings.xml new file mode 100644 index 000000000000..caf9610e921e --- /dev/null +++ b/packages/SystemUI/customization/res/values-si/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ඩිජිටල් පෙරනිමිය"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sk/strings.xml b/packages/SystemUI/customization/res/values-sk/strings.xml new file mode 100644 index 000000000000..1843f971d251 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitálne predvolené"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sl/strings.xml b/packages/SystemUI/customization/res/values-sl/strings.xml new file mode 100644 index 000000000000..12df66f571d3 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digitalna (privzeta)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sq/strings.xml b/packages/SystemUI/customization/res/values-sq/strings.xml new file mode 100644 index 000000000000..1fc9f252175f --- /dev/null +++ b/packages/SystemUI/customization/res/values-sq/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Dixhitale e parazgjedhur"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sr/strings.xml b/packages/SystemUI/customization/res/values-sr/strings.xml new file mode 100644 index 000000000000..6b127c9f79e5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Дигитални подразумевани"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sv/strings.xml b/packages/SystemUI/customization/res/values-sv/strings.xml new file mode 100644 index 000000000000..84ad25c96655 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sv/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital standard"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sw/strings.xml b/packages/SystemUI/customization/res/values-sw/strings.xml new file mode 100644 index 000000000000..e2ec3de573a8 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sw/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Dijitali chaguomsingi"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sw400dp/dimens.xml b/packages/SystemUI/customization/res/values-sw400dp/dimens.xml new file mode 100644 index 000000000000..3c9e0789b5f2 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sw400dp/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +--> +<resources> + <dimen name="presentation_clock_text_size">170dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ta/strings.xml b/packages/SystemUI/customization/res/values-ta/strings.xml new file mode 100644 index 000000000000..f4eea07b0aa5 --- /dev/null +++ b/packages/SystemUI/customization/res/values-ta/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"டிஜிட்டல் இயல்பு"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-te/strings.xml b/packages/SystemUI/customization/res/values-te/strings.xml new file mode 100644 index 000000000000..c7c77d527e7f --- /dev/null +++ b/packages/SystemUI/customization/res/values-te/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"డిజిటల్ ఆటోమేటిక్ సెట్టింగ్"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-th/strings.xml b/packages/SystemUI/customization/res/values-th/strings.xml new file mode 100644 index 000000000000..61d880eb1ad9 --- /dev/null +++ b/packages/SystemUI/customization/res/values-th/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ดิจิทัลแบบเริ่มต้น"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-tl/strings.xml b/packages/SystemUI/customization/res/values-tl/strings.xml new file mode 100644 index 000000000000..a3484a7b6d2a --- /dev/null +++ b/packages/SystemUI/customization/res/values-tl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Digital na default"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-tr/strings.xml b/packages/SystemUI/customization/res/values-tr/strings.xml new file mode 100644 index 000000000000..a90e9852ef2f --- /dev/null +++ b/packages/SystemUI/customization/res/values-tr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Dijital varsayılan"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-uk/strings.xml b/packages/SystemUI/customization/res/values-uk/strings.xml new file mode 100644 index 000000000000..ee9b77b905a2 --- /dev/null +++ b/packages/SystemUI/customization/res/values-uk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Цифровий, стандартний"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-ur/strings.xml b/packages/SystemUI/customization/res/values-ur/strings.xml new file mode 100644 index 000000000000..06a6a7cd01fd --- /dev/null +++ b/packages/SystemUI/customization/res/values-ur/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"ڈیجیٹل ڈیفالٹ"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-uz/strings.xml b/packages/SystemUI/customization/res/values-uz/strings.xml new file mode 100644 index 000000000000..6b31b048b4a1 --- /dev/null +++ b/packages/SystemUI/customization/res/values-uz/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Raqamli soat, standart"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-vi/strings.xml b/packages/SystemUI/customization/res/values-vi/strings.xml new file mode 100644 index 000000000000..830b6e2b16a1 --- /dev/null +++ b/packages/SystemUI/customization/res/values-vi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Mặt số mặc định"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zh-rCN/strings.xml b/packages/SystemUI/customization/res/values-zh-rCN/strings.xml new file mode 100644 index 000000000000..747567e9fb65 --- /dev/null +++ b/packages/SystemUI/customization/res/values-zh-rCN/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"默认数字"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zh-rHK/strings.xml b/packages/SystemUI/customization/res/values-zh-rHK/strings.xml new file mode 100644 index 000000000000..c19cc68ff150 --- /dev/null +++ b/packages/SystemUI/customization/res/values-zh-rHK/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"數碼時鐘 (預設)"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zh-rTW/strings.xml b/packages/SystemUI/customization/res/values-zh-rTW/strings.xml new file mode 100644 index 000000000000..6fcd313a291d --- /dev/null +++ b/packages/SystemUI/customization/res/values-zh-rTW/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"數位預設"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-zu/strings.xml b/packages/SystemUI/customization/res/values-zu/strings.xml new file mode 100644 index 000000000000..c87c250ae1b9 --- /dev/null +++ b/packages/SystemUI/customization/res/values-zu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="clock_default_description" msgid="5309401440896597541">"Okuzenzakalelayo kwedijithali"</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index 8eb8132b07b9..c574d1fc674b 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -17,6 +17,7 @@ --> <resources> <!-- Clock maximum font size (dp is intentional, to prevent any further scaling) --> + <dimen name="presentation_clock_text_size">50dp</dimen> <dimen name="large_clock_text_size">150dp</dimen> <dimen name="small_clock_text_size">86dp</dimen> diff --git a/packages/SystemUI/customization/res/values/strings.xml b/packages/SystemUI/customization/res/values/strings.xml new file mode 100644 index 000000000000..897c842b6d4b --- /dev/null +++ b/packages/SystemUI/customization/res/values/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- default clock face name [CHAR LIMIT=NONE]--> + <string name="clock_default_name">Default</string> + + <!-- default clock face description [CHAR LIMIT=NONE]--> + <string name="clock_default_description">Digital default</string> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index b28920c590c5..b076b2cacf08 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -65,7 +65,13 @@ class DefaultClockController( protected var onSecondaryDisplay: Boolean = false override val events: DefaultClockEvents - override val config = ClockConfig(DEFAULT_CLOCK_ID) + override val config: ClockConfig by lazy { + ClockConfig( + DEFAULT_CLOCK_ID, + resources.getString(R.string.clock_default_name), + resources.getString(R.string.clock_default_description) + ) + } init { val parent = FrameLayout(ctx) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index 949641a7f75e..dd52e39488ac 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -25,7 +25,6 @@ import com.android.systemui.plugins.ClockProvider import com.android.systemui.plugins.ClockSettings private val TAG = DefaultClockProvider::class.simpleName -const val DEFAULT_CLOCK_NAME = "Default Clock" const val DEFAULT_CLOCK_ID = "DEFAULT" /** Provides the default system clock */ @@ -35,8 +34,7 @@ class DefaultClockProvider( val resources: Resources, val hasStepClockAnimation: Boolean = false ) : ClockProvider { - override fun getClocks(): List<ClockMetadata> = - listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME)) + override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID)) override fun createClock(settings: ClockSettings): ClockController { if (settings.clockId != DEFAULT_CLOCK_ID) { diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index e2f4793b8f91..485c27e16150 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -192,15 +192,18 @@ enum class ClockTickRate(val value: Int) { /** Some data about a clock design */ data class ClockMetadata( val clockId: ClockId, - val name: String, -) { - constructor(clockId: ClockId) : this(clockId, clockId) {} -} +) /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ data class ClockConfig( val id: String, + /** Localized name of the clock */ + val name: String, + + /** Localized accessibility description for the clock */ + val description: String, + /** Transition to AOD should move smartspace like large clock instead of small clock */ val useAlternateSmartspaceAODTransition: Boolean = false, diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml new file mode 100644 index 000000000000..218735229a5d --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:id="@+id/alternate_bouncer" + android:focusable="true" + android:clickable="true" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.systemui.scrim.ScrimView + android:id="@+id/alternate_bouncer_scrim" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:importantForAccessibility="no" + sysui:ignoreRightInset="true" + /> +</FrameLayout> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml index 593f507f3c88..f68ab8110b6d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml @@ -16,22 +16,30 @@ */ --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/presentation" android:layout_width="match_parent" android:layout_height="match_parent"> <com.android.keyguard.KeyguardStatusView android:id="@+id/clock" - android:layout_width="410dp" - android:layout_height="wrap_content" + android:layout_width="0dp" + android:layout_height="0dp" android:layout_gravity="center" - android:orientation="vertical"> + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + <include android:id="@+id/keyguard_clock_container" layout="@layout/keyguard_clock_switch" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content"/> </com.android.keyguard.KeyguardStatusView> -</FrameLayout> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index d4b73a4e3de0..acee4258b64d 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -130,6 +130,11 @@ android:inflatedId="@+id/multi_shade" android:layout="@layout/multi_shade" /> + <include layout="@layout/alternate_bouncer" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="invisible" /> + <com.android.systemui.biometrics.AuthRippleView android:id="@+id/auth_ripple" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 18f24ec6293e..1add90ff4083 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -354,9 +354,6 @@ <!-- Whether to show activity indicators in the status bar --> <bool name="config_showActivity">false</bool> - <!-- Whether or not the button to clear all notifications will be shown. --> - <bool name="config_enableNotificationsClearAll">true</bool> - <!-- Whether or not to show the notification shelf that houses the icons of notifications that have been scrolled off-screen. --> <bool name="config_showNotificationShelf">true</bool> diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt new file mode 100644 index 000000000000..57a49c83ae17 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.dagger.qualifiers + +import java.lang.annotation.Documented +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy.RUNTIME +import javax.inject.Qualifier + +/** Annotates a class that is display specific. */ +@Qualifier @Documented @Retention(RUNTIME) annotation class DisplaySpecific diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 0094820f0dad..a6e04cec5f86 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -23,6 +23,7 @@ import android.view.SurfaceControl; import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; +import com.android.internal.os.IResultReceiver; import com.android.systemui.shared.recents.model.ThumbnailData; public class RecentsAnimationControllerCompat { @@ -89,11 +90,16 @@ public class RecentsAnimationControllerCompat { * @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous * app. */ - public void finish(boolean toHome, boolean sendUserLeaveHint) { + public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) { try { - mAnimationController.finish(toHome, sendUserLeaveHint); + mAnimationController.finish(toHome, sendUserLeaveHint, finishCb); } catch (RemoteException e) { Log.e(TAG, "Failed to finish recents animation", e); + try { + finishCb.send(0, null); + } catch (Exception ex) { + // Local call, can ignore + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 2f3c1f263782..eb7a7358cbf1 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -205,19 +205,19 @@ constructor( private var isRegistered = false private var disposableHandle: DisposableHandle? = null private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING) - + private var largeClockOnSecondaryDisplay = false private fun updateColors() { if (regionSamplingEnabled) { clock?.let { clock -> smallRegionSampler?.let { - smallClockIsDark = it.currentRegionDarkness().isDark - clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark) + val isRegionDark = it.currentRegionDarkness().isDark + clock.smallClock.events.onRegionDarknessChanged(isRegionDark) } largeRegionSampler?.let { - largeClockIsDark = it.currentRegionDarkness().isDark - clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark) + val isRegionDark = it.currentRegionDarkness().isDark + clock.largeClock.events.onRegionDarknessChanged(isRegionDark) } } return @@ -225,12 +225,12 @@ constructor( val isLightTheme = TypedValue() context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true) - smallClockIsDark = isLightTheme.data == 0 - largeClockIsDark = isLightTheme.data == 0 + val isRegionDark = isLightTheme.data == 0 clock?.run { - smallClock.events.onRegionDarknessChanged(smallClockIsDark) - largeClock.events.onRegionDarknessChanged(largeClockIsDark) + Log.i(TAG, "Region isDark: $isRegionDark") + smallClock.events.onRegionDarknessChanged(isRegionDark) + largeClock.events.onRegionDarknessChanged(isRegionDark) } } protected open fun createRegionSampler( @@ -260,9 +260,6 @@ constructor( get() = isKeyguardVisible && dozeAmount < DOZE_TICKRATE_THRESHOLD private var cachedWeatherData: WeatherData? = null - private var smallClockIsDark = true - private var largeClockIsDark = true - private val configListener = object : ConfigurationController.ConfigurationListener { override fun onThemeChanged() { @@ -381,6 +378,19 @@ constructor( ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) } + /** + * Sets this clock as showing in a secondary display. + * + * Not that this is not necessarily needed, as we could get the displayId from [Context] + * directly and infere [largeClockOnSecondaryDisplay] from the id being different than the + * default display one. However, if we do so, current screenshot tests would not work, as they + * pass an activity context always from the default display. + */ + fun setLargeClockOnSecondaryDisplay(onSecondaryDisplay: Boolean) { + largeClockOnSecondaryDisplay = onSecondaryDisplay + updateFontSizes() + } + private fun updateTimeListeners() { smallTimeListener?.stop() largeTimeListener?.stop() @@ -403,9 +413,15 @@ constructor( smallClock.events.onFontSettingChanged( resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat() ) - largeClock.events.onFontSettingChanged( - resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat() - ) + largeClock.events.onFontSettingChanged(getLargeClockSizePx()) + } + } + + private fun getLargeClockSizePx(): Float { + return if (largeClockOnSecondaryDisplay) { + resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat() + } else { + resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat() } } diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt index d677a15862de..dec7d7992596 100644 --- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt +++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt @@ -68,10 +68,13 @@ constructor( clock = requireViewById(R.id.clock) keyguardStatusViewController = - keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply { - setDisplayedOnSecondaryDisplay() - init() - } + keyguardStatusViewComponentFactory + .build(clock, display) + .keyguardStatusViewController + .apply { + setDisplayedOnSecondaryDisplay() + init() + } } /** [ConnectedDisplayKeyguardPresentation] factory. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index ff8e489528b7..3c8301fe1b26 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -84,7 +84,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final DumpManager mDumpManager; private final ClockEventController mClockEventController; private final LogBuffer mLogBuffer; - private FrameLayout mSmallClockFrame; // top aligned clock private FrameLayout mLargeClockFrame; // centered clock @@ -265,6 +264,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (mShownOnSecondaryDisplay) { mView.setLargeClockOnSecondaryDisplay(true); + mClockEventController.setLargeClockOnSecondaryDisplay(true); displayClock(LARGE, /* animate= */ false); hideSliceViewAndNotificationIconContainer(); return; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 9c015fea5cf7..8a6f101b6c6c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -419,7 +419,7 @@ public class KeyguardDisplayManager { mClock.post(mMoveTextRunnable); mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory - .build(findViewById(R.id.clock)) + .build(findViewById(R.id.clock), getDisplay()) .getKeyguardClockSwitchController(); mKeyguardClockSwitchController.setOnlyClock(true); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 51c0676876b9..50be97ec1af9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -139,7 +139,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { case PROMPT_REASON_USER_REQUEST: return R.string.kg_prompt_after_user_lockdown_password; case PROMPT_REASON_PREPARE_FOR_UPDATE: - return R.string.kg_prompt_unattended_update_password; + return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_TRUSTAGENT_EXPIRED: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 714ba81fc664..57151ae32db0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -321,7 +321,7 @@ public class KeyguardPatternViewController resId = R.string.kg_prompt_after_user_lockdown_pattern; break; case PROMPT_REASON_PREPARE_FOR_UPDATE: - resId = R.string.kg_prompt_unattended_update_pattern; + resId = R.string.kg_prompt_reason_timeout_pattern; break; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: resId = R.string.kg_prompt_reason_timeout_pattern; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 9d6d0332b96b..681aa70402bd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -123,7 +123,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView case PROMPT_REASON_USER_REQUEST: return R.string.kg_prompt_after_user_lockdown_pin; case PROMPT_REASON_PREPARE_FOR_UPDATE: - return R.string.kg_prompt_unattended_update_pin; + return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_TRUSTAGENT_EXPIRED: diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index 1d2d77f16b75..40d0be1173fa 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -18,6 +18,7 @@ package com.android.keyguard; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; @@ -68,6 +69,7 @@ public class LockIconView extends FrameLayout implements Dumpable { private boolean mUseBackground = false; private float mDozeAmount = 0f; + @SuppressLint("ClickableViewAccessibility") public LockIconView(Context context, AttributeSet attrs) { super(context, attrs); mSensorRect = new RectF(); diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 9b00b5f2aaf2..0d3f726b011b 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -29,6 +29,7 @@ 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; @@ -57,11 +58,11 @@ import androidx.annotation.VisibleForTesting; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -73,12 +74,16 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; +import dagger.Lazy; + import java.io.PrintWriter; import java.util.Objects; import java.util.function.Consumer; @@ -127,6 +132,8 @@ public class LockIconViewController implements Dumpable { @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; @NonNull private final KeyguardInteractor mKeyguardInteractor; @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate; + @NonNull private final Lazy<BouncerInteractor> mBouncerInteractor; + @NonNull private final SceneContainerFlags mSceneContainerFlags; // Tracks the velocity of a touch to help filter out the touches that move too fast. private VelocityTracker mVelocityTracker; @@ -203,7 +210,9 @@ public class LockIconViewController implements Dumpable { @NonNull KeyguardInteractor keyguardInteractor, @NonNull FeatureFlags featureFlags, PrimaryBouncerInteractor primaryBouncerInteractor, - Context context + Context context, + Lazy<BouncerInteractor> bouncerInteractor, + SceneContainerFlags sceneContainerFlags ) { mStatusBarStateController = statusBarStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -232,6 +241,8 @@ public class LockIconViewController implements Dumpable { dumpManager.registerDumpable(TAG, this); mResources = resources; mContext = context; + mBouncerInteractor = bouncerInteractor; + mSceneContainerFlags = sceneContainerFlags; mAccessibilityDelegate = new View.AccessibilityDelegate() { private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint = @@ -256,6 +267,7 @@ public class LockIconViewController implements Dumpable { } /** Sets the LockIconView to the controller and rebinds any that depend on it. */ + @SuppressLint("ClickableViewAccessibility") public void setLockIconView(LockIconView lockIconView) { mView = lockIconView; mView.setImageDrawable(mIcon); @@ -305,6 +317,8 @@ public class LockIconViewController implements Dumpable { if (lockIconView.isAttachedToWindow()) { registerCallbacks(); } + + lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent)); } private void registerCallbacks() { @@ -635,19 +649,18 @@ public class LockIconViewController implements Dumpable { }; /** - * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true. + * Handles the touch if {@link #isActionable()} is true. * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon * area for {@link #mLongPressTimeout} ms. * * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}. */ - public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) { - if (!onInterceptTouchEvent(event)) { + private boolean onTouchEvent(MotionEvent event) { + if (!actionableDownEventStartedOnView(event)) { cancelTouches(); return false; } - mOnGestureDetectedRunnable = onGestureDetectedRunnable; switch(event.getActionMasked()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: @@ -700,12 +713,8 @@ public class LockIconViewController implements Dumpable { return true; } - /** - * Intercepts the touch if the onDown event and current event are within this lock icon view's - * bounds. - */ - public boolean onInterceptTouchEvent(MotionEvent event) { - if (!inLockIconArea(event) || !isActionable()) { + private boolean actionableDownEventStartedOnView(MotionEvent event) { + if (!isActionable()) { return false; } @@ -716,7 +725,8 @@ public class LockIconViewController implements Dumpable { return mDownDetected; } - private void onLongPress() { + @VisibleForTesting + protected void onLongPress() { cancelTouches(); if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) { Log.v(TAG, "lock icon long-press rejected by the falsing manager."); @@ -729,14 +739,15 @@ public class LockIconViewController implements Dumpable { mAuthRippleController.showUnlockRipple(FINGERPRINT); } updateVisibility(); - if (mOnGestureDetectedRunnable != null) { - mOnGestureDetectedRunnable.run(); - } // play device entry haptic (consistent with UDFPS controller longpress) vibrateOnLongPress(); - mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); + if (mSceneContainerFlags.isEnabled()) { + mBouncerInteractor.get().showOrUnlockDevice(null); + } else { + mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); + } } @@ -751,12 +762,6 @@ public class LockIconViewController implements Dumpable { } } - private boolean inLockIconArea(MotionEvent event) { - mView.getHitRect(mSensorTouchLocation); - return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) - && mView.getVisibility() == View.VISIBLE; - } - private boolean isActionable() { if (mIsBouncerShowing) { Log.v(TAG, "lock icon long-press ignored, bouncer already showing."); @@ -834,6 +839,19 @@ public class LockIconViewController implements Dumpable { } }; + /** + * Whether the lock icon will handle a touch while dozing. + */ + public boolean willHandleTouchWhileDozing(MotionEvent event) { + // is in lock icon area + mView.getHitRect(mSensorTouchLocation); + final boolean inLockIconArea = + mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) + && mView.getVisibility() == View.VISIBLE; + + return inLockIconArea && actionableDownEventStartedOnView(event); + } + private final View.OnClickListener mA11yClickListener = v -> onLongPress(); private final AccessibilityManager.AccessibilityStateChangeListener diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt new file mode 100644 index 000000000000..c5817881718b --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt @@ -0,0 +1,40 @@ +package com.android.keyguard.dagger + +import android.content.Context +import android.content.res.Resources +import android.view.Display +import com.android.systemui.dagger.qualifiers.DisplaySpecific +import dagger.BindsOptionalOf +import dagger.Module +import dagger.Provides +import java.util.Optional + +/** + * Binds display specific context and resources. + * + * When a [Display] is available in the scope, binds a [DisplaySpecific] [Context] and [Resources]. + * When not available, the default display context and resources are used. + */ +@Module +abstract class KeyguardDisplayModule { + + @BindsOptionalOf abstract fun optionalDisplay(): Display + + companion object { + @Provides + @DisplaySpecific + fun getDisplayContext(context: Context, display: Optional<Display>): Context { + return if (display.isPresent) { + context.createDisplayContext(display.get()) + } else { + context + } + } + + @Provides + @DisplaySpecific + fun getDisplayResources(@DisplaySpecific context: Context): Resources { + return context.resources + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java index d342377da49b..f3014f1ecc58 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java @@ -16,6 +16,8 @@ package com.android.keyguard.dagger; +import android.view.Display; + import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; @@ -28,13 +30,17 @@ import dagger.Subcomponent; * * TODO: unify this with {@link KeyguardStatusBarViewComponent} */ -@Subcomponent(modules = {KeyguardStatusViewModule.class}) +@Subcomponent(modules = {KeyguardStatusViewModule.class, KeyguardDisplayModule.class}) @KeyguardStatusViewScope public interface KeyguardStatusViewComponent { /** Simple factory for {@link KeyguardStatusViewComponent}. */ @Subcomponent.Factory interface Factory { - KeyguardStatusViewComponent build(@BindsInstance KeyguardStatusView presentation); + /** Creates {@link KeyguardStatusViewComponent} for a given display. */ + KeyguardStatusViewComponent build( + @BindsInstance KeyguardStatusView presentation, + @BindsInstance Display display + ); } /** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */ diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 018038423d4e..7739021bad33 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -87,7 +87,6 @@ import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -130,14 +129,14 @@ import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakReporter; import com.android.systemui.util.sensors.AsyncSensorManager; +import dagger.Lazy; + import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; -import dagger.Lazy; - /** * Class to handle ugly dependencies throughout sysui until we determine the * long-term dependency injection solution. @@ -298,7 +297,6 @@ public class Dependency { @Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController; @Inject Lazy<StatusBarStateController> mStatusBarStateController; @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager; - @Inject Lazy<NotificationGutsManager> mNotificationGutsManager; @Inject Lazy<NotificationMediaManager> mNotificationMediaManager; @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager; @Inject Lazy<SmartReplyConstants> mSmartReplyConstants; @@ -498,7 +496,6 @@ public class Dependency { mProviders.put(NotificationLockscreenUserManager.class, mNotificationLockscreenUserManager::get); mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get); - mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get); mProviders.put(NotificationRemoteInputManager.class, mNotificationRemoteInputManager::get); mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get); diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 4541384aaa37..b573fadea15e 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -28,6 +28,7 @@ import com.android.systemui.res.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.settings.UserTracker; @@ -46,6 +47,7 @@ import dagger.assisted.AssistedInject; /** * Manages notification when a guest session is resumed. */ +@SysUISingleton public class GuestResumeSessionReceiver { @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt index 4efc21b41e6a..4b78e9eeb265 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModel.kt @@ -35,10 +35,8 @@ import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit * The input is guaranteed to always contain a initial [ClearAll] token as a sentinel, thus clients * can always assume there is a 'ClearAll' watermark available. */ -data class PinInputViewModel -internal constructor( - @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal val input: List<EntryToken> +data class PinInputViewModel( + @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) val input: List<EntryToken> ) { init { require(input.firstOrNull() is ClearAll) { "input does not begin with a ClearAll token" } @@ -132,8 +130,7 @@ sealed interface EntryToken : Comparable<EntryToken> { val sequenceNumber: Int /** The pin bouncer [input] as digits 0-9. */ - data class Digit - internal constructor(val input: Int, override val sequenceNumber: Int = nextSequenceNumber++) : + data class Digit(val input: Int, override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken { init { check(input in 0..9) @@ -144,8 +141,7 @@ sealed interface EntryToken : Comparable<EntryToken> { * Marker to indicate the input is completely cleared, and subsequent [EntryToken]s mark a new * pin entry. */ - data class ClearAll - internal constructor(override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken + data class ClearAll(override val sequenceNumber: Int = nextSequenceNumber++) : EntryToken override fun compareTo(other: EntryToken): Int = compareValuesBy(this, other, EntryToken::sequenceNumber) diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java index c0ee71cf0dc8..0dfaf0f4318d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java @@ -18,8 +18,15 @@ package com.android.systemui.classifier; import android.view.MotionEvent; +import javax.inject.Inject; + /** */ public class FalsingCollectorFake implements FalsingCollector { + + @Inject + public FalsingCollectorFake() { + } + @Override public void onSuccessfulUnlock() { } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index 046ccf165d07..a90980fddfb0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -18,7 +18,6 @@ package com.android.systemui.dagger; import com.android.systemui.globalactions.ShutdownUiModule; import com.android.systemui.keyguard.CustomizationProvider; -import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; @@ -33,7 +32,6 @@ import dagger.Subcomponent; DependencyProvider.class, NotificationInsetsModule.class, QsFrameTranslateModule.class, - ShadeModule.class, ShutdownUiModule.class, SystemUIBinder.class, SystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 9e5fd5572dbc..1dd4abfa0767 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -21,12 +21,9 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; import android.hardware.SensorPrivacyManager; -import android.os.Handler; -import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardViewController; import com.android.systemui.battery.BatterySaverModule; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; @@ -34,7 +31,6 @@ import com.android.systemui.media.dagger.MediaModule; import com.android.systemui.navigationbar.NavigationBarControllerModule; import com.android.systemui.navigationbar.gestural.GestureModule; import com.android.systemui.plugins.qs.QSFactory; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.dagger.PowerModule; import com.android.systemui.qs.dagger.QSModule; import com.android.systemui.qs.tileimpl.QSFactoryImpl; @@ -45,7 +41,7 @@ import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; -import com.android.systemui.shade.ShadeExpansionStateManager; +import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyboardShortcutsModule; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -53,20 +49,13 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule; import com.android.systemui.statusbar.events.StatusBarEventsModule; -import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; -import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.phone.DozeServiceHost; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.HeadsUpModule; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule; -import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.AospPolicyModule; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl; import com.android.systemui.statusbar.policy.SensorPrivacyController; @@ -100,11 +89,13 @@ import javax.inject.Named; BatterySaverModule.class, CollapsedStatusBarFragmentStartableModule.class, GestureModule.class, + HeadsUpModule.class, MediaModule.class, MultiUserUtilsModule.class, NavigationBarControllerModule.class, PowerModule.class, QSModule.class, + ShadeModule.class, ReferenceScreenshotModule.class, RotationLockModule.class, SceneContainerFrameworkModule.class, @@ -161,38 +152,6 @@ public abstract class ReferenceSystemUIModule { return true; } - @SysUISingleton - @Provides - static HeadsUpManagerPhone provideHeadsUpManagerPhone( - Context context, - HeadsUpManagerLogger headsUpManagerLogger, - StatusBarStateController statusBarStateController, - KeyguardBypassController bypassController, - GroupMembershipManager groupManager, - VisualStabilityProvider visualStabilityProvider, - ConfigurationController configurationController, - @Main Handler handler, - AccessibilityManagerWrapper accessibilityManagerWrapper, - UiEventLogger uiEventLogger, - ShadeExpansionStateManager shadeExpansionStateManager) { - return new HeadsUpManagerPhone( - context, - headsUpManagerLogger, - statusBarStateController, - bypassController, - groupManager, - visualStabilityProvider, - configurationController, - handler, - accessibilityManagerWrapper, - uiEventLogger, - shadeExpansionStateManager - ); - } - - @Binds - abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); - @Provides @SysUISingleton static Recents provideRecents(Context context, RecentsImplementation recentsImplementation, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index d89332d6d686..5d6949b3e87f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -37,6 +37,7 @@ import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable +import com.android.systemui.keyguard.ui.binder.AlternateBouncerBinder import com.android.systemui.keyguard.ui.binder.KeyguardDismissActionBinder import com.android.systemui.keyguard.ui.binder.KeyguardDismissBinder import com.android.systemui.log.SessionTracker @@ -92,6 +93,11 @@ abstract class SystemUICoreStartableModule { @ClassKey(AuthController::class) abstract fun bindAuthController(service: AuthController): CoreStartable + @Binds + @IntoMap + @ClassKey(AlternateBouncerBinder::class) + abstract fun bindAlternateBouncerBinder(impl: AlternateBouncerBinder): CoreStartable + /** Inject into BiometricNotificationService */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt index 5b85ad01301b..1e29e1fa3197 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt @@ -2,7 +2,7 @@ package com.android.systemui.deviceentry.data.repository import com.android.internal.widget.LockPatternUtils import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging -import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -51,7 +51,7 @@ interface DeviceEntryRepository { * When this is `false`, an automatically-triggered face unlock shouldn't automatically dismiss * the lockscreen. */ - fun isBypassEnabled(): Boolean + val isBypassEnabled: StateFlow<Boolean> } /** Encapsulates application state for device entry. */ @@ -68,7 +68,7 @@ constructor( ) : DeviceEntryRepository { override val isUnlocked = - ConflatedCallbackFlow.conflatedCallbackFlow { + conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { override fun onUnlockedChanged() { @@ -112,7 +112,24 @@ constructor( } } - override fun isBypassEnabled() = keyguardBypassController.bypassEnabled + override val isBypassEnabled: StateFlow<Boolean> = + conflatedCallbackFlow { + val listener = + object : KeyguardBypassController.OnBypassStateChangedListener { + override fun onBypassStateChanged(isEnabled: Boolean) { + trySend(isEnabled) + } + } + keyguardBypassController.registerOnBypassStateChangedListener(listener) + awaitClose { + keyguardBypassController.unregisterOnBypassStateChangedListener(listener) + } + } + .stateIn( + applicationScope, + SharingStarted.Eagerly, + initialValue = keyguardBypassController.bypassEnabled, + ) companion object { private const val TAG = "DeviceEntryRepositoryImpl" diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 5612c9a488ff..e96e318fd59d 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -106,5 +106,5 @@ constructor( * authentication challenge via face unlock or fingerprint sensor can automatically bypass the * lock screen. */ - fun isBypassEnabled() = repository.isBypassEnabled() + val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index bdd245ed7b7f..f6f24e0aecc0 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -144,11 +144,6 @@ object Flags { "lockscreen_custom_clocks" ) - // TODO(b/275694445): Tracking Bug - @JvmField - val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = - releasedFlag("lockscreen_without_secure_lock_when_dreaming") - // TODO(b/286092087): Tracking Bug @JvmField val ENABLE_SYSTEM_UI_DREAM_CONTROLLER = unreleasedFlag("enable_system_ui_dream_controller") @@ -234,8 +229,7 @@ object Flags { /** Whether page transition animations in the wallpaper picker are enabled */ // TODO(b/291710220): Tracking bug. @JvmField - val WALLPAPER_PICKER_PAGE_TRANSITIONS = - unreleasedFlag("wallpaper_picker_page_transitions", teamfood = true) + val WALLPAPER_PICKER_PAGE_TRANSITIONS = releasedFlag("wallpaper_picker_page_transitions") /** Add "Apply" button to wall paper picker's grid preview page. */ // TODO(b/294866904): Tracking bug. @@ -310,6 +304,11 @@ object Flags { @JvmField val WALLPAPER_PICKER_PREVIEW_ANIMATION = releasedFlag("wallpaper_picker_preview_animation") + /** Flag to enable rest to unlock feature. */ + // TODO(b/303672286): Tracking bug + @JvmField + val REST_TO_UNLOCK: UnreleasedFlag = unreleasedFlag("rest_to_unlock") + /** * TODO(b/278086361): Tracking bug * Complete rewrite of the interactions between System UI and Window Manager involving keyguard @@ -332,7 +331,7 @@ object Flags { /** Flag to use a separate view for the alternate bouncer. */ // TODO(b/300440924): Tracking bug @JvmField - val ALTERNATE_BOUNCER_REFACTOR: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view") + val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view") // 300 - power menu // TODO(b/254512600): Tracking Bug diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index f500017e3a33..86bf368791bc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -82,7 +82,8 @@ constructor( val statusViewComponent = keyguardStatusViewComponentFactory.build( LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, null) - as KeyguardStatusView + as KeyguardStatusView, + context.display ) val controller = statusViewComponent.keyguardStatusViewController controller.init() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7678f4d643ca..39742a02000b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -171,6 +171,8 @@ import com.android.systemui.util.time.SystemClock; import com.android.systemui.wallpapers.data.repository.WallpaperRepository; import com.android.wm.shell.keyguard.KeyguardTransitions; +import dagger.Lazy; + import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -180,7 +182,8 @@ import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; -import dagger.Lazy; + + import kotlinx.coroutines.CoroutineDispatcher; /** @@ -1933,11 +1936,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void onDreamingStarted() { mUpdateMonitor.dispatchDreamingStarted(); synchronized (this) { - final boolean alwaysShowKeyguard = - mFeatureFlags.isEnabled(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING); - if (mDeviceInteractive - && (alwaysShowKeyguard || - mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()))) { + if (mDeviceInteractive) { doKeyguardLaterLocked(); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 03c166c05a62..41bde91d9a01 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -28,6 +28,7 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.dagger.KeyguardDisplayModule; import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; @@ -100,6 +101,7 @@ import kotlinx.coroutines.CoroutineDispatcher; KeyguardDataQuickAffordanceModule.class, KeyguardRepositoryModule.class, KeyguardFaceAuthModule.class, + KeyguardDisplayModule.class, StartKeyguardTransitionModule.class, ResourceTrimmerModule.class, }) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 3ccf446fef59..d06f31fed8db 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds @SysUISingleton class FromAlternateBouncerTransitionInteractor @@ -129,11 +130,11 @@ constructor( override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { return ValueAnimator().apply { interpolator = Interpolators.LINEAR - duration = TRANSITION_DURATION_MS + duration = TRANSITION_DURATION_MS.inWholeMilliseconds } } companion object { - private const val TRANSITION_DURATION_MS = 300L + val TRANSITION_DURATION_MS = 300.milliseconds } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt index e57c919a5b3e..89aca7631934 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt @@ -61,6 +61,7 @@ interface KeyguardFaceAuthInteractor { fun onSwipeUpOnBouncer() fun onPrimaryBouncerUserInput() fun onAccessibilityAction() + fun onWalletLaunched() } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt index d69876292024..ac012f840d1f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt @@ -29,8 +29,10 @@ import com.android.systemui.shade.ShadeController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi /** Handles key events arriving when the keyguard is showing or device is dozing. */ +@ExperimentalCoroutinesApi @SysUISingleton class KeyguardKeyEventInteractor @Inject @@ -56,7 +58,11 @@ constructor( if (event.handleAction()) { when (event.keyCode) { KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent() - KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent() + KeyEvent.KEYCODE_SPACE, + KeyEvent.KEYCODE_ENTER -> + if (isDeviceAwake()) { + return collapseShadeLockedOrShowPrimaryBouncer() + } } } return false @@ -92,16 +98,24 @@ constructor( (statusBarStateController.state != StatusBarState.SHADE) && statusBarKeyguardViewManager.shouldDismissOnMenuPressed() if (shouldUnlockOnMenuPressed) { - shadeController.animateCollapseShadeForced() - return true + return collapseShadeLockedOrShowPrimaryBouncer() } return false } - private fun dispatchSpaceEvent(): Boolean { - if (isDeviceAwake() && statusBarStateController.state != StatusBarState.SHADE) { - shadeController.animateCollapseShadeForced() - return true + private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean { + when (statusBarStateController.state) { + StatusBarState.SHADE -> return false + StatusBarState.SHADE_LOCKED -> { + shadeController.animateCollapseShadeForced() + return true + } + StatusBarState.KEYGUARD -> { + if (!statusBarKeyguardViewManager.primaryBouncerIsShowing()) { + statusBarKeyguardViewManager.showPrimaryBouncer(true) + return true + } + } } return false } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt index 596a1c01ca42..f38bb2b519e7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt @@ -61,4 +61,5 @@ class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInt override fun onSwipeUpOnBouncer() {} override fun onPrimaryBouncerUserInput() {} override fun onAccessibilityAction() {} + override fun onWalletLaunched() = Unit } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt index ef1d5ac2a6d4..797dec2c9625 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.CoreStartable import com.android.systemui.biometrics.data.repository.FacePropertyRepository import com.android.systemui.biometrics.shared.model.LockoutMode +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton @@ -33,7 +34,6 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.TransitionState @@ -44,6 +44,7 @@ import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -56,7 +57,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.yield -import javax.inject.Inject /** * Encapsulates business logic related face authentication being triggered for device entry from @@ -79,7 +79,6 @@ constructor( private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, private val userRepository: UserRepository, private val facePropertyRepository: FacePropertyRepository, - private val keyguardRepository: KeyguardRepository, private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig, private val powerInteractor: PowerInteractor, ) : CoreStartable, KeyguardFaceAuthInteractor { @@ -207,6 +206,12 @@ constructor( runFaceAuth(FaceAuthUiEvent.FACE_AUTH_ACCESSIBILITY_ACTION, false) } + override fun onWalletLaunched() { + if (facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG) { + runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true) + } + } + override fun registerListener(listener: FaceAuthenticationListener) { listeners.add(listener) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt new file mode 100644 index 000000000000..41af9e810fbf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.binder + +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.scrim.ScrimView +import com.android.systemui.shade.NotificationShadeWindowView +import com.android.systemui.statusbar.NotificationShadeWindowController +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import javax.inject.Inject + +@ExperimentalCoroutinesApi +@SysUISingleton +class AlternateBouncerBinder +@Inject +constructor( + private val notificationShadeWindowView: NotificationShadeWindowView, + private val featureFlags: FeatureFlagsClassic, + private val alternateBouncerViewModel: AlternateBouncerViewModel, + @Application private val scope: CoroutineScope, + private val notificationShadeWindowController: NotificationShadeWindowController, +) : CoreStartable { + override fun start() { + if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + return + } + + AlternateBouncerViewBinder.bind( + notificationShadeWindowView.requireViewById(R.id.alternate_bouncer), + alternateBouncerViewModel, + scope, + notificationShadeWindowController, + ) + } +} + +/** + * Binds the alternate bouncer view to its view-model. + * + * To use this properly, users should maintain a one-to-one relationship between the [View] and the + * view-binding, binding each view only once. It is okay and expected for the same instance of the + * view-model to be reused for multiple view/view-binder bindings. + */ +@ExperimentalCoroutinesApi +object AlternateBouncerViewBinder { + + /** Binds the view to the view-model, continuing to update the former based on the latter. */ + @JvmStatic + fun bind( + view: ViewGroup, + viewModel: AlternateBouncerViewModel, + scope: CoroutineScope, + notificationShadeWindowController: NotificationShadeWindowController, + ) { + scope.launch { + // forcePluginOpen is necessary to show over occluded apps. + // This cannot be tied to the view's lifecycle because setting this allows the view + // to be started in the first place. + viewModel.forcePluginOpen.collect { + notificationShadeWindowController.setForcePluginOpen(it, this) + } + } + + val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView + view.repeatWhenAttached { alternateBouncerViewContainer -> + repeatOnLifecycle(Lifecycle.State.STARTED) { + scrim.viewAlpha = 0f + + launch { + viewModel.onClickListener.collect { + // TODO (b/287599719): Support swiping to dismiss altBouncer + alternateBouncerViewContainer.setOnClickListener(it) + } + } + + launch { + viewModel.scrimAlpha.collect { + alternateBouncerViewContainer.visibility = + if (it < .1f) View.INVISIBLE else View.VISIBLE + scrim.viewAlpha = it + } + } + + launch { viewModel.scrimColor.collect { scrim.tint = it } } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index 5ae2abaea788..c6d8ec7789f2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -27,11 +27,14 @@ 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 @@ -125,6 +128,8 @@ constructor( private val shouldHideClock: Boolean = bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false) private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS) + private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY) + private val display: Display = displayManager.getDisplay(displayId) private var host: SurfaceControlViewHost @@ -164,7 +169,7 @@ constructor( host = SurfaceControlViewHost( context, - displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)), + displayManager.getDisplay(DEFAULT_DISPLAY), hostToken, "KeyguardPreviewRenderer" ) @@ -174,21 +179,27 @@ constructor( fun render() { mainHandler.post { - val rootView = FrameLayout(context) + val previewContext = context.createDisplayContext(display) - setupKeyguardRootView(rootView) + val rootView = FrameLayout(previewContext) + + setupKeyguardRootView(previewContext, rootView) if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { setUpBottomArea(rootView) } + val windowContext = context.createWindowContext(display, TYPE_KEYGUARD, null) + val windowManagerOfDisplay = windowContext.getSystemService(WindowManager::class.java) rootView.measure( View.MeasureSpec.makeMeasureSpec( - windowManager.currentWindowMetrics.bounds.width(), + windowManagerOfDisplay?.currentWindowMetrics?.bounds?.width() + ?: windowManager.currentWindowMetrics.bounds.width(), View.MeasureSpec.EXACTLY ), View.MeasureSpec.makeMeasureSpec( - windowManager.currentWindowMetrics.bounds.height(), + windowManagerOfDisplay?.currentWindowMetrics?.bounds?.height() + ?: windowManager.currentWindowMetrics.bounds.height(), View.MeasureSpec.EXACTLY ), ) @@ -251,7 +262,7 @@ constructor( * * The end padding is as follows: Below clock padding end */ - private fun setUpSmartspace(parentView: ViewGroup) { + private fun setUpSmartspace(previewContext: Context, parentView: ViewGroup) { if ( !lockscreenSmartspaceController.isEnabled() || !lockscreenSmartspaceController.isDateWeatherDecoupled() @@ -263,12 +274,12 @@ constructor( val topPadding: Int = KeyguardPreviewSmartspaceViewModel.getLargeClockSmartspaceTopPadding( - context.resources, + previewContext.resources, ) val startPadding: Int = - context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) + previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) val endPadding: Int = - context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) + previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) smartSpaceView?.let { it.setPaddingRelative(startPadding, topPadding, endPadding, 0) @@ -308,8 +319,8 @@ constructor( } @OptIn(ExperimentalCoroutinesApi::class) - private fun setupKeyguardRootView(rootView: FrameLayout) { - val keyguardRootView = KeyguardRootView(context, null).apply { removeAllViews() } + private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) { + val keyguardRootView = KeyguardRootView(previewContext, null).apply { removeAllViews() } disposables.add( KeyguardRootViewBinder.bind( keyguardRootView, @@ -333,10 +344,10 @@ constructor( if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { setupShortcuts(keyguardRootView) } - setUpUdfps(rootView) + setUpUdfps(previewContext, rootView) if (!shouldHideClock) { - setUpClock(rootView) + setUpClock(previewContext, rootView) KeyguardPreviewClockViewBinder.bind( largeClockHostView, smallClockHostView, @@ -344,7 +355,7 @@ constructor( ) } - setUpSmartspace(rootView) + setUpSmartspace(previewContext, rootView) smartSpaceView?.let { KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel) } @@ -381,7 +392,7 @@ constructor( } } - private fun setUpUdfps(parentView: ViewGroup) { + private fun setUpUdfps(previewContext: Context, parentView: ViewGroup) { val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds // If sensorBounds are default rect, then there is no UDFPS @@ -399,7 +410,7 @@ constructor( sensorBounds.bottom ) val finger = - LayoutInflater.from(context) + LayoutInflater.from(previewContext) .inflate( R.layout.udfps_keyguard_preview, parentView, @@ -408,12 +419,12 @@ constructor( parentView.addView(finger, fingerprintLayoutParams) } - private fun setUpClock(parentView: ViewGroup) { + private fun setUpClock(previewContext: Context, parentView: ViewGroup) { largeClockHostView = if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view_large) } else { - val hostView = FrameLayout(context) + val hostView = FrameLayout(previewContext) hostView.layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, @@ -429,7 +440,7 @@ constructor( parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view) } else { val resources = parentView.resources - val hostView = FrameLayout(context) + val hostView = FrameLayout(previewContext) val layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt index 1a9f021a7622..0b0f21d7a281 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt @@ -31,12 +31,12 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.keyguard.KeyguardStatusView import com.android.keyguard.dagger.KeyguardStatusViewComponent -import com.android.systemui.res.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.media.controls.ui.KeyguardMediaController +import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.statusbar.policy.SplitShadeStateController @@ -84,7 +84,8 @@ constructor( override fun bindData(constraintLayout: ConstraintLayout) { if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let { - val statusViewComponent = keyguardStatusViewComponentFactory.build(it) + val statusViewComponent = + keyguardStatusViewComponentFactory.build(it, context.display) val controller = statusViewComponent.keyguardStatusViewController controller.init() keyguardMediaController.attachSplitShadeContainer( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt new file mode 100644 index 000000000000..235a28d4ebc5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import android.graphics.Color +import android.view.View +import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.wm.shell.animation.Interpolators +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +@ExperimentalCoroutinesApi +class AlternateBouncerViewModel +@Inject +constructor( + statusBarKeyguardViewManager: StatusBarKeyguardViewManager, + transitionInteractor: KeyguardTransitionInteractor, + falsingManager: FalsingManager, +) { + // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: + private val alternateBouncerScrimAlpha = .66f + private val toAlternateBouncerTransition = + KeyguardTransitionAnimationFlow( + transitionDuration = TRANSITION_DURATION_MS, + transitionFlow = transitionInteractor.anyStateToAlternateBouncerTransition, + ) + .createFlow( + duration = TRANSITION_DURATION_MS, + onStep = { it }, + onFinish = { 1f }, + // Reset on cancel + onCancel = { 0f }, + interpolator = Interpolators.FAST_OUT_SLOW_IN, + ) + private val fromAlternateBouncerTransition = + KeyguardTransitionAnimationFlow( + transitionDuration = TRANSITION_DURATION_MS, + transitionFlow = transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER), + ) + .createFlow( + duration = TRANSITION_DURATION_MS, + onStep = { 1f - it }, + // Reset on cancel + onCancel = { 0f }, + interpolator = Interpolators.FAST_OUT_SLOW_IN, + ) + + /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */ + private val transitionToAlternateBouncerProgress = + merge(fromAlternateBouncerTransition, toAlternateBouncerTransition) + + val forcePluginOpen: Flow<Boolean> = + transitionToAlternateBouncerProgress.map { it > 0f }.distinctUntilChanged() + + /** An observable for the scrim alpha. */ + val scrimAlpha = transitionToAlternateBouncerProgress.map { it * alternateBouncerScrimAlpha } + + /** An observable for the scrim color. Change color for easier debugging. */ + val scrimColor: Flow<Int> = flowOf(Color.BLACK) + + private val clickListener = + View.OnClickListener { + if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true) + } + } + + val onClickListener: Flow<View.OnClickListener?> = + transitionToAlternateBouncerProgress + .map { + if (it == 1f) { + clickListener + } else { + null + } + } + .distinctUntilChanged() +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt index 54abc5bc695f..0b1079f69d6e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt @@ -19,13 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context import androidx.annotation.ColorInt import com.android.settingslib.Utils.getColorAttrDefaultColor -import com.android.systemui.res.R import com.android.systemui.keyguard.domain.interactor.BurnInOffsets import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.res.R import com.android.wm.shell.animation.Interpolators import javax.inject.Inject import kotlin.math.roundToInt @@ -62,7 +62,7 @@ open class UdfpsLockscreenViewModel( private val toAlternateBouncer: Flow<TransitionViewModel> = keyguardInteractor.statusBarState.flatMapLatest { statusBarState -> - transitionInteractor.anyStateToAlternateBouncerTransition.map { + transitionInteractor.transitionStepsToState(KeyguardState.ALTERNATE_BOUNCER).map { TransitionViewModel( alpha = 1f, scale = diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java index 68202d5629a0..80be76661098 100644 --- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java @@ -38,6 +38,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.VibrationEffect; +import android.os.vibrator.Flags; import android.provider.MediaStore; import android.util.Log; @@ -111,7 +112,7 @@ public class RingtonePlayer implements CoreStartable { @Override public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping) throws RemoteException { - if (Ringtone.useRingtoneV2()) { + if (Flags.hapticsCustomizationRingtoneV2Enabled()) { playRemoteRingtone(token, uri, aa, true, Ringtone.MEDIA_SOUND, null, volume, looping, /* hapticGenerator= */ false, null); diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index 2b56d0cf9f83..d08d0400f354 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -239,6 +239,8 @@ public class MediaProjectionPermissionActivity extends Activity protected void onDestroy() { super.onDestroy(); if (mDialog != null) { + mDialog.setOnDismissListener(null); + mDialog.setOnCancelListener(null); mDialog.dismiss(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java index 8589ae9305fd..68bf88b16ae2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java @@ -72,7 +72,7 @@ public class QSFragmentLegacy extends LifecycleFragment implements QS { @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this); + QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(getView()); mQsImpl = mQsImplProvider.get(); mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt index 2a36fdb21e18..65a2c8ca692b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt @@ -105,7 +105,7 @@ constructor( override fun removeCallback(callback: QSHost.Callback) { if (useNewHost) { - synchronized(callbacksMap) { callbacksMap.get(callback)?.cancel() } + synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() } } else { qsTileHost.removeCallback(callback) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java index a32a024dbb44..202254bb323f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java @@ -199,11 +199,13 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner(); } + /** + * This method will set up all the necessary fields. Methods from the implemented interfaces + * should not be called before this method returns. + */ public void onComponentCreated(QSComponent qsComponent, @Nullable Bundle savedInstanceState) { mRootView = qsComponent.getRootView(); - mCommandQueue.addCallback(this); - mQSPanelController = qsComponent.getQSPanelController(); mQuickQSPanelController = qsComponent.getQuickQSPanelController(); @@ -270,6 +272,9 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl mQSPanelController.getMediaHost().getHostView().setAlpha(1.0f); mQSAnimator.requestAnimatorUpdate(); }); + + // This will immediately call disable, so it needs to be added after setting up the fields. + mCommandQueue.addCallback(this); } private void bindFooterActionsView(View root) { @@ -323,6 +328,8 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl public void onDestroy() { mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); + mQSPanelController.destroy(); + mQuickQSPanelController.destroy(); if (mListening) { setListening(false); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 60c92c000760..fc2f5f9c7226 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -158,6 +158,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr protected void onInit() { mView.initialize(mQSLogger); mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), ""); + mHost.addCallback(mQSHostCallback); } /** @@ -172,6 +173,18 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } @Override + public void destroy() { + super.destroy(); + mHost.removeCallback(mQSHostCallback); + + for (TileRecord record : mRecords) { + record.tile.removeCallback(record.callback); + mView.removeTile(record); + } + mRecords.clear(); + } + + @Override protected void onViewAttached() { mQsTileRevealController = createTileRevealController(); if (mQsTileRevealController != null) { @@ -180,7 +193,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener); mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener); - mHost.addCallback(mQSHostCallback); setTiles(); mLastOrientation = getResources().getConfiguration().orientation; mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag()); @@ -193,16 +205,11 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr protected void onViewDetached() { mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag()); mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); - mHost.removeCallback(mQSHostCallback); mView.getTileLayout().setListening(false, mUiEventLogger); mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener); - for (TileRecord record : mRecords) { - record.tile.removeCallback(record.callback); - } - mRecords.clear(); mDumpManager.unregisterDumpable(mView.getDumpableTag()); } @@ -222,15 +229,30 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr if (!collapsedView && mQsTileRevealController != null) { mQsTileRevealController.updateRevealedTiles(tiles); } - - for (QSPanelControllerBase.TileRecord record : mRecords) { - mView.removeTile(record); - record.tile.removeCallback(record.callback); + boolean shouldChange = false; + if (tiles.size() == mRecords.size()) { + int i = 0; + for (QSTile tile : tiles) { + if (tile != mRecords.get(i).tile) { + shouldChange = true; + break; + } + i++; + } + } else { + shouldChange = true; } - mRecords.clear(); - mCachedSpecs = ""; - for (QSTile tile : tiles) { - addTile(tile, collapsedView); + + if (shouldChange) { + for (QSPanelControllerBase.TileRecord record : mRecords) { + mView.removeTile(record); + record.tile.removeCallback(record.callback); + } + mRecords.clear(); + mCachedSpecs = ""; + for (QSTile tile : tiles) { + addTile(tile, collapsedView); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java index 327e858059f3..ce8db789842a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java @@ -16,6 +16,9 @@ package com.android.systemui.qs.dagger; +import android.view.View; + +import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.qs.QSFragmentLegacy; import dagger.BindsInstance; @@ -31,6 +34,7 @@ public interface QSFragmentComponent extends QSComponent { /** Factory for building a {@link QSFragmentComponent}. */ @Subcomponent.Factory interface Factory { - QSFragmentComponent create(@BindsInstance QSFragmentLegacy qsFragment); + /** */ + QSFragmentComponent create(@BindsInstance @RootView View view); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java index 0c9c24df5e36..0e75b2157a7d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java @@ -20,34 +20,17 @@ import static com.android.systemui.util.Utils.useCollapsedMediaInLandscape; import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.content.Context; -import android.view.View; -import com.android.systemui.dagger.qualifiers.RootView; -import com.android.systemui.plugins.qs.QS; -import com.android.systemui.qs.QSFragmentLegacy; - -import javax.inject.Named; - -import dagger.Binds; import dagger.Module; import dagger.Provides; +import javax.inject.Named; + /** * Dagger Module for {@link QSFragmentComponent}. */ @Module(includes = {QSScopeModule.class}) public interface QSFragmentModule { - - @Provides - @RootView - static View provideRootView(QSFragmentLegacy qsFragment) { - return qsFragment.getView(); - } - - /** */ - @Binds - QS bindQS(QSFragmentLegacy qsFragment); - /** */ @Provides @Named(QSScopeModule.QS_USING_MEDIA_PLAYER) diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index c5512c15ccc2..4bb8c6e4bb2d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -272,7 +272,6 @@ constructor( // repository launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } } - Log.d("Fabian", "Finished resolving tiles") } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 584456d79890..91b4d1778e1c 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -42,6 +42,7 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICA import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.distinctUntilChanged @@ -51,7 +52,6 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch -import javax.inject.Inject /** * Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI @@ -142,7 +142,7 @@ constructor( // When the device becomes unlocked in Lockscreen, go to Gone if // bypass is enabled. renderedScenes.contains(SceneKey.Lockscreen) -> - if (deviceEntryInteractor.isBypassEnabled()) { + if (deviceEntryInteractor.isBypassEnabled.value) { SceneKey.Gone to "device unlocked in Lockscreen scene with bypass" } else { @@ -179,36 +179,34 @@ constructor( } applicationScope.launch { - powerInteractor.isAsleep - .collect { isAsleep -> - if (isAsleep) { - switchToScene( - targetSceneKey = SceneKey.Lockscreen, - loggingReason = "device is starting to sleep", - ) - } else { - val authMethod = authenticationInteractor.getAuthenticationMethod() - val isUnlocked = deviceEntryInteractor.isUnlocked.value - when { - authMethod == AuthenticationMethodModel.None -> { - switchToScene( - targetSceneKey = SceneKey.Gone, - loggingReason = - "device is starting to wake up while auth method is" + - " none", - ) - } - authMethod.isSecure && isUnlocked -> { - switchToScene( - targetSceneKey = SceneKey.Gone, - loggingReason = - "device is starting to wake up while unlocked with a" + - " secure auth method", - ) - } + powerInteractor.isAsleep.collect { isAsleep -> + if (isAsleep) { + switchToScene( + targetSceneKey = SceneKey.Lockscreen, + loggingReason = "device is starting to sleep", + ) + } else { + val authMethod = authenticationInteractor.getAuthenticationMethod() + val isUnlocked = deviceEntryInteractor.isUnlocked.value + when { + authMethod == AuthenticationMethodModel.None -> { + switchToScene( + targetSceneKey = SceneKey.Gone, + loggingReason = + "device is starting to wake up while auth method is" + " none", + ) + } + authMethod.isSecure && isUnlocked -> { + switchToScene( + targetSceneKey = SceneKey.Gone, + loggingReason = + "device is starting to wake up while unlocked with a" + + " secure auth method", + ) } } } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt index 3927873f8ba8..f704894e56e2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt @@ -42,6 +42,13 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isUserInputDriven: Boolean, + val isInitiatedByUserInput: Boolean, + + /** + * Whether user input is currently driving the transition. For example, if a user is + * dragging a pointer, this emits true. Once they lift their finger, this emits false while + * the transition completes/settles. + */ + val isUserInputOngoing: Flow<Boolean>, ) : ObservableTransitionState() } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt index 4bc93a8f1ca5..2e45353634fe 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt @@ -61,12 +61,17 @@ sealed interface UserAction { data class Swipe( /** The direction of the swipe. */ val direction: Direction, + /** + * The edge from which the swipe originated or `null`, if the swipe didn't start close to an + * edge. + */ + val fromEdge: Edge? = null, /** The number of pointers that were used (for example, one or two fingers). */ val pointerCount: Int = 1, ) : UserAction /** The user has hit the back button or performed the back navigation gesture. */ - object Back : UserAction + data object Back : UserAction } /** Enumerates all known "cardinal" directions for user actions. */ @@ -76,3 +81,11 @@ enum class Direction { RIGHT, DOWN, } + +/** Enumerates all known edges from which a swipe can start. */ +enum class Edge { + LEFT, + TOP, + RIGHT, + BOTTOM, +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index 2f873019349c..393a698bcdb7 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -132,6 +132,7 @@ open class UserTrackerImpl internal constructor( setUserIdInternal(startingUser) val filter = IntentFilter().apply { + addAction(Intent.ACTION_LOCALE_CHANGED) addAction(Intent.ACTION_USER_INFO_CHANGED) // These get called when a managed profile goes in or out of quiet mode. addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) @@ -149,6 +150,7 @@ open class UserTrackerImpl internal constructor( override fun onReceive(context: Context, intent: Intent) { when (intent.action) { + Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_USER_INFO_CHANGED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java index 9235fcc8202f..c42fdf8e7b93 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java @@ -67,8 +67,6 @@ public class DebugDrawable extends Drawable { mDebugPaint.setStrokeWidth(2); mDebugPaint.setStyle(Paint.Style.STROKE); mDebugPaint.setTextSize(24); - String headerDebugInfo = mNotificationPanelViewController.getHeaderDebugInfo(); - if (headerDebugInfo != null) canvas.drawText(headerDebugInfo, 50, 100, mDebugPaint); drawDebugInfo(canvas, mNotificationPanelViewController.getMaxPanelHeight(), Color.RED, "getMaxPanelHeight()"); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index d3d38e5c21cd..2ef83dd42868 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -138,7 +138,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; -import com.android.systemui.power.shared.model.WakefulnessModel; import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel; @@ -162,9 +161,10 @@ import com.android.systemui.plugins.FalsingManager.FalsingTapListener; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.power.shared.model.WakefulnessModel; import com.android.systemui.res.R; import com.android.systemui.shade.data.repository.ShadeRepository; -import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; @@ -200,7 +200,6 @@ import com.android.systemui.statusbar.phone.BounceInterpolator; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; @@ -217,6 +216,7 @@ import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; @@ -362,7 +362,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private boolean mIsLaunchAnimationRunning; private float mOverExpansion; private CentralSurfaces mCentralSurfaces; - private HeadsUpManagerPhone mHeadsUpManager; + private HeadsUpManager mHeadsUpManager; private float mExpandedHeight = 0; /** The current squish amount for the predictive back animation */ private float mCurrentBackProgress = 0.0f; @@ -1275,7 +1275,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById( R.id.keyguard_status_view); KeyguardStatusViewComponent statusViewComponent = - mKeyguardStatusViewComponentFactory.build(keyguardStatusView); + mKeyguardStatusViewComponentFactory.build(keyguardStatusView, + mView.getContext().getDisplay()); mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); mKeyguardStatusViewController.init(); } @@ -3025,7 +3026,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return headsUpVisible || isExpanded() || mBouncerShowing; } - private void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { + private void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; mHeadsUpManager.addListener(mOnHeadsUpChangedListener); mHeadsUpTouchHelper = new HeadsUpTouchHelper( @@ -3507,7 +3508,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump GestureRecorder recorder, Runnable hideExpandedRunnable, NotificationShelfController notificationShelfController, - HeadsUpManagerPhone headsUpManager) { + HeadsUpManager headsUpManager) { setHeadsUpManager(headsUpManager); // TODO(b/254859580): this can be injected. mCentralSurfaces = centralSurfaces; @@ -3556,10 +3557,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); } - String getHeaderDebugInfo() { - return "USER " + mHeadsUpManager.getUser(); - } - @Override public void onThemeChanged() { mConfigurationListener.onThemeChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 6cf4ff569662..5414b3f30aa5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -21,7 +21,6 @@ import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.app.StatusBarManager; -import android.os.PowerManager; import android.util.Log; import android.view.GestureDetector; import android.view.InputDevice; @@ -36,10 +35,11 @@ import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.LockIconViewController; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.back.domain.interactor.BackActionInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder; import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.classifier.FalsingCollector; @@ -56,6 +56,7 @@ import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.log.BouncerLogger; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.res.R; import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -106,6 +107,8 @@ public class NotificationShadeWindowViewController implements Dumpable { private final boolean mIsTrackpadCommonEnabled; private final FeatureFlags mFeatureFlags; private final KeyEventInteractor mKeyEventInteractor; + private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private GestureDetector mPulsingWakeupGestureHandler; private GestureDetector mDreamingWakeupGestureHandler; private View mBrightnessMirror; @@ -182,7 +185,9 @@ public class NotificationShadeWindowViewController implements Dumpable { SystemClock clock, BouncerMessageInteractor bouncerMessageInteractor, BouncerLogger bouncerLogger, - KeyEventInteractor keyEventInteractor) { + KeyEventInteractor keyEventInteractor, + PrimaryBouncerInteractor primaryBouncerInteractor, + AlternateBouncerInteractor alternateBouncerInteractor) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; @@ -210,6 +215,8 @@ public class NotificationShadeWindowViewController implements Dumpable { mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON); mFeatureFlags = featureFlags; mKeyEventInteractor = keyEventInteractor; + mPrimaryBouncerInteractor = primaryBouncerInteractor; + mAlternateBouncerInteractor = alternateBouncerInteractor; // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); @@ -351,16 +358,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing()) { mDozeScrimController.extendPulse(); } - mLockIconViewController.onTouchEvent( - ev, - /* onGestureDetectedRunnable */ - () -> { - mService.userActivity(); - mPowerInteractor.wakeUpIfDozing( - "LOCK_ICON_TOUCH", - PowerManager.WAKE_REASON_GESTURE); - } - ); // In case we start outside of the view bounds (below the status bar), we need to // dispatch the touch manually as the view system can't accommodate for touches @@ -415,8 +412,18 @@ public class NotificationShadeWindowViewController implements Dumpable { private boolean shouldInterceptTouchEventInternal(MotionEvent ev) { mLastInterceptWasDragDownHelper = false; - if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing() - && !mDockManager.isDocked()) { + // When the device starts dozing, there's a delay before the device's display state + // changes from ON => DOZE to allow for the light reveal animation to run at + // a higher refresh rate and to delay visual changes (ie: display blink) when + // changing the display state. We'll call this specific state the + // "aodDefermentState". In this state we: + // - don't want touches to get sent to underlying views, except the lock icon + // - handle the tap to wake gesture via the PulsingGestureListener + if (mStatusBarStateController.isDozing() + && !mDozeServiceHost.isPulsing() + && !mDockManager.isDocked() + && !mLockIconViewController.willHandleTouchWhileDozing(ev) + ) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mShadeLogger.d("NSWVC: capture all touch events in always-on"); } @@ -432,16 +439,15 @@ public class NotificationShadeWindowViewController implements Dumpable { return true; } - if (mLockIconViewController.onInterceptTouchEvent(ev)) { - // immediately return true; don't send the touch to the drag down helper - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mShadeLogger.d("NSWVC: don't send touch to drag down helper"); - } - return true; + boolean bouncerShowing; + if (mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() + || mAlternateBouncerInteractor.isVisibleState(); + } else { + bouncerShowing = mService.isBouncerShowing(); } - if (mNotificationPanelViewController.isFullyExpanded() - && !mService.isBouncerShowing() + && !bouncerShowing && !mStatusBarStateController.isDozing()) { if (mDragDownHelper.isDragDownEnabled()) { // This handles drag down over lockscreen diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index 447a15d34c05..2c4b0b990e6d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -178,9 +178,6 @@ public interface ShadeController extends CoreStartable { /** Listens for shade visibility changes. */ interface ShadeVisibilityListener { - /** Called when the visibility of the shade changes. */ - void visibilityChanged(boolean visible); - /** Called when shade expanded and visible state changed. */ void expandedVisibleChanged(boolean expandedVisible); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index 367449b9d59d..fdc7eecd9b15 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -24,6 +24,7 @@ import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import com.android.systemui.DejankUtils; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -75,6 +76,7 @@ public final class ShadeControllerImpl implements ShadeController { private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); private boolean mExpandedVisible; + private boolean mLockscreenOrShadeVisible; private NotificationPresenter mPresenter; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; @@ -399,8 +401,19 @@ public final class ShadeControllerImpl implements ShadeController { } private void notifyVisibilityChanged(boolean visible) { - mShadeVisibilityListener.visibilityChanged(visible); mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(visible); + if (mLockscreenOrShadeVisible != visible) { + mLockscreenOrShadeVisible = visible; + if (visible) { + // It would be best if this could be done as a side effect of listening to the + // [WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible] flow inside + // NotificationShadeWindowViewController. However, there's no guarantee that the + // flow will emit in the same frame as when the visibility changed, and we want the + // DejankUtils to be notified immediately, so we do it immediately here. + DejankUtils.notifyRendererOfExpensiveFrame( + getNotificationShadeWindowView(), "onShadeVisibilityChanged"); + } + } } private void notifyExpandedVisibleChanged(boolean expandedVisible) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt index 6ee6cbf69a70..4e23e7d97a01 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt @@ -20,7 +20,7 @@ import com.android.systemui.statusbar.GestureRecorder import com.android.systemui.statusbar.NotificationShelfController import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.HeadsUpManager /** * Allows CentralSurfacesImpl to interact with the shade. Only CentralSurfacesImpl should reference @@ -34,7 +34,7 @@ interface ShadeSurface : ShadeViewController { recorder: GestureRecorder, hideExpandedRunnable: Runnable, notificationShelfController: NotificationShelfController, - headsUpManager: HeadsUpManagerPhone + headsUpManager: HeadsUpManager ) /** Cancels any pending collapses. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt index ac8333ae84ad..6117f9f80e6c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt @@ -19,7 +19,11 @@ package com.android.systemui.shade.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.ObservableTransitionState @@ -27,8 +31,9 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject @@ -56,13 +61,16 @@ class ShadeInteractor @Inject constructor( @Application scope: CoroutineScope, + deviceProvisioningRepository: DeviceProvisioningRepository, disableFlagsRepository: DisableFlagsRepository, + dozeParams: DozeParameters, sceneContainerFlags: SceneContainerFlags, // TODO(b/300258424) convert to direct reference instead of provider sceneInteractorProvider: Provider<SceneInteractor>, keyguardRepository: KeyguardRepository, + keyguardTransitionInteractor: KeyguardTransitionInteractor, + powerInteractor: PowerInteractor, userSetupRepository: UserSetupRepository, - deviceProvisionedController: DeviceProvisionedController, userInteractor: UserInteractor, sharedNotificationContainerInteractor: SharedNotificationContainerInteractor, repository: ShadeRepository, @@ -187,6 +195,26 @@ constructor( combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs } .distinctUntilChanged() + /** Are touches allowed on the notification panel? */ + val isShadeTouchable: Flow<Boolean> = + combine( + powerInteractor.isAsleep, + keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD }, + keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING }, + deviceProvisioningRepository.isFactoryResetProtectionActive, + ) { isAsleep, goingToSleep, isPulsing, isFrpActive -> + when { + // Touches are disabled when Factory Reset Protection is active + isFrpActive -> false + // If the device is going to sleep, only accept touches if we're still + // animating + goingToSleep -> dozeParams.shouldControlScreenOff() + // If the device is asleep, only accept touches if there's a pulse + isAsleep -> isPulsing + else -> true + } + } + /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */ val isExpandToQsEnabled: Flow<Boolean> = combine( @@ -194,8 +222,9 @@ constructor( isShadeEnabled, keyguardRepository.isDozing, userSetupRepository.isUserSetupFlow, - ) { disableFlags, isShadeEnabled, isDozing, isUserSetup -> - deviceProvisionedController.isDeviceProvisioned && + deviceProvisioningRepository.isDeviceProvisioned, + ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned -> + isDeviceProvisioned && // Disallow QS during setup if it's a simple user switcher. (The user intends to // use the lock screen user switcher, QS is not needed.) (isUserSetup || !userInteractor.isSimpleUserSwitcher) && @@ -232,7 +261,7 @@ constructor( when (state) { is ObservableTransitionState.Idle -> false is ObservableTransitionState.Transition -> - state.isUserInputDriven && + state.isInitiatedByUserInput && (state.toScene == sceneKey || state.fromScene == sceneKey) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java index d24f9d8f476c..77b095802b00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import android.view.View; +import androidx.annotation.Nullable; + import com.android.app.animation.Interpolators; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -113,7 +115,16 @@ public class CrossFadeHelper { fadeIn(view, ANIMATION_DURATION_LENGTH, 0); } + public static void fadeIn(final View view, Runnable endRunnable) { + fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, endRunnable); + } + public static void fadeIn(final View view, long duration, int delay) { + fadeIn(view, duration, delay, /* endRunnable= */ null); + } + + public static void fadeIn(final View view, long duration, int delay, + @Nullable Runnable endRunnable) { view.animate().cancel(); if (view.getVisibility() == View.INVISIBLE) { view.setAlpha(0.0f); @@ -124,7 +135,7 @@ public class CrossFadeHelper { .setDuration(duration) .setStartDelay(delay) .setInterpolator(Interpolators.ALPHA_IN) - .withEndAction(null); + .withEndAction(endRunnable); if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) { view.animate().withLayer(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 3640ae067537..4ea70264b152 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -31,22 +31,22 @@ import androidx.annotation.VisibleForTesting import com.android.app.animation.Interpolators import com.android.systemui.Dumpable import com.android.systemui.Gefingerpoken -import com.android.systemui.res.R import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.HeadsUpManager import java.io.PrintWriter import javax.inject.Inject import kotlin.math.max @@ -63,7 +63,7 @@ constructor( context: Context, private val wakeUpCoordinator: NotificationWakeUpCoordinator, private val bypassController: KeyguardBypassController, - private val headsUpManager: HeadsUpManagerPhone, + private val headsUpManager: HeadsUpManager, private val roundnessManager: NotificationRoundnessManager, configurationController: ConfigurationController, private val statusBarStateController: StatusBarStateController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index f616b91c4712..3a4ad0e79994 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -51,6 +51,7 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.Interpolator; +import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import com.android.app.animation.Interpolators; @@ -959,12 +960,17 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } public void setDozing(boolean dozing, boolean fade, long delay) { + setDozing(dozing, fade, delay, /* onChildCompleted= */ null); + } + + public void setDozing(boolean dozing, boolean fade, long delay, + @Nullable Runnable endRunnable) { mDozer.setDozing(f -> { mDozeAmount = f; updateDecorColor(); updateIconColor(); updateAllowAnimation(); - }, dozing, fade, delay, this); + }, dozing, fade, delay, this, endRunnable); } private void updateAllowAnimation() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java index 167efc784ff5..dc0eb7b4aab3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java @@ -24,6 +24,8 @@ import android.graphics.ColorMatrixColorFilter; import android.view.View; import android.widget.ImageView; +import androidx.annotation.Nullable; + import com.android.app.animation.Interpolators; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -81,6 +83,11 @@ public class NotificationDozeHelper { public void setDozing(Consumer<Float> listener, boolean dozing, boolean animate, long delay, View view) { + setDozing(listener, dozing, animate, delay, view, /* endRunnable= */ null); + } + + public void setDozing(Consumer<Float> listener, boolean dozing, + boolean animate, long delay, View view, @Nullable Runnable endRunnable) { if (animate) { startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dozing, delay, @@ -89,6 +96,9 @@ public class NotificationDozeHelper { @Override public void onAnimationEnd(Animator animation) { view.setTag(DOZE_ANIMATOR_TAG, null); + if (endRunnable != null) { + endRunnable.run(); + } } @Override @@ -102,6 +112,9 @@ public class NotificationDozeHelper { animator.cancel(); } listener.accept(dozing ? 1f : 0f); + if (endRunnable != null) { + endRunnable.run(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt index c62546f67f8d..756151bd57e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt @@ -24,7 +24,7 @@ import com.android.systemui.animation.LaunchAnimator import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpUtil import kotlin.math.ceil import kotlin.math.max @@ -35,7 +35,7 @@ private const val TAG = "NotificationLaunchAnimatorController" class NotificationLaunchAnimatorControllerProvider( private val notificationExpansionRepository: NotificationExpansionRepository, private val notificationListContainer: NotificationListContainer, - private val headsUpManager: HeadsUpManagerPhone, + private val headsUpManager: HeadsUpManager, private val jankMonitor: InteractionJankMonitor ) { @JvmOverloads @@ -62,7 +62,7 @@ class NotificationLaunchAnimatorControllerProvider( class NotificationLaunchAnimatorController( private val notificationExpansionRepository: NotificationExpansionRepository, private val notificationListContainer: NotificationListContainer, - private val headsUpManager: HeadsUpManagerPhone, + private val headsUpManager: HeadsUpManager, private val notification: ExpandableNotificationRow, private val jankMonitor: InteractionJankMonitor, private val onFinishAnimationCallback: Runnable? @@ -152,16 +152,17 @@ class NotificationLaunchAnimatorController( } } - private val headsUpNotificationRow: ExpandableNotificationRow? get() { - val summaryEntry = notificationEntry.parent?.summary + private val headsUpNotificationRow: ExpandableNotificationRow? + get() { + val summaryEntry = notificationEntry.parent?.summary - return when { - headsUpManager.isAlerting(notificationKey) -> notification - summaryEntry == null -> null - headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row - else -> null + return when { + headsUpManager.isAlerting(notificationKey) -> notification + summaryEntry == null -> null + headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row + else -> null + } } - } private fun removeHun(animate: Boolean) { val row = headsUpNotificationRow ?: return diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 07eb8a00a178..2d839704e0b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -37,8 +37,8 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.asIndenting @@ -85,7 +85,7 @@ constructor( @Application private val scope: CoroutineScope, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val secureSettings: SecureSettings, - private val seenNotifsProvider: SeenNotificationsProviderImpl, + private val notificationListInteractor: NotificationListInteractor, private val statusBarStateController: StatusBarStateController, ) : Coordinator, Dumpable { @@ -351,7 +351,7 @@ constructor( override fun onCleanup() { logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs) - seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs + notificationListInteractor.setHasFilteredOutSeenNotifications(hasFilteredAnyNotifs) hasFilteredAnyNotifs = false } } @@ -388,8 +388,8 @@ constructor( override fun dump(pw: PrintWriter, args: Array<out String>) = with(pw.asIndenting()) { println( - "seenNotifsProvider.hasFilteredOutSeenNotifications=" + - seenNotifsProvider.hasFilteredOutSeenNotifications + "notificationListInteractor.hasFilteredOutSeenNotifications.value=" + + notificationListInteractor.hasFilteredOutSeenNotifications.value ) println("unseen notifications:") indentIfPossible { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt index 657c394d4e30..c0f674846991 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt @@ -28,6 +28,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Compile +import com.android.systemui.util.traceSection import javax.inject.Inject /** @@ -122,8 +123,10 @@ class ViewConfigCoordinator @Inject internal constructor( private fun updateNotificationsOnUiModeChanged() { log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" } - mPipeline?.allNotifs?.forEach { entry -> - entry.row?.onUiModeChanged() + traceSection("updateNotifOnUiModeChanged") { + mPipeline?.allNotifs?.forEach { entry -> + entry.row?.onUiModeChanged() + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt deleted file mode 100644 index cff47e220299..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.collection.provider - -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module -import javax.inject.Inject - -/** Keeps track of whether "seen" notification content has been filtered out of the shade. */ -interface SeenNotificationsProvider { - /** Are any already-seen notifications currently filtered out of the shade? */ - val hasFilteredOutSeenNotifications: Boolean -} - -@Module -interface SeenNotificationsProviderModule { - @Binds - fun bindSeenNotificationsProvider( - impl: SeenNotificationsProviderImpl - ): SeenNotificationsProvider -} - -@SysUISingleton -class SeenNotificationsProviderImpl @Inject constructor() : SeenNotificationsProvider { - override var hasFilteredOutSeenNotifications: Boolean = false -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt index 59fc387c4608..1a88815acfaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt @@ -231,18 +231,24 @@ private class ShadeNode(val controller: NodeController) { fun getChildCount(): Int = controller.getChildCount() fun addChildAt(child: ShadeNode, index: Int) { - controller.addChildAt(child.controller, index) - child.controller.onViewAdded() + traceSection("ShadeNode#addChildAt") { + controller.addChildAt(child.controller, index) + child.controller.onViewAdded() + } } fun moveChildTo(child: ShadeNode, index: Int) { - controller.moveChildTo(child.controller, index) - child.controller.onViewMoved() + traceSection("ShadeNode#moveChildTo") { + controller.moveChildTo(child.controller, index) + child.controller.onViewMoved() + } } fun removeChild(child: ShadeNode, isTransfer: Boolean) { - controller.removeChild(child.controller, isTransfer) - child.controller.onViewRemoved() + traceSection("ShadeNode#removeChild") { + controller.removeChild(child.controller, isTransfer) + child.controller.onViewRemoved() + } } fun offerToKeepInParentForAnimation(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 8ee0de691557..8561869af352 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -20,10 +20,10 @@ import android.content.Context; import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.CoreStartable; -import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; import com.android.systemui.shade.ShadeEventsModule; import com.android.systemui.statusbar.NotificationListener; @@ -45,7 +45,6 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProviderImpl; import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl; -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl; @@ -54,6 +53,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; +import com.android.systemui.statusbar.notification.data.NotificationDataLayerModule; import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository; import com.android.systemui.statusbar.notification.icon.ConversationIconManager; import com.android.systemui.statusbar.notification.icon.IconManager; @@ -74,9 +74,9 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.kotlin.JavaAdapter; import dagger.Binds; @@ -95,8 +95,8 @@ import javax.inject.Provider; @Module(includes = { CoordinatorsModule.class, KeyguardNotificationVisibilityProviderModule.class, - SeenNotificationsProviderModule.class, ShadeEventsModule.class, + NotificationDataLayerModule.class, NotifPipelineChoreographerModule.class, NotificationSectionHeadersModule.class, NotificationListViewModelModule.class, @@ -206,7 +206,7 @@ public interface NotificationsModule { static NotificationLaunchAnimatorControllerProvider provideNotifLaunchAnimControllerProvider( NotificationExpansionRepository notificationExpansionRepository, NotificationListContainer notificationListContainer, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, InteractionJankMonitor jankMonitor) { return new NotificationLaunchAnimatorControllerProvider( notificationExpansionRepository, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt new file mode 100644 index 000000000000..5435fb5449cd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.data + +import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardStateRepositoryModule +import dagger.Module + +@Module(includes = [NotificationsKeyguardStateRepositoryModule::class]) +interface NotificationDataLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt new file mode 100644 index 000000000000..cf03d1c5addc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.data.repository + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** View-states pertaining to notifications on the keyguard. */ +interface NotificationsKeyguardViewStateRepository { + /** Are notifications fully hidden from view? */ + val areNotificationsFullyHidden: Flow<Boolean> + + /** Is a pulse expansion occurring? */ + val isPulseExpanding: Flow<Boolean> +} + +@Module +interface NotificationsKeyguardStateRepositoryModule { + @Binds + fun bindImpl( + impl: NotificationsKeyguardViewStateRepositoryImpl + ): NotificationsKeyguardViewStateRepository +} + +@SysUISingleton +class NotificationsKeyguardViewStateRepositoryImpl +@Inject +constructor( + wakeUpCoordinator: NotificationWakeUpCoordinator, +) : NotificationsKeyguardViewStateRepository { + override val areNotificationsFullyHidden: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : NotificationWakeUpCoordinator.WakeUpListener { + override fun onFullyHiddenChanged(isFullyHidden: Boolean) { + trySend(isFullyHidden) + } + } + trySend(wakeUpCoordinator.notificationsFullyHidden) + wakeUpCoordinator.addListener(listener) + awaitClose { wakeUpCoordinator.removeListener(listener) } + } + + override val isPulseExpanding: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : NotificationWakeUpCoordinator.WakeUpListener { + override fun onPulseExpansionChanged(expandingChanged: Boolean) { + trySend(expandingChanged) + } + } + trySend(wakeUpCoordinator.isPulseExpanding()) + wakeUpCoordinator.addListener(listener) + awaitClose { wakeUpCoordinator.removeListener(listener) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt new file mode 100644 index 000000000000..87b8e55dbd1a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.domain.interactor + +import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Domain logic pertaining to notifications on the keyguard. */ +class NotificationsKeyguardInteractor +@Inject +constructor( + repository: NotificationsKeyguardViewStateRepository, +) { + /** Is a pulse expansion occurring? */ + val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding + + /** Are notifications fully hidden from view? */ + val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt index 5cc5e751ca8e..805a4dba111f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt @@ -26,26 +26,21 @@ import android.widget.FrameLayout import androidx.annotation.ColorInt import androidx.annotation.VisibleForTesting import androidx.collection.ArrayMap -import com.android.app.animation.Interpolators import com.android.internal.statusbar.StatusBarIcon import com.android.internal.util.ContrastColorUtil import com.android.settingslib.Utils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.demomode.DemoMode import com.android.systemui.demomode.DemoModeController -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags -import com.android.systemui.flags.Flags.NEW_AOD_TRANSITION 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 @@ -60,6 +55,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.wm.shell.bubbles.Bubbles import java.util.Optional @@ -79,9 +75,9 @@ class NotificationIconAreaControllerViewBinderWrapperImpl @Inject constructor( private val context: Context, - private val statusBarStateController: StatusBarStateController, private val wakeUpCoordinator: NotificationWakeUpCoordinator, private val bypassController: KeyguardBypassController, + private val configurationController: ConfigurationController, private val mediaManager: NotificationMediaManager, notificationListener: NotificationListener, private val dozeParameters: DozeParameters, @@ -89,7 +85,7 @@ constructor( private val bubblesOptional: Optional<Bubbles>, demoModeController: DemoModeController, darkIconDispatcher: DarkIconDispatcher, - private val featureFlags: FeatureFlags, + private val featureFlags: FeatureFlagsClassic, private val statusBarWindowController: StatusBarWindowController, private val screenOffAnimationController: ScreenOffAnimationController, private val shelfIconsViewModel: NotificationIconContainerShelfViewModel, @@ -98,14 +94,12 @@ constructor( ) : NotificationIconAreaController, DarkIconDispatcher.DarkReceiver, - StatusBarStateController.StateListener, NotificationWakeUpCoordinator.WakeUpListener, DemoMode { private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context) private val updateStatusBarIcons = Runnable { updateStatusBarIcons() } private val shelfRefactor = ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR) - private val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) private val tintAreas = ArrayList<Rect>() private var iconSize = 0 @@ -118,9 +112,7 @@ constructor( private var aodIcons: NotificationIconContainer? = null private var aodBindJob: DisposableHandle? = null private var aodIconAppearTranslation = 0 - private var animationsEnabled = false private var aodIconTint = 0 - private var aodIconsVisible = false private var showLowPriority = true @VisibleForTesting @@ -133,7 +125,6 @@ constructor( } init { - statusBarStateController.addCallback(this) wakeUpCoordinator.addListener(this) demoModeController.addCallback(this) notificationListener.addNotificationSettingsListener(settingsListener) @@ -157,9 +148,15 @@ constructor( } this.aodIcons = aodIcons this.aodIcons!!.setOnLockScreen(true) - aodBindJob = NotificationIconContainerViewBinder.bind(aodIcons, aodIconsViewModel) - updateAodIconsVisibility(animate = false, forceUpdate = changed) - updateAnimations() + aodBindJob = + NotificationIconContainerViewBinder.bind( + aodIcons, + aodIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, + ) if (changed) { updateAodNotificationIcons() } @@ -171,7 +168,14 @@ constructor( override fun setShelfIcons(icons: NotificationIconContainer) { if (shelfRefactor.expectEnabled()) { - NotificationIconContainerViewBinder.bind(icons, shelfIconsViewModel) + NotificationIconContainerViewBinder.bind( + icons, + shelfIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, + ) shelfIcons = icons } } @@ -244,23 +248,7 @@ constructor( notificationIcons!!.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate) } - override fun onDozingChanged(isDozing: Boolean) { - if (aodIcons == null) { - return - } - val animate = (dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking) - aodIcons!!.setDozing(isDozing, animate, 0) - } - - override fun setAnimationsEnabled(enabled: Boolean) { - animationsEnabled = enabled - updateAnimations() - } - - override fun onStateChanged(newState: Int) { - updateAodIconsVisibility(animate = false, forceUpdate = false) - updateAnimations() - } + override fun setAnimationsEnabled(enabled: Boolean) = unsupported override fun onThemeChanged() { reloadAodColor() @@ -271,53 +259,11 @@ constructor( return if (aodIcons == null) 0 else aodIcons!!.height } - @VisibleForTesting - fun appearAodIcons() { - if (aodIcons == null) { - return - } - if (screenOffAnimationController.shouldAnimateAodIcons()) { - if (!statusViewMigrated) { - aodIcons!!.translationY = -aodIconAppearTranslation.toFloat() - } - aodIcons!!.alpha = 0f - animateInAodIconTranslation() - aodIcons!! - .animate() - .alpha(1f) - .setInterpolator(Interpolators.LINEAR) - .setDuration(AOD_ICONS_APPEAR_DURATION) - .start() - } else { - aodIcons!!.alpha = 1.0f - if (!statusViewMigrated) { - aodIcons!!.translationY = 0f - } - } - } - override fun onFullyHiddenChanged(isFullyHidden: Boolean) { - var animate = true - if (!bypassController.bypassEnabled) { - animate = dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking - if (!featureFlags.isEnabled(NEW_AOD_TRANSITION)) { - // 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) @@ -348,7 +294,14 @@ constructor( val layoutInflater = LayoutInflater.from(context) notificationIconArea = inflateIconArea(layoutInflater) notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons) - NotificationIconContainerViewBinder.bind(notificationIcons!!, statusBarIconsViewModel) + NotificationIconContainerViewBinder.bind( + notificationIcons!!, + statusBarIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, + ) } private fun updateIconLayoutParams(context: Context) { @@ -598,25 +551,6 @@ constructor( v.setDecorColor(tint) } - private fun updateAnimations() { - val inShade = statusBarStateController.state == StatusBarState.SHADE - if (aodIcons != null) { - aodIcons!!.setAnimationsEnabled(animationsEnabled && !inShade) - } - notificationIcons!!.setAnimationsEnabled(animationsEnabled && inShade) - } - - private fun animateInAodIconTranslation() { - if (!statusViewMigrated) { - aodIcons!! - .animate() - .setInterpolator(Interpolators.DECELERATE_QUINT) - .translationY(0f) - .setDuration(AOD_ICONS_APPEAR_DURATION) - .start() - } - } - private fun reloadAodColor() { aodIconTint = Utils.getColorAttrDefaultColor( @@ -639,70 +573,13 @@ constructor( } } - private fun updateAodIconsVisibility(animate: Boolean, forceUpdate: Boolean) { - if (aodIcons == null) { - return - } - var visible = (bypassController.bypassEnabled || wakeUpCoordinator.notificationsFullyHidden) - - // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is - // playing, in which case we want them to be visible since we're animating in the AOD UI and - // will be switching to KEYGUARD shortly. - if ( - statusBarStateController.state != StatusBarState.KEYGUARD && - !screenOffAnimationController.shouldShowAodIconsWhenShade() - ) { - visible = false - } - if (visible && wakeUpCoordinator.isPulseExpanding() && !bypassController.bypassEnabled) { - visible = false - } - if (aodIconsVisible != visible || forceUpdate) { - aodIconsVisible = visible - aodIcons!!.animate().cancel() - if (animate) { - if (featureFlags.isEnabled(NEW_AOD_TRANSITION)) { - // Let's make sure the icon are translated to 0, since we cancelled it above - animateInAodIconTranslation() - if (aodIconsVisible) { - CrossFadeHelper.fadeIn(aodIcons) - } else { - CrossFadeHelper.fadeOut(aodIcons) - } - } else { - val wasFullyInvisible = aodIcons!!.visibility != View.VISIBLE - if (aodIconsVisible) { - if (wasFullyInvisible) { - // No fading here, let's just appear the icons instead! - aodIcons!!.visibility = View.VISIBLE - aodIcons!!.alpha = 1.0f - appearAodIcons() - } else { - // Let's make sure the icon are translated to 0, since we cancelled it - // above - animateInAodIconTranslation() - // We were fading out, let's fade in instead - CrossFadeHelper.fadeIn(aodIcons) - } - } else { - // Let's make sure the icon are translated to 0, since we cancelled it above - animateInAodIconTranslation() - CrossFadeHelper.fadeOut(aodIcons) - } - } - } else { - aodIcons!!.alpha = 1.0f - if (!statusViewMigrated) { - aodIcons!!.translationY = 0f - } - aodIcons!!.visibility = if (visible) View.VISIBLE else View.INVISIBLE - } - } - } - companion object { - private const val AOD_ICONS_APPEAR_DURATION: Long = 200 - @ColorInt private val DEFAULT_AOD_ICON_COLOR = -0x1 + + val unsupported: Nothing + get() = + error( + "Code path not supported when NOTIFICATION_ICON_CONTAINER_REFACTOR is disabled" + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index 8293bb329a01..0d2f00aa3627 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -15,19 +15,172 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewbinder +import android.content.res.Resources +import android.view.View +import androidx.annotation.DimenRes import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.statusbar.CrossFadeHelper import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged +import com.android.systemui.util.kotlin.stateFlow +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch /** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */ object NotificationIconContainerViewBinder { fun bind( view: NotificationIconContainer, viewModel: NotificationIconContainerViewModel, + configurationController: ConfigurationController, + dozeParameters: DozeParameters, + featureFlags: FeatureFlagsClassic, + screenOffAnimationController: ScreenOffAnimationController, ): DisposableHandle { - return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) {} } + return view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.CREATED) { + launch { viewModel.animationsEnabled.collect(view::setAnimationsEnabled) } + launch { + viewModel.isDozing.collect { (isDozing, animate) -> + val animateIfNotBlanking = animate && !dozeParameters.displayNeedsBlanking + view.setDozing(isDozing, animateIfNotBlanking, /* delay= */ 0) { + viewModel.completeDozeAnimation() + } + } + } + // TODO(278765923): this should live where AOD is bound, not inside of the NIC + // view-binder + launch { + val iconAppearTranslation = + view.resources.getConfigAwareDimensionPixelSize( + this, + configurationController, + R.dimen.shelf_appear_translation, + ) + bindVisibility( + viewModel, + view, + featureFlags, + screenOffAnimationController, + iconAppearTranslation, + ) { + viewModel.completeVisibilityAnimation() + } + } + } + } } + private suspend fun bindVisibility( + viewModel: NotificationIconContainerViewModel, + view: NotificationIconContainer, + featureFlags: FeatureFlagsClassic, + screenOffAnimationController: ScreenOffAnimationController, + iconAppearTranslation: StateFlow<Int>, + onAnimationEnd: () -> Unit, + ) { + val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) + viewModel.isVisible.collect { (isVisible, animate) -> + view.animate().cancel() + when { + !animate -> { + view.alpha = 1f + if (!statusViewMigrated) { + view.translationY = 0f + } + view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + } + featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> { + animateInIconTranslation(view, statusViewMigrated) + if (isVisible) { + CrossFadeHelper.fadeIn(view, onAnimationEnd) + } else { + CrossFadeHelper.fadeOut(view, onAnimationEnd) + } + } + !isVisible -> { + // Let's make sure the icon are translated to 0, since we cancelled it above + animateInIconTranslation(view, statusViewMigrated) + CrossFadeHelper.fadeOut(view, onAnimationEnd) + } + view.visibility != View.VISIBLE -> { + // No fading here, let's just appear the icons instead! + view.visibility = View.VISIBLE + view.alpha = 1f + appearIcons( + view, + animate = screenOffAnimationController.shouldAnimateAodIcons(), + iconAppearTranslation.value, + statusViewMigrated, + ) + onAnimationEnd() + } + else -> { + // Let's make sure the icons are translated to 0, since we cancelled it above + animateInIconTranslation(view, statusViewMigrated) + // We were fading out, let's fade in instead + CrossFadeHelper.fadeIn(view, onAnimationEnd) + } + } + } + } + + private fun appearIcons( + view: View, + animate: Boolean, + iconAppearTranslation: Int, + statusViewMigrated: Boolean, + ) { + if (animate) { + if (!statusViewMigrated) { + view.translationY = -iconAppearTranslation.toFloat() + } + view.alpha = 0f + animateInIconTranslation(view, statusViewMigrated) + view + .animate() + .alpha(1f) + .setInterpolator(Interpolators.LINEAR) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } else { + view.alpha = 1.0f + if (!statusViewMigrated) { + view.translationY = 0f + } + } + } + + private fun animateInIconTranslation(view: View, statusViewMigrated: Boolean) { + if (!statusViewMigrated) { + view + .animate() + .setInterpolator(Interpolators.DECELERATE_QUINT) + .translationY(0f) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } + } + + private const val AOD_ICONS_APPEAR_DURATION: Long = 200 } + +fun Resources.getConfigAwareDimensionPixelSize( + scope: CoroutineScope, + configurationController: ConfigurationController, + @DimenRes id: Int, +): StateFlow<Int> = + scope.stateFlow( + changedSignals = configurationController.onDensityOrFontScaleChanged, + getValue = { getDimensionPixelSize(id) } + ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt index f68b0ef79638..3289a3ce5574 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt @@ -15,8 +15,146 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.util.kotlin.pairwise +import com.android.systemui.util.kotlin.sample +import com.android.systemui.util.ui.AnimatableEvent +import com.android.systemui.util.ui.AnimatedValue +import com.android.systemui.util.ui.toAnimatedValueFlow import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map /** View-model for the row of notification icons displayed on the always-on display. */ -class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor() : - NotificationIconContainerViewModel +@SysUISingleton +class NotificationIconContainerAlwaysOnDisplayViewModel +@Inject +constructor( + private val deviceEntryInteractor: DeviceEntryInteractor, + private val dozeParameters: DozeParameters, + private val featureFlags: FeatureFlagsClassic, + keyguardInteractor: KeyguardInteractor, + keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor, + screenOffAnimationController: ScreenOffAnimationController, + shadeInteractor: ShadeInteractor, +) : NotificationIconContainerViewModel { + + private val onDozeAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) + private val onVisAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) + + override val animationsEnabled: Flow<Boolean> = + combine( + shadeInteractor.isShadeTouchable, + keyguardInteractor.isKeyguardVisible, + ) { panelTouchesEnabled, isKeyguardVisible -> + panelTouchesEnabled && isKeyguardVisible + } + + override val isDozing: Flow<AnimatedValue<Boolean>> = + keyguardTransitionInteractor.startedKeyguardTransitionStep + // Determine if we're dozing based on the most recent transition + .map { step: TransitionStep -> + val isDozing = step.to == KeyguardState.AOD || step.to == KeyguardState.DOZING + isDozing to step + } + // Only emit changes based on whether we've started or stopped dozing + .distinctUntilChanged { (wasDozing, _), (isDozing, _) -> wasDozing != isDozing } + // Determine whether we need to animate + .map { (isDozing, step) -> + val animate = step.to == KeyguardState.AOD || step.from == KeyguardState.AOD + AnimatableEvent(isDozing, animate) + } + .distinctUntilChanged() + .toAnimatedValueFlow(completionEvents = onDozeAnimationComplete) + + override val isVisible: Flow<AnimatedValue<Boolean>> = + combine( + keyguardTransitionInteractor.finishedKeyguardState.map { it != KeyguardState.GONE }, + deviceEntryInteractor.isBypassEnabled, + areNotifsFullyHiddenAnimated(), + isPulseExpandingAnimated(), + ) { + onKeyguard: Boolean, + bypassEnabled: Boolean, + (notifsFullyHidden: Boolean, isAnimatingHide: Boolean), + (pulseExpanding: Boolean, isAnimatingPulse: Boolean), + -> + val isAnimating = isAnimatingHide || isAnimatingPulse + when { + // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off + // animation is playing, in which case we want them to be visible if we're + // animating in the AOD UI and will be switching to KEYGUARD shortly. + !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() -> + AnimatedValue(false, isAnimating = false) + // If we're bypassing, then we're visible + bypassEnabled -> AnimatedValue(true, isAnimating) + // If we are pulsing (and not bypassing), then we are hidden + pulseExpanding -> AnimatedValue(false, isAnimating) + // If notifs are fully gone, then we're visible + notifsFullyHidden -> AnimatedValue(true, isAnimating) + // Otherwise, we're hidden + else -> AnimatedValue(false, isAnimating) + } + } + .distinctUntilChanged() + + override fun completeDozeAnimation() { + onDozeAnimationComplete.tryEmit(Unit) + } + + override fun completeVisibilityAnimation() { + onVisAnimationComplete.tryEmit(Unit) + } + + /** Is there an expanded pulse, are we animating in response? */ + private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> { + return notificationsKeyguardInteractor.isPulseExpanding + .pairwise(initialValue = null) + // If pulsing changes, start animating, unless it's the first emission + .map { (prev, expanding) -> + AnimatableEvent(expanding!!, startAnimating = prev != null) + } + .toAnimatedValueFlow(completionEvents = onVisAnimationComplete) + } + + /** Are notifications completely hidden from view, are we animating in response? */ + private fun areNotifsFullyHiddenAnimated(): Flow<AnimatedValue<Boolean>> { + return notificationsKeyguardInteractor.areNotificationsFullyHidden + .pairwise(initialValue = null) + .sample(deviceEntryInteractor.isBypassEnabled) { (prev, fullyHidden), bypassEnabled -> + val animate = + when { + // Don't animate for the first value + prev == null -> false + // Always animate if bypass is enabled. + bypassEnabled -> true + // If we're not bypassing and we're not going to AOD, then we're not + // animating. + !dozeParameters.alwaysOn -> false + // Don't animate when going to AOD if the display needs blanking. + dozeParameters.displayNeedsBlanking -> false + // We only want the appear animations to happen when the notifications + // get fully hidden, since otherwise the un-hide animation overlaps. + featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true + else -> fullyHidden!! + } + AnimatableEvent(fullyHidden!!, animate) + } + .toAnimatedValueFlow(completionEvents = onVisAnimationComplete) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt index 933c76f19aee..c44a2b60142c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt @@ -15,8 +15,18 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.util.ui.AnimatedValue import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flowOf /** View-model for the overflow row of notification icons displayed in the notification shade. */ class NotificationIconContainerShelfViewModel @Inject constructor() : - NotificationIconContainerViewModel + NotificationIconContainerViewModel { + override val animationsEnabled: Flow<Boolean> = flowOf(true) + override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow() + override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() + override fun completeDozeAnimation() {} + override fun completeVisibilityAnimation() {} +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt index 2217646e6022..035687a4a91b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt @@ -15,8 +15,31 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.util.ui.AnimatedValue import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow /** View-model for the row of notification icons displayed in the status bar, */ -class NotificationIconContainerStatusBarViewModel @Inject constructor() : - NotificationIconContainerViewModel +class NotificationIconContainerStatusBarViewModel +@Inject +constructor( + keyguardInteractor: KeyguardInteractor, + shadeInteractor: ShadeInteractor, +) : NotificationIconContainerViewModel { + override val animationsEnabled: Flow<Boolean> = + combine( + shadeInteractor.isShadeTouchable, + keyguardInteractor.isKeyguardShowing, + ) { panelTouchesEnabled, isKeyguardShowing -> + panelTouchesEnabled && !isKeyguardShowing + } + + override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow() + override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() + override fun completeDozeAnimation() {} + override fun completeVisibilityAnimation() {} +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt index 892b2be9ed6e..65eb22075ec7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt @@ -15,8 +15,32 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.util.ui.AnimatedValue +import kotlinx.coroutines.flow.Flow + /** * View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and * AOD. */ -interface NotificationIconContainerViewModel +interface NotificationIconContainerViewModel { + /** Are changes to the icon container animated? */ + val animationsEnabled: Flow<Boolean> + + /** Should icons be rendered in "dozing" mode? */ + val isDozing: Flow<AnimatedValue<Boolean>> + + /** Is the icon container visible? */ + val isVisible: Flow<AnimatedValue<Boolean>> + + /** + * Signal completion of the [isDozing] animation; if [isDozing]'s [AnimatedValue.isAnimating] + * property was `true`, calling this method will update it to `false. + */ + fun completeDozeAnimation() + + /** + * Signal completion of the [isVisible] animation; if [isVisible]'s [AnimatedValue.isAnimating] + * property was `true`, calling this method will update it to `false. + */ + fun completeVisibilityAnimation() +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index c61258b5e0ec..847d94861401 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -355,12 +355,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView AnimatorListenerAdapter animationListener) { enableAppearDrawing(true); mIsHeadsUpAnimation = isHeadsUpAnimation; - if (mDrawingAppearAnimation) { - startAppearAnimation(false /* isAppearing */, translationDirection, - delay, duration, onFinishedRunnable, animationListener); - } else if (onFinishedRunnable != null) { - onFinishedRunnable.run(); - } + startAppearAnimation(false /* isAppearing */, translationDirection, + delay, duration, onFinishedRunnable, animationListener); return 0; } @@ -369,10 +365,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView Runnable onFinishRunnable) { enableAppearDrawing(true); mIsHeadsUpAnimation = isHeadsUpAppear; - if (mDrawingAppearAnimation) { - startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay, - duration, null, null); - } + startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay, + duration, null, null); } private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java index 42c80ed22717..40897dae4c44 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java @@ -42,9 +42,8 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.Dependency; -import com.android.systemui.res.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.util.Compile; @@ -76,7 +75,9 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC final StatusBarNotification sbn, final NotificationEntry entry, final ExpandableNotificationRow row, - final AssistantFeedbackController controller) { + final AssistantFeedbackController controller, + final IStatusBarService statusBarService, + final NotificationGutsManager notificationGutsManager) { mPkg = sbn.getPackageName(); mPm = pm; mEntry = entry; @@ -84,8 +85,8 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC mRanking = entry.getRanking(); mFeedbackController = controller; mAppName = mPkg; - mStatusBarService = Dependency.get(IStatusBarService.class); - mNotificationGutsManager = Dependency.get(NotificationGutsManager.class); + mStatusBarService = statusBarService; + mNotificationGutsManager = notificationGutsManager; bindHeader(); bindPrompt(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 6d6566058aed..9e9116bd70e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -44,9 +44,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.statusbar.IStatusBarService; import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.CoreStartable; -import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -54,6 +54,7 @@ import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.shade.ShadeController; @@ -69,8 +70,8 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CentralSurfaces; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.wmshell.BubblesManager; @@ -99,6 +100,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager; private final StatusBarStateController mStatusBarStateController; + private final IStatusBarService mStatusBarService; private final DeviceProvisionedController mDeviceProvisionedController; private final AssistantFeedbackController mAssistantFeedbackController; @@ -127,7 +129,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta private final ShadeController mShadeController; private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor; private NotifGutsViewListener mGutsListener; - private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private final HeadsUpManager mHeadsUpManager; private final ActivityStarter mActivityStarter; @Inject @@ -152,9 +154,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor, NotificationLockscreenUserManager notificationLockscreenUserManager, StatusBarStateController statusBarStateController, + IStatusBarService statusBarService, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, - HeadsUpManagerPhone headsUpManagerPhone, + HeadsUpManager headsUpManager, ActivityStarter activityStarter) { mContext = context; mMainHandler = mainHandler; @@ -177,9 +180,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor; mLockscreenUserManager = notificationLockscreenUserManager; mStatusBarStateController = statusBarStateController; + mStatusBarService = statusBarService; mDeviceProvisionedController = deviceProvisionedController; mMetricsLogger = metricsLogger; - mHeadsUpManagerPhone = headsUpManagerPhone; + mHeadsUpManager = headsUpManager; mActivityStarter = activityStarter; } @@ -296,7 +300,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta if (mGutsListener != null) { mGutsListener.onGutsClose(entry); } - mHeadsUpManagerPhone.setGutsShown(row.getEntry(), false); + mHeadsUpManager.setGutsShown(row.getEntry(), false); }); View gutsView = item.getGutsView(); @@ -358,7 +362,8 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext, userHandle.getIdentifier()); - feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController); + feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController, + mStatusBarService, this); } /** @@ -676,7 +681,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta row.closeRemoteInput(); mListContainer.onHeightChanged(row, true /* needsAnimation */); mGutsMenuItem = menuItem; - mHeadsUpManagerPhone.setGutsShown(row.getEntry(), true); + mHeadsUpManager.setGutsShown(row.getEntry(), true); } }; guts.post(mOpenRunnable); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 9f95c0924c64..78d75582817b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -487,8 +487,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private ShadeController mShadeController; private Consumer<Boolean> mOnStackYChanged; - protected boolean mClearAllEnabled; - private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN; private final NotificationSectionsManager mSectionsManager; @@ -673,7 +671,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mDebugPaint.setStyle(Paint.Style.STROKE); mDebugPaint.setTextSize(25f); } - mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll); mGroupMembershipManager = Dependency.get(GroupMembershipManager.class); mGroupExpansionManager = Dependency.get(GroupExpansionManager.class); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); @@ -761,8 +758,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private boolean shouldShowDismissView() { - return mClearAllEnabled - && mController.hasActiveClearableNotifications(ROWS_ALL); + return mController.hasActiveClearableNotifications(ROWS_ALL); } private boolean shouldShowFooterView(boolean showDismissView) { @@ -5178,7 +5174,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable DumpUtilsKt.withIncreasedIndent( pw, () -> { - pw.println("mClearAllEnabled: " + mClearAllEnabled); pw.println( "hasActiveClearableNotifications: " + mController.hasActiveClearableNotifications( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 66b2555a4446..9695cb132ba9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -99,7 +99,6 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumper; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.NotifStackController; @@ -115,10 +114,10 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor; import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; @@ -127,6 +126,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -156,7 +156,7 @@ public class NotificationStackScrollLayoutController { private final NotificationGutsManager mNotificationGutsManager; private final NotificationsController mNotificationsController; private final NotificationVisibilityProvider mVisibilityProvider; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotificationRoundnessManager mNotificationRoundnessManager; private final TunerService mTunerService; private final DeviceProvisionedController mDeviceProvisionedController; @@ -194,7 +194,7 @@ public class NotificationStackScrollLayoutController { private final GroupExpansionManager mGroupExpansionManager; private final NotifPipelineFlags mNotifPipelineFlags; - private final SeenNotificationsProvider mSeenNotificationsProvider; + private final NotificationListInteractor mNotificationListInteractor; private final KeyguardTransitionRepository mKeyguardTransitionRepo; private NotificationStackScrollLayout mView; @@ -631,7 +631,7 @@ public class NotificationStackScrollLayoutController { NotificationGutsManager notificationGutsManager, NotificationsController notificationsController, NotificationVisibilityProvider visibilityProvider, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, DeviceProvisionedController deviceProvisionedController, @@ -662,7 +662,7 @@ public class NotificationStackScrollLayoutController { UiEventLogger uiEventLogger, NotificationRemoteInputManager remoteInputManager, VisibilityLocationProviderDelegator visibilityLocationProviderDelegator, - SeenNotificationsProvider seenNotificationsProvider, + NotificationListInteractor notificationListInteractor, ShadeController shadeController, InteractionJankMonitor jankMonitor, StackStateLogger stackLogger, @@ -715,7 +715,7 @@ public class NotificationStackScrollLayoutController { mUiEventLogger = uiEventLogger; mRemoteInputManager = remoteInputManager; mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator; - mSeenNotificationsProvider = seenNotificationsProvider; + mNotificationListInteractor = notificationListInteractor; mShadeController = shadeController; mNotifIconAreaController = notifIconAreaController; mFeatureFlags = featureFlags; @@ -2006,7 +2006,7 @@ public class NotificationStackScrollLayoutController { public void setNotifStats(@NonNull NotifStats notifStats) { mNotifStats = notifStats; mView.setHasFilteredOutSeenNotifications( - mSeenNotificationsProvider.getHasFilteredOutSeenNotifications()); + mNotificationListInteractor.getHasFilteredOutSeenNotifications().getValue()); updateFooter(); updateShowEmptyShadeView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt new file mode 100644 index 000000000000..f6ed8c884816 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack.data.repository + +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Repository for information about the current notification list. */ +@SysUISingleton +class NotificationListRepository @Inject constructor() { + private val _hasFilteredOutSeenNotifications = MutableStateFlow(false) + val hasFilteredOutSeenNotifications: StateFlow<Boolean> = + _hasFilteredOutSeenNotifications.asStateFlow() + + fun setHasFilteredOutSeenNotifications(value: Boolean) { + _hasFilteredOutSeenNotifications.value = value + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt new file mode 100644 index 000000000000..3fd68a8bdd7c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +/** Interactor for business logic associated with the notification stack. */ +@SysUISingleton +class NotificationListInteractor +@Inject +constructor( + private val notificationListRepository: NotificationListRepository, +) { + /** Are any already-seen notifications currently filtered out of the shade? */ + val hasFilteredOutSeenNotifications: StateFlow<Boolean> + get() = notificationListRepository.hasFilteredOutSeenNotifications + + fun setHasFilteredOutSeenNotifications(value: Boolean) { + notificationListRepository.setHasFilteredOutSeenNotifications(value) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 3e2f10dd1a0c..6e6318e780dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -220,6 +220,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.ExtensionController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -236,6 +237,8 @@ import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.SplashscreenContentDrawer; import com.android.wm.shell.startingsurface.StartingSurface; +import dagger.Lazy; + import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; @@ -247,8 +250,6 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; -import dagger.Lazy; - /** * A class handling initialization and coordination between some of the key central surfaces in * System UI: The notification shade, the keyguard (lockscreen), and the status bar. @@ -417,7 +418,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final NotificationWakeUpCoordinator mWakeUpCoordinator; private final KeyguardBypassController mKeyguardBypassController; private final KeyguardStateController mKeyguardStateController; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; @@ -611,7 +612,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { NotificationWakeUpCoordinator notificationWakeUpCoordinator, KeyguardBypassController keyguardBypassController, KeyguardStateController keyguardStateController, - HeadsUpManagerPhone headsUpManagerPhone, + HeadsUpManager headsUpManager, DynamicPrivacyController dynamicPrivacyController, FalsingManager falsingManager, FalsingCollector falsingCollector, @@ -718,7 +719,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mWakeUpCoordinator = notificationWakeUpCoordinator; mKeyguardBypassController = keyguardBypassController; mKeyguardStateController = keyguardStateController; - mHeadsUpManager = headsUpManagerPhone; + mHeadsUpManager = headsUpManager; mBackActionInteractor = backActionInteractor; mKeyguardIndicationController = keyguardIndicationController; mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; @@ -1088,11 +1089,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { void initShadeVisibilityListener() { mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() { @Override - public void visibilityChanged(boolean visible) { - onShadeVisibilityChanged(visible); - } - - @Override public void expandedVisibleChanged(boolean expandedVisible) { if (expandedVisible) { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); @@ -1195,7 +1191,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }); mStatusBarInitializer.initializeStatusBar(); - mStatusBarTouchableRegionManager.setup(this, getNotificationShadeWindowView()); + mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView()); createNavigationBar(result); @@ -2688,7 +2684,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { !mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive()); mShadeSurface.setTouchAndAnimationDisabled(disabled); - mNotificationIconAreaController.setAnimationsEnabled(!disabled); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mNotificationIconAreaController.setAnimationsEnabled(!disabled); + } } final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @@ -2844,13 +2842,16 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScrimController.setExpansionAffectsAlpha(!unlocking); if (mAlternateBouncerInteractor.isVisibleState()) { - if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) - && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED - || mTransitionToFullShadeProgress > 0f)) { - mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE); - } else { - mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); + if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) + && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED + || mTransitionToFullShadeProgress > 0f)) { + mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE); + } else { + mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); + } } + // This will cancel the keyguardFadingAway animation if it is running. We need to do // this as otherwise it can remain pending and leave keyguard in a weird state. mUnlockScrimCallback.onCancelled(); @@ -2913,8 +2914,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected boolean mDeviceInteractive; - protected boolean mVisible; - protected DevicePolicyManager mDevicePolicyManager; private final PowerManager mPowerManager; protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -3032,16 +3031,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { afterKeyguardGone); } - private void onShadeVisibilityChanged(boolean visible) { - if (mVisible != visible) { - mVisible = visible; - if (visible) { - DejankUtils.notifyRendererOfExpensiveFrame( - getNotificationShadeWindowView(), "onShadeVisibilityChanged"); - } - } - } - private void clearNotificationEffects() { try { mBarService.clearNotificationEffects(); @@ -3163,7 +3152,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // TODO: Bring these out of CentralSurfaces. mUserInfoControllerImpl.onDensityOrFontScaleChanged(); mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); - mHeadsUpManager.onDensityOrFontScaleChanged(); } @Override @@ -3200,7 +3188,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // down on the lockscreen), clear notification LED, vibration, // ringing. // Other transitions are covered in WindowRootViewVisibilityInteractor. - if (mVisible && (newState == StatusBarState.SHADE_LOCKED + if (mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible().getValue() + && (newState == StatusBarState.SHADE_LOCKED || mStatusBarStateController.goingToFullShade())) { clearNotificationEffects(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index 4849f64659d0..d3d11ea4a9f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.Assert; @@ -80,7 +81,7 @@ public final class DozeServiceHost implements DozeHost { private final WakefulnessLifecycle mWakefulnessLifecycle; private final SysuiStatusBarStateController mStatusBarStateController; private final DeviceProvisionedController mDeviceProvisionedController; - private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private final HeadsUpManager mHeadsUpManager; private final BatteryController mBatteryController; private final ScrimController mScrimController; private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; @@ -106,7 +107,7 @@ public final class DozeServiceHost implements DozeHost { WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, DeviceProvisionedController deviceProvisionedController, - HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController, + HeadsUpManager headsUpManager, BatteryController batteryController, ScrimController scrimController, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, Lazy<AssistManager> assistManagerLazy, @@ -123,7 +124,7 @@ public final class DozeServiceHost implements DozeHost { mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mDeviceProvisionedController = deviceProvisionedController; - mHeadsUpManagerPhone = headsUpManagerPhone; + mHeadsUpManager = headsUpManager; mBatteryController = batteryController; mScrimController = scrimController; mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; @@ -135,7 +136,7 @@ public final class DozeServiceHost implements DozeHost { mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; mAuthController = authController; mNotificationIconAreaController = notificationIconAreaController; - mHeadsUpManagerPhone.addListener(mOnHeadsUpChangedListener); + mHeadsUpManager.addListener(mOnHeadsUpChangedListener); mDozeInteractor = dozeInteractor; } @@ -337,8 +338,8 @@ public final class DozeServiceHost implements DozeHost { if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH) { mScrimController.setWakeLockScreenSensorActive(true); } - if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) { - mHeadsUpManagerPhone.extendHeadsUp(); + if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) { + mHeadsUpManager.extendHeadsUp(); } else { mDozeScrimController.extendPulse(); } @@ -493,7 +494,7 @@ public final class DozeServiceHost implements DozeHost { mDozeScrimController.cancelPendingPulseTimeout(); } } - if (!isHeadsUp && !mHeadsUpManagerPhone.hasNotifications()) { + if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) { // There are no longer any notifications to show. We should end the // pulse now. stopPulsing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 270c40e801cd..c493eeda7077 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -26,9 +26,9 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ViewClippingUtil; -import com.android.systemui.res.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeHeadsUpTracker; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.ViewController; @@ -70,7 +71,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar private static final SourceType HEADS_UP = SourceType.from("HeadsUp"); private static final SourceType PULSING = SourceType.from("Pulsing"); private final NotificationIconAreaController mNotificationIconAreaController; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotificationStackScrollLayoutController mStackScrollerController; private final DarkIconDispatcher mDarkIconDispatcher; @@ -108,7 +109,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar @Inject public HeadsUpAppearanceController( NotificationIconAreaController notificationIconAreaController, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, StatusBarStateController stateController, PhoneStatusBarTransitions phoneStatusBarTransitions, KeyguardBypassController bypassController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index a5ea1426164b..6b4382f731ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -29,11 +29,11 @@ import androidx.collection.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.SystemBarUtils; -import com.android.systemui.Dumpable; -import com.android.systemui.res.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -42,10 +42,12 @@ import com.android.systemui.statusbar.notification.collection.provider.VisualSta import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.AnimationStateHandler; +import com.android.systemui.statusbar.policy.BaseHeadsUpManager; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange; import java.io.PrintWriter; import java.util.ArrayList; @@ -53,11 +55,11 @@ import java.util.HashSet; import java.util.List; import java.util.Stack; -/** - * A implementation of HeadsUpManager for phone and car. - */ -public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, - OnHeadsUpChangedListener { +import javax.inject.Inject; + +/** A implementation of HeadsUpManager for phone. */ +@SysUISingleton +public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUpChangedListener { private static final String TAG = "HeadsUpManagerPhone"; @VisibleForTesting @@ -102,7 +104,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, /////////////////////////////////////////////////////////////////////////////////////////////// // Constructor: - + @Inject public HeadsUpManagerPhone(@NonNull final Context context, HeadsUpManagerLogger logger, StatusBarStateController statusBarStateController, @@ -154,7 +156,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, /** * Add a listener to receive callbacks onHeadsUpGoingAway */ - void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) { + @Override + public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) { mHeadsUpPhoneListeners.add(listener); } @@ -162,7 +165,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * Gets the touchable region needed for heads up notifications. Returns null if no touchable * region is required (ie: no heads up notification currently exists). */ - @Nullable Region getTouchableRegion() { + @Override + public @Nullable Region getTouchableRegion() { NotificationEntry topEntry = getTopEntry(); // This call could be made in an inconsistent state while the pinnedMode hasn't been @@ -197,8 +201,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * @param key the key of the touched notification * @return whether the touch is invalid and should be discarded */ - boolean shouldSwallowClick(@NonNull String key) { - HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); + @Override + public boolean shouldSwallowClick(@NonNull String key) { + BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); return entry != null && mClock.currentTimeMillis() < entry.mPostTime; } @@ -238,7 +243,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * Set that we are exiting the headsUp pinned mode, but some notifications might still be * animating out. This is used to keep the touchable regions in a reasonable state. */ - void setHeadsUpGoingAway(boolean headsUpGoingAway) { + @Override + public void setHeadsUpGoingAway(boolean headsUpGoingAway) { if (headsUpGoingAway != mHeadsUpGoingAway) { mHeadsUpGoingAway = headsUpGoingAway; for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) { @@ -247,7 +253,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } } - boolean isHeadsUpGoingAway() { + @Override + public boolean isHeadsUpGoingAway() { return mHeadsUpGoingAway; } @@ -260,8 +267,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, public void setRemoteInputActive( @NonNull NotificationEntry entry, boolean remoteInputActive) { HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey()); - if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) { - headsUpEntry.remoteInputActive = remoteInputActive; + if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) { + headsUpEntry.mRemoteInputActive = remoteInputActive; if (remoteInputActive) { headsUpEntry.removeAutoRemovalCallbacks("setRemoteInputActive(true)"); } else { @@ -313,6 +320,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mSwipedOutKeys.add(key); } + @Override public boolean removeNotification(@NonNull String key, boolean releaseImmediately, boolean animate) { if (animate) { @@ -411,7 +419,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, /////////////////////////////////////////////////////////////////////////////////////////////// // HeadsUpEntryPhone: - protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry { + protected class HeadsUpEntryPhone extends BaseHeadsUpManager.HeadsUpEntry { private boolean mGutsShownPinned; @@ -459,11 +467,11 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, @Override public void setExpanded(boolean expanded) { - if (this.expanded == expanded) { + if (this.mExpanded == expanded) { return; } - this.expanded = expanded; + this.mExpanded = expanded; if (expanded) { removeAutoRemovalCallbacks("setExpanded(true)"); } else { @@ -504,21 +512,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } } - public interface AnimationStateHandler { - void setHeadsUpGoingAwayAnimationsAllowed(boolean allowed); - } - - /** - * Listener to register for HeadsUpNotification Phone changes. - */ - public interface OnHeadsUpPhoneListenerChange { - /** - * Called when a heads up notification is 'going away' or no longer 'going away'. - * See {@link HeadsUpManagerPhone#setHeadsUpGoingAway}. - */ - void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway); - } - private final StateListener mStatusBarStateListener = new StateListener() { @Override public void onStateChanged(int newState) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt new file mode 100644 index 000000000000..0d0f2cd50a29 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt @@ -0,0 +1,11 @@ +package com.android.systemui.statusbar.phone + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.HeadsUpManager +import dagger.Binds +import dagger.Module + +@Module +interface HeadsUpModule { + @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerPhone): HeadsUpManager +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index dcbaac20eacc..198272e9b162 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -26,13 +26,14 @@ import com.android.systemui.Gefingerpoken; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.policy.HeadsUpManager; /** * A helper class to handle touches on the heads-up views. */ public class HeadsUpTouchHelper implements Gefingerpoken { - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final IStatusBarService mStatusBarService; private final Callback mCallback; private int mTrackingPointer; @@ -45,7 +46,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private final HeadsUpNotificationViewController mPanel; private ExpandableNotificationRow mPickedChild; - public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager, + public HeadsUpTouchHelper(HeadsUpManager headsUpManager, IStatusBarService statusBarService, Callback callback, HeadsUpNotificationViewController notificationPanelView) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 7cbaf63bc6db..b15c0fdd5a4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -33,6 +33,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.collection.ArrayMap; @@ -624,12 +625,32 @@ public class NotificationIconContainer extends ViewGroup { } public void setDozing(boolean dozing, boolean fade, long delay) { + setDozing(dozing, fade, delay, /* endRunnable= */ null); + } + + public void setDozing(boolean dozing, boolean fade, long delay, + @Nullable Runnable endRunnable) { mDozing = dozing; mDisallowNextAnimation |= !fade; - for (int i = 0; i < getChildCount(); i++) { + final int childCount = getChildCount(); + // Track all the child invocations of setDozing, invoking the top-level endRunnable once + // they have all completed. + final Runnable onChildCompleted = endRunnable == null ? null : new Runnable() { + private int mPendingCallbacks = childCount; + + @Override + public void run() { + if (--mPendingCallbacks == 0) { + endRunnable.run(); + } + } + }; + for (int i = 0; i < childCount; i++) { View view = getChildAt(i); if (view instanceof StatusBarIconView) { - ((StatusBarIconView) view).setDozing(dozing, fade, delay); + ((StatusBarIconView) view).setDozing(dozing, fade, delay, onChildCompleted); + } else if (onChildCompleted != null) { + onChildCompleted.run(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java index 4b39854e43ff..235ed25e2ea1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java @@ -24,6 +24,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -39,7 +40,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, private final ShadeViewController mShadeViewController; private final NotificationStackScrollLayoutController mNsslController; private final KeyguardBypassController mKeyguardBypassController; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final StatusBarStateController mStatusBarStateController; private final NotificationRemoteInputManager mNotificationRemoteInputManager; @@ -50,7 +51,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, ShadeViewController shadeViewController, NotificationStackScrollLayoutController nsslController, KeyguardBypassController keyguardBypassController, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, StatusBarStateController statusBarStateController, NotificationRemoteInputManager notificationRemoteInputManager) { mNotificationShadeWindowController = notificationShadeWindowController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index eedf35f1e9d4..62b2445de13a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1573,6 +1573,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * notification shade's child views. */ public boolean shouldInterceptTouchEvent(MotionEvent event) { + if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + return false; + } return mAlternateBouncerInteractor.isVisibleState(); } @@ -1581,6 +1584,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * showing. */ public boolean onTouch(MotionEvent event) { + if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { + return false; + } + boolean handleTouch = shouldInterceptTouchEvent(event); if (handleTouch) { final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index dc4c7095237e..dbee080f238d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -76,6 +76,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.wmshell.BubblesManager; @@ -102,7 +103,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final Executor mUiBgExecutor; private final NotificationVisibilityProvider mVisibilityProvider; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final ActivityStarter mActivityStarter; private final NotificationClickNotifier mClickNotifier; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -141,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit Handler mainThreadHandler, Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, ActivityStarter activityStarter, NotificationClickNotifier clickNotifier, StatusBarKeyguardViewManager statusBarKeyguardViewManager, @@ -213,7 +214,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit */ @Override public void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row) { - mLogger.logStartingActivityFromClick(entry); + mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(), + mKeyguardStateController.isVisible(), + mNotificationShadeWindowController.getPanelExpanded()); if (mRemoteInputManager.isRemoteInputActive(entry)) { // We have an active remote input typed and the user clicked on the notification. 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 2d30a12c3948..4211cabcbc56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt @@ -30,11 +30,16 @@ import javax.inject.Inject class StatusBarNotificationActivityStarterLogger @Inject constructor( @NotifInteractionLog private val buffer: LogBuffer ) { - fun logStartingActivityFromClick(entry: NotificationEntry) { + fun logStartingActivityFromClick(entry: NotificationEntry, isHeadsUpState: Boolean, + isKeyguardVisible: Boolean, isPanelExpanded: Boolean) { buffer.log(TAG, DEBUG, { str1 = entry.logKey + bool1 = isHeadsUpState + bool2 = isKeyguardVisible + bool3 = isPanelExpanded }, { - "(1/5) onNotificationClicked: $str1" + "(1/5) onNotificationClicked: $str1 isHeadsUpState: $bool1 " + + "isKeyguardVisible: $bool2 isPanelExpanded: $bool3" }) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index fb7f9d1ec19b..2d14f6b3c508 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -31,11 +31,11 @@ import android.view.View; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.InitController; -import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.res.R; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.ShadeViewController; @@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; @@ -76,7 +77,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu private final NotificationMediaManager mMediaManager; private final NotificationGutsManager mGutsManager; private final ShadeViewController mNotificationPanel; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final AboveShelfObserver mAboveShelfObserver; private final DozeScrimController mDozeScrimController; private final NotificationsInteractor mNotificationsInteractor; @@ -98,7 +99,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu Context context, ShadeViewController panel, QuickSettingsController quickSettingsController, - HeadsUpManagerPhone headsUp, + HeadsUpManager headsUp, NotificationShadeWindowView statusBarWindow, ActivityStarter activityStarter, NotificationStackScrollLayoutController stackScrollerController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index 40bd8d3446b5..ba73c1098637 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -31,15 +31,18 @@ import android.view.WindowInsets; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.ScreenDecorations; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.kotlin.JavaAdapter; @@ -58,13 +61,12 @@ public final class StatusBarTouchableRegionManager implements Dumpable { private static final String TAG = "TouchableRegionManager"; private final Context mContext; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotificationShadeWindowController mNotificationShadeWindowController; private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private boolean mIsStatusBarExpanded = false; private boolean mShouldAdjustInsets = false; - private CentralSurfaces mCentralSurfaces; private View mNotificationShadeWindowView; private View mNotificationPanelView; private boolean mForceCollapsedUntilLayout = false; @@ -72,6 +74,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable { private Region mTouchableRegion = new Region(); private int mDisplayCutoutTouchableRegionSize; private int mStatusBarHeight; + private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; + private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener; @@ -80,12 +84,14 @@ public final class StatusBarTouchableRegionManager implements Dumpable { Context context, NotificationShadeWindowController notificationShadeWindowController, ConfigurationController configurationController, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, ShadeExpansionStateManager shadeExpansionStateManager, Provider<SceneInteractor> sceneInteractor, Provider<JavaAdapter> javaAdapter, SceneContainerFlags sceneContainerFlags, - UnlockedScreenOffAnimationController unlockedScreenOffAnimationController + UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + PrimaryBouncerInteractor primaryBouncerInteractor, + AlternateBouncerInteractor alternateBouncerInteractor ) { mContext = context; initResources(); @@ -128,13 +134,12 @@ public final class StatusBarTouchableRegionManager implements Dumpable { this::onShadeExpansionFullyChanged); } + mPrimaryBouncerInteractor = primaryBouncerInteractor; + mAlternateBouncerInteractor = alternateBouncerInteractor; mOnComputeInternalInsetsListener = this::onComputeInternalInsets; } - protected void setup( - @NonNull CentralSurfaces centralSurfaces, - @NonNull View notificationShadeWindowView) { - mCentralSurfaces = centralSurfaces; + protected void setup(@NonNull View notificationShadeWindowView) { mNotificationShadeWindowView = notificationShadeWindowView; mNotificationPanelView = mNotificationShadeWindowView.findViewById(R.id.notification_panel); } @@ -260,7 +265,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable { // since we don't want stray touches to go through the light reveal scrim to whatever is // underneath. return mIsStatusBarExpanded - || mCentralSurfaces.isBouncerShowing() + || mPrimaryBouncerInteractor.isShowing().getValue() + || mAlternateBouncerInteractor.isVisibleState() || mUnlockedScreenOffAnimationController.isAnimationPlaying(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index babd435c62ba..63c022c5bb6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -38,11 +38,11 @@ import com.android.app.animation.Interpolators; import com.android.app.animation.InterpolatorsAndroidX; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; @@ -74,8 +74,6 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; -import kotlin.Unit; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -85,6 +83,7 @@ import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; +import kotlin.Unit; /** * Contains the collapsed status bar and handles hiding/showing based on disable flags @@ -279,7 +278,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mDumpManager.registerDumpable(getClass().getSimpleName(), this); - mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this); + mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create( + (PhoneStatusBarView) getView()); mStatusBarFragmentComponent.init(); mStartableStates.clear(); for (Startable startable : mStatusBarFragmentComponent.getStartables()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java index d9a58442d856..0618abbf00d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java @@ -27,11 +27,11 @@ import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.StatusBarDemoMode; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; -import java.util.Set; - import dagger.BindsInstance; import dagger.Subcomponent; +import java.util.Set; + /** * A subcomponent that gets re-created each time we create a new {@link CollapsedStatusBarFragment}. * @@ -54,7 +54,7 @@ public interface StatusBarFragmentComponent { @Subcomponent.Factory interface Factory { StatusBarFragmentComponent create( - @BindsInstance CollapsedStatusBarFragment collapsedStatusBarFragment); + @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index 6ef877bd57bf..3741f143dd8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -19,15 +19,14 @@ package com.android.systemui.statusbar.phone.fragment.dagger; import android.view.View; import android.view.ViewStub; -import com.android.systemui.res.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.dagger.qualifiers.RootView; +import com.android.systemui.res.R; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarLocation; -import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -51,15 +50,6 @@ public interface StatusBarFragmentModule { /** */ @Provides - @RootView - @StatusBarFragmentScope - static PhoneStatusBarView providePhoneStatusBarView( - CollapsedStatusBarFragment collapsedStatusBarFragment) { - return (PhoneStatusBarView) collapsedStatusBarFragment.getView(); - } - - /** */ - @Provides @StatusBarFragmentScope static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.battery); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java index e59ec044bece..cec76f3140cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -34,8 +34,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.EventLogTags; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.res.R; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -47,7 +47,8 @@ import java.io.PrintWriter; * A manager which handles heads up notifications which is a special mode where * they simply peek from the top of the screen. */ -public abstract class HeadsUpManager extends AlertingNotificationManager { +public abstract class BaseHeadsUpManager extends AlertingNotificationManager implements + HeadsUpManager { private static final String TAG = "HeadsUpManager"; private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; @@ -81,7 +82,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } } - public HeadsUpManager(@NonNull final Context context, + public BaseHeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger, @Main Handler handler, AccessibilityManagerWrapper accessibilityManagerWrapper, @@ -131,6 +132,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { mListeners.remove(listener); } + /** Updates the notification with the given key. */ public void updateNotification(@NonNull String key, boolean alert) { super.updateNotification(key, alert); HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); @@ -146,7 +148,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { // the NotificationEntry into AlertingNotificationManager's mAlertEntries map. return hasFullScreenIntent(entry); } - return hasFullScreenIntent(entry) && !headsUpEntry.wasUnpinned; + return hasFullScreenIntent(entry) && !headsUpEntry.mWasUnpinned; } protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) { @@ -154,11 +156,11 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } protected void setEntryPinned( - @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) { + @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) { mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned); NotificationEntry entry = headsUpEntry.mEntry; if (!isPinned) { - headsUpEntry.wasUnpinned = true; + headsUpEntry.mWasUnpinned = true; } if (entry.isRowPinned() != isPinned) { entry.setRowPinned(isPinned); @@ -292,10 +294,12 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { mUser = user; } + /** Returns the ID of the current user. */ public int getUser() { return mUser; } + @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("HeadsUpManager state:"); dumpInternal(pw, args); @@ -309,9 +313,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { for (AlertEntry entry: mAlertEntries.values()) { pw.print(" HeadsUpEntry="); pw.println(entry.mEntry); } - int N = mSnoozedPackages.size(); - pw.println(" snoozed packages: " + N); - for (int i = 0; i < N; i++) { + int n = mSnoozedPackages.size(); + pw.println(" snoozed packages: " + n); + for (int i = 0; i < n; i++) { pw.print(" "); pw.print(mSnoozedPackages.valueAt(i)); pw.print(", "); pw.println(mSnoozedPackages.keyAt(i)); } @@ -399,20 +403,20 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * * @param entry the entry that might be indirectly removed by the user's action * - * @see com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator#mActionPressListener + * @see HeadsUpCoordinator#mActionPressListener * @see #canRemoveImmediately(String) */ public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); if (headsUpEntry != null) { - headsUpEntry.userActionMayIndirectlyRemove = true; + headsUpEntry.mUserActionMayIndirectlyRemove = true; } } @Override public boolean canRemoveImmediately(@NonNull String key) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); - if (headsUpEntry != null && headsUpEntry.userActionMayIndirectlyRemove) { + if (headsUpEntry != null && headsUpEntry.mUserActionMayIndirectlyRemove) { return true; } return super.canRemoveImmediately(key); @@ -424,9 +428,6 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { return new HeadsUpEntry(); } - public void onDensityOrFontScaleChanged() { - } - /** * Determines if the notification is for a critical call that must display on top of an active * input notification. @@ -445,16 +446,16 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * lifecycle automatically when created. */ protected class HeadsUpEntry extends AlertEntry { - public boolean remoteInputActive; - public boolean userActionMayIndirectlyRemove; + public boolean mRemoteInputActive; + public boolean mUserActionMayIndirectlyRemove; - protected boolean expanded; - protected boolean wasUnpinned; + protected boolean mExpanded; + protected boolean mWasUnpinned; @Override public boolean isSticky() { - return (mEntry.isRowPinned() && expanded) - || remoteInputActive + return (mEntry.isRowPinned() && mExpanded) + || mRemoteInputActive || hasFullScreenIntent(mEntry); } @@ -490,9 +491,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { return 1; } - if (remoteInputActive && !headsUpEntry.remoteInputActive) { + if (mRemoteInputActive && !headsUpEntry.mRemoteInputActive) { return -1; - } else if (!remoteInputActive && headsUpEntry.remoteInputActive) { + } else if (!mRemoteInputActive && headsUpEntry.mRemoteInputActive) { return 1; } @@ -500,14 +501,14 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } public void setExpanded(boolean expanded) { - this.expanded = expanded; + this.mExpanded = expanded; } @Override public void reset() { super.reset(); - expanded = false; - remoteInputActive = false; + mExpanded = false; + mRemoteInputActive = false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt new file mode 100644 index 000000000000..21acfb41f10c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.android.systemui.statusbar.policy + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** + * A [Flow] that emits whenever screen density or font scale has changed. + * + * @see ConfigurationController.ConfigurationListener.onDensityOrFontScaleChanged + */ +val ConfigurationController.onDensityOrFontScaleChanged: Flow<Unit> + get() = + ConflatedCallbackFlow.conflatedCallbackFlow { + val listener = + object : ConfigurationController.ConfigurationListener { + override fun onDensityOrFontScaleChanged() { + trySend(Unit) + } + } + addCallback(listener) + awaitClose { removeCallback(listener) } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt new file mode 100644 index 000000000000..d9527fe98b1a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt @@ -0,0 +1,245 @@ +package com.android.systemui.statusbar.policy + +import android.graphics.Region +import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import dagger.Binds +import dagger.Module +import java.io.PrintWriter +import java.util.stream.Stream +import javax.inject.Inject + +/** + * A manager which handles heads up notifications which is a special mode where they simply peek + * from the top of the screen. + */ +interface HeadsUpManager : Dumpable { + /** The stream of all current notifications managed by this manager. */ + val allEntries: Stream<NotificationEntry> + + /** Add a listener to receive callbacks onHeadsUpGoingAway. */ + fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) + + /** Adds an OnHeadUpChangedListener to observe events. */ + fun addListener(listener: OnHeadsUpChangedListener) + + fun addSwipedOutNotification(key: String) + + /** + * Whether or not the alert can be removed currently. If it hasn't been on screen long enough it + * should not be removed unless forced + * + * @param key the key to check if removable + * @return true if the alert entry can be removed + */ + fun canRemoveImmediately(key: String): Boolean + + /** + * Compare two entries and decide how they should be ranked. + * + * @return -1 if the first argument should be ranked higher than the second, 1 if the second one + * should be ranked higher and 0 if they are equal. + */ + fun compare(a: NotificationEntry?, b: NotificationEntry?): Int + /** + * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts + * longer. + */ + fun extendHeadsUp() + + /** Returns when a HUN entry should be removed in milliseconds from now. */ + fun getEarliestRemovalTime(key: String?): Long + + /** Returns the top Heads Up Notification, which appears to show at first. */ + fun getTopEntry(): NotificationEntry? + + /** + * Gets the touchable region needed for heads up notifications. Returns null if no touchable + * region is required (ie: no heads up notification currently exists). + */ + fun getTouchableRegion(): Region? + + /** + * Whether or not there are any active alerting notifications. + * + * @return true if there is an alert, false otherwise + */ + fun hasNotifications(): Boolean = false + + /** Returns whether there are any pinned Heads Up Notifications or not. */ + fun hasPinnedHeadsUp(): Boolean + + /** Returns whether or not the given notification is alerting and managed by this manager. */ + fun isAlerting(key: String): Boolean + + fun isHeadsUpGoingAway(): Boolean + + /** Returns if the given notification is snoozed or not. */ + fun isSnoozed(packageName: String): Boolean + + /** Returns whether the entry is (pinned and expanded) or (has an active remote input). */ + fun isSticky(key: String?): Boolean + + fun isTrackingHeadsUp(): Boolean + + fun onExpandingFinished() + + /** Removes the OnHeadUpChangedListener from the observer list. */ + fun removeListener(listener: OnHeadsUpChangedListener) + + /** + * Try to remove the notification. May not succeed if the notification has not been shown long + * enough and needs to be kept around. + * + * @param key the key of the notification to remove + * @param releaseImmediately force a remove regardless of earliest removal time + * @return true if notification is removed, false otherwise + */ + fun removeNotification(key: String, releaseImmediately: Boolean): Boolean + + /** + * Try to remove the notification. May not succeed if the notification has not been shown long + * enough and needs to be kept around. + * + * @param key the key of the notification to remove + * @param releaseImmediately force a remove regardless of earliest removal time + * @param animate if true, animate the removal + * @return true if notification is removed, false otherwise + */ + fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean): Boolean + + /** Clears all managed notifications. */ + fun releaseAllImmediately() + + fun setAnimationStateHandler(handler: AnimationStateHandler) + + /** + * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until + * it's collapsed again. + */ + fun setExpanded(entry: NotificationEntry, expanded: Boolean) + + /** + * Sets whether an entry's guts are exposed and therefore it should stick in the heads up area + * if it's pinned until it's hidden again. + */ + fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) + + /** + * Set that we are exiting the headsUp pinned mode, but some notifications might still be + * animating out. This is used to keep the touchable regions in a reasonable state. + */ + fun setHeadsUpGoingAway(headsUpGoingAway: Boolean) + + /** + * Notifies that a remote input textbox in notification gets active or inactive. + * + * @param entry The entry of the target notification. + * @param remoteInputActive True to notify active, False to notify inactive. + */ + fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) + + fun setTrackingHeadsUp(tracking: Boolean) + + /** Sets the current user. */ + fun setUser(user: Int) + + /** + * Notes that the user took an action on an entry that might indirectly cause the system or the + * app to remove the notification. + * + * @param entry the entry that might be indirectly removed by the user's action + * @see + * com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator.mActionPressListener + * @see .canRemoveImmediately + */ + fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) + + /** + * Decides whether a click is invalid for a notification, i.e. it has not been shown long enough + * that a user might have consciously clicked on it. + * + * @param key the key of the touched notification + * @return whether the touch is invalid and should be discarded + */ + fun shouldSwallowClick(key: String): Boolean + + /** + * Called when posting a new notification that should alert the user and appear on screen. Adds + * the notification to be managed. + * + * @param entry entry to show + */ + fun showNotification(entry: NotificationEntry) + + fun snooze() + + /** + * Unpins all pinned Heads Up Notifications. + * + * @param userUnPinned The unpinned action is trigger by user real operation. + */ + fun unpinAll(userUnPinned: Boolean) + + fun updateNotification(key: String, alert: Boolean) +} + +/** Sets the animation state of the HeadsUpManager. */ +interface AnimationStateHandler { + fun setHeadsUpGoingAwayAnimationsAllowed(allowed: Boolean) +} + +/** Listener to register for HeadsUpNotification Phone changes. */ +interface OnHeadsUpPhoneListenerChange { + /** + * Called when a heads up notification is 'going away' or no longer 'going away'. See + * [HeadsUpManager.setHeadsUpGoingAway]. + */ + fun onHeadsUpGoingAwayStateChanged(headsUpGoingAway: Boolean) +} + +/* No op impl of HeadsUpManager. */ +class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager { + override val allEntries = Stream.empty<NotificationEntry>() + override fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) {} + override fun addListener(listener: OnHeadsUpChangedListener) {} + override fun addSwipedOutNotification(key: String) {} + override fun canRemoveImmediately(key: String) = false + override fun compare(a: NotificationEntry?, b: NotificationEntry?) = 0 + override fun dump(pw: PrintWriter, args: Array<out String>) {} + override fun extendHeadsUp() {} + override fun getEarliestRemovalTime(key: String?) = 0L + override fun getTouchableRegion(): Region? = null + override fun getTopEntry() = null + override fun hasPinnedHeadsUp() = false + override fun isAlerting(key: String) = false + override fun isHeadsUpGoingAway() = false + override fun isSnoozed(packageName: String) = false + override fun isSticky(key: String?) = false + override fun isTrackingHeadsUp() = false + override fun onExpandingFinished() {} + override fun releaseAllImmediately() {} + override fun removeListener(listener: OnHeadsUpChangedListener) {} + override fun removeNotification(key: String, releaseImmediately: Boolean) = false + override fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean) = + false + override fun setAnimationStateHandler(handler: AnimationStateHandler) {} + override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {} + override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {} + override fun setHeadsUpGoingAway(headsUpGoingAway: Boolean) {} + override fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) {} + override fun setTrackingHeadsUp(tracking: Boolean) {} + override fun setUser(user: Int) {} + override fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) {} + override fun shouldSwallowClick(key: String): Boolean = false + override fun showNotification(entry: NotificationEntry) {} + override fun snooze() {} + override fun unpinAll(userUnPinned: Boolean) {} + override fun updateNotification(key: String, alert: Boolean) {} +} + +@Module +interface HeadsUpEmptyImplModule { + @Binds @SysUISingleton fun bindsHeadsUpManager(noOpHum: HeadsUpManagerEmptyImpl): HeadsUpManager +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt index c302d6aeadbc..859e636a7714 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.content.res.Resources import com.android.systemui.res.R +import javax.inject.Inject /** * Fake SplitShadeStateController @@ -24,7 +25,7 @@ import com.android.systemui.res.R * Identical behaviour to legacy implementation (that used LargeScreenUtils.kt) I.E., behaviour * based solely on resources, no extra flag logic. */ -class ResourcesSplitShadeStateController : SplitShadeStateController { +class ResourcesSplitShadeStateController @Inject constructor() : SplitShadeStateController { override fun shouldUseSplitNotificationShade(resources: Resources): Boolean { return resources.getBoolean(R.bool.config_use_split_notification_shade) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index a77c69236946..818ba9512e15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepository; import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepositoryImpl; +import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepositoryModule; import dagger.Binds; import dagger.Module; @@ -80,7 +81,7 @@ import java.util.concurrent.Executor; import javax.inject.Named; /** Dagger Module for code in the statusbar.policy package. */ -@Module +@Module(includes = { DeviceProvisioningRepositoryModule.class }) public interface StatusBarPolicyModule { String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt new file mode 100644 index 000000000000..1160d657ba88 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy.data.repository + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** Tracks state related to device provisioning. */ +interface DeviceProvisioningRepository { + /** + * Whether this device has been provisioned. + * + * @see android.provider.Settings.Global.DEVICE_PROVISIONED + */ + val isDeviceProvisioned: Flow<Boolean> + + /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */ + val isFactoryResetProtectionActive: Flow<Boolean> +} + +@Module +interface DeviceProvisioningRepositoryModule { + @Binds fun bindsImpl(impl: DeviceProvisioningRepositoryImpl): DeviceProvisioningRepository +} + +class DeviceProvisioningRepositoryImpl +@Inject +constructor( + private val deviceProvisionedController: DeviceProvisionedController, +) : DeviceProvisioningRepository { + + override val isDeviceProvisioned: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : DeviceProvisionedController.DeviceProvisionedListener { + override fun onDeviceProvisionedChanged() { + trySend(deviceProvisionedController.isDeviceProvisioned) + } + } + deviceProvisionedController.addCallback(listener) + trySend(deviceProvisionedController.isDeviceProvisioned) + awaitClose { deviceProvisionedController.removeCallback(listener) } + } + + override val isFactoryResetProtectionActive: Flow<Boolean> = conflatedCallbackFlow { + val listener = + object : DeviceProvisionedController.DeviceProvisionedListener { + override fun onFrpActiveChanged() { + trySend(deviceProvisionedController.isFrpActive) + } + } + deviceProvisionedController.addCallback(listener) + trySend(deviceProvisionedController.isFrpActive) + awaitClose { deviceProvisionedController.removeCallback(listener) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt index ce9d6fd752c5..dbc3bf3a75a2 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt @@ -325,6 +325,7 @@ constructor( addAction(Intent.ACTION_USER_SWITCHED) addAction(Intent.ACTION_USER_STOPPED) addAction(Intent.ACTION_USER_UNLOCKED) + addAction(Intent.ACTION_LOCALE_CHANGED) }, user = UserHandle.SYSTEM, map = { intent, _ -> intent }, @@ -615,6 +616,7 @@ constructor( ) { val shouldRefreshAllUsers = when (intent.action) { + Intent.ACTION_LOCALE_CHANGED -> true Intent.ACTION_USER_SWITCHED -> { dismissDialog() val selectedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1) @@ -644,6 +646,11 @@ constructor( } private fun restartSecondaryService(@UserIdInt userId: Int) { + // Do not start service for user that is marked for deletion. + if (!manager.aliveUsers.map { it.id }.contains(userId)) { + return + } + val intent = Intent(applicationContext, SystemUISecondaryUserService::class.java) // Disconnect from the old secondary user's service val secondaryUserId = repository.secondaryUserId @@ -657,6 +664,7 @@ constructor( // Connect to the new secondary user's service (purely to ensure that a persistent // SystemUI application is created for that user) + if (userId != Process.myUserHandle().identifier) { applicationContext.startServiceAsUser( intent, diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt index d3f83b1e18bd..20f0fa8cf46b 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt @@ -68,6 +68,7 @@ constructor( private val hasCancelButtonBeenClicked = MutableStateFlow(false) private val isFinishRequiredDueToExecutedAction = MutableStateFlow(false) + private val userSwitched = MutableStateFlow(false) /** * Whether the observer should finish the experience. Once consumed, [onFinished] must be called @@ -89,6 +90,7 @@ constructor( fun onFinished() { hasCancelButtonBeenClicked.value = false isFinishRequiredDueToExecutedAction.value = false + userSwitched.value = false } /** Notifies that the user has clicked the "open menu" button. */ @@ -121,8 +123,9 @@ constructor( hasCancelButtonBeenClicked, // If an executed action told us to finish, we should finish, isFinishRequiredDueToExecutedAction, - ) { cancelButtonClicked, executedActionFinish -> - cancelButtonClicked || executedActionFinish + userSwitched, + ) { cancelButtonClicked, executedActionFinish, userSwitched -> + cancelButtonClicked || executedActionFinish || userSwitched } private fun toViewModel( @@ -191,7 +194,10 @@ constructor( return if (!model.isSelectable) { null } else { - { userInteractor.selectUser(model.id) } + { + userInteractor.selectUser(model.id) + userSwitched.value = true + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt new file mode 100644 index 000000000000..ac04d31041b6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt @@ -0,0 +1,50 @@ +package com.android.systemui.util + +import java.lang.ref.SoftReference +import java.lang.ref.WeakReference +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/** + * Creates a Kotlin idiomatic weak reference. + * + * Usage: + * ``` + * var weakReferenceObj: Object? by weakReference(null) + * weakReferenceObj = Object() + * ``` + */ +fun <T> weakReference(obj: T? = null): ReadWriteProperty<Any?, T?> { + return object : ReadWriteProperty<Any?, T?> { + var weakRef = WeakReference<T?>(obj) + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return weakRef.get() + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + weakRef = WeakReference(value) + } + } +} + +/** + * Creates a Kotlin idiomatic soft reference. + * + * Usage: + * ``` + * var softReferenceObj: Object? by softReference(null) + * softReferenceObj = Object() + * ``` + */ +fun <T> softReference(obj: T? = null): ReadWriteProperty<Any?, T?> { + return object : ReadWriteProperty<Any?, T?> { + var softRef = SoftReference<T?>(obj) + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return softRef.get() + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + softRef = SoftReference(value) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt index 47d505ea36d5..83ff78980880 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt @@ -18,19 +18,24 @@ package com.android.systemui.util.kotlin import com.android.systemui.util.time.SystemClock import com.android.systemui.util.time.SystemClockImpl -import kotlinx.coroutines.CoroutineStart import java.util.concurrent.atomic.AtomicReference +import kotlin.math.max +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlin.math.max /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. @@ -44,8 +49,7 @@ fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> var previousValue: Any? = noVal collect { newVal -> if (previousValue != noVal) { - @Suppress("UNCHECKED_CAST") - emit(transform(previousValue as T, newVal)) + @Suppress("UNCHECKED_CAST") emit(transform(previousValue as T, newVal)) } previousValue = newVal } @@ -60,13 +64,11 @@ fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> fun <T, R> Flow<T>.pairwiseBy( initialValue: T, transform: suspend (previousValue: T, newValue: T) -> R, -): Flow<R> = - onStart { emit(initialValue) }.pairwiseBy(transform) +): Flow<R> = onStart { emit(initialValue) }.pairwiseBy(transform) /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. * - * * The output of [getInitialValue] will be used as the "old" value for the first emission. As * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before * returning the initial value. @@ -76,8 +78,7 @@ fun <T, R> Flow<T>.pairwiseBy( fun <T, R> Flow<T>.pairwiseBy( getInitialValue: suspend () -> T, transform: suspend (previousValue: T, newValue: T) -> R, -): Flow<R> = - onStart { emit(getInitialValue()) }.pairwiseBy(transform) +): Flow<R> = onStart { emit(getInitialValue()) }.pairwiseBy(transform) /** * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new @@ -88,8 +89,8 @@ fun <T, R> Flow<T>.pairwiseBy( fun <T> Flow<T>.pairwise(): Flow<WithPrev<T>> = pairwiseBy(::WithPrev) /** - * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue] - * will be used as the "old" value for the first emission. + * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue] will + * be used as the "old" value for the first emission. * * Useful for code that needs to compare the current value to the previous value. */ @@ -102,9 +103,9 @@ data class WithPrev<T>(val previousValue: T, val newValue: T) * Returns a new [Flow] that combines the [Set] changes between each emission from [this] using * [transform]. * - * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause - * a change event to be emitted that contains no removals, and all elements from that first [Set] - * as additions. + * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a + * change event to be emitted that contains no removals, and all elements from that first [Set] as + * additions. * * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted * until a second [Set] has been emitted from the upstream [Flow]. @@ -112,22 +113,23 @@ data class WithPrev<T>(val previousValue: T, val newValue: T) fun <T, R> Flow<Set<T>>.setChangesBy( transform: suspend (removed: Set<T>, added: Set<T>) -> R, emitFirstEvent: Boolean = true, -): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this) - .distinctUntilChanged() - .pairwiseBy { old: Set<T>, new: Set<T> -> - // If an element was present in the old set, but not the new one, then it was removed - val removed = old - new - // If an element is present in the new set, but on the old one, then it was added - val added = new - old - transform(removed, added) - } +): Flow<R> = + (if (emitFirstEvent) onStart { emit(emptySet()) } else this) + .distinctUntilChanged() + .pairwiseBy { old: Set<T>, new: Set<T> -> + // If an element was present in the old set, but not the new one, then it was removed + val removed = old - new + // If an element is present in the new set, but on the old one, then it was added + val added = new - old + transform(removed, added) + } /** * Returns a new [Flow] that produces the [Set] changes between each emission from [this]. * - * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause - * a change event to be emitted that contains no removals, and all elements from that first [Set] - * as additions. + * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a + * change event to be emitted that contains no removals, and all elements from that first [Set] as + * additions. * * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted * until a second [Set] has been emitted from the upstream [Flow]. @@ -153,14 +155,11 @@ fun <A, B, C> Flow<A>.sample(other: Flow<B>, transform: suspend (A, B) -> C): Fl coroutineScope { val noVal = Any() val sampledRef = AtomicReference(noVal) - val job = launch(Dispatchers.Unconfined) { - other.collect { sampledRef.set(it) } - } + val job = launch(Dispatchers.Unconfined) { other.collect { sampledRef.set(it) } } collect { val sampled = sampledRef.get() if (sampled != noVal) { - @Suppress("UNCHECKED_CAST") - emit(transform(it, sampled as B)) + @Suppress("UNCHECKED_CAST") emit(transform(it, sampled as B)) } } job.cancel() @@ -181,7 +180,6 @@ fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a } * latest value is emitted. * * Example: - * * ```kotlin * flow { * emit(1) // t=0ms @@ -210,7 +208,6 @@ fun <T> Flow<T>.throttle(periodMs: Long): Flow<T> = this.throttle(periodMs, Syst * during this period, only The latest value is emitted. * * Example: - * * ```kotlin * flow { * emit(1) // t=0ms @@ -248,10 +245,11 @@ fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelF // We create delayJob to allow cancellation during the delay period delayJob = launch { delay(timeUntilNextEmit) - sendJob = outerScope.launch(start = CoroutineStart.UNDISPATCHED) { - send(it) - previousEmitTimeMs = clock.elapsedRealtime() - } + sendJob = + outerScope.launch(start = CoroutineStart.UNDISPATCHED) { + send(it) + previousEmitTimeMs = clock.elapsedRealtime() + } } } else { send(it) @@ -259,4 +257,15 @@ fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelF } } } -}
\ No newline at end of file +} + +/** + * Returns a [StateFlow] launched in the surrounding [CoroutineScope]. This [StateFlow] gets its + * value by invoking [getValue] whenever an event is emitted from [changedSignals]. It will also + * immediately invoke [getValue] to establish its initial value. + */ +inline fun <T> CoroutineScope.stateFlow( + changedSignals: Flow<Unit>, + crossinline getValue: () -> T, +): StateFlow<T> = + changedSignals.map { getValue() }.stateIn(this, SharingStarted.Eagerly, getValue()) diff --git a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt new file mode 100644 index 000000000000..51d2afabd7f9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.util.ui + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.transformLatest + +/** + * A state comprised of a [value] of type [T] paired with a boolean indicating whether or not the + * [value] [isAnimating] in the UI. + */ +data class AnimatedValue<T>( + val value: T, + val isAnimating: Boolean, +) + +/** + * An event comprised of a [value] of type [T] paired with a [boolean][startAnimating] indicating + * whether or not this event should start an animation. + */ +data class AnimatableEvent<T>( + val value: T, + val startAnimating: Boolean, +) + +/** + * Returns a [Flow] that tracks an [AnimatedValue] state. The input [Flow] is used to update the + * [AnimatedValue.value], as well as [AnimatedValue.isAnimating] if the event's + * [AnimatableEvent.startAnimating] value is `true`. When [completionEvents] emits a value, the + * [AnimatedValue.isAnimating] will flip to `false`. + */ +fun <T> Flow<AnimatableEvent<T>>.toAnimatedValueFlow( + completionEvents: Flow<Any?>, +): Flow<AnimatedValue<T>> = transformLatest { (value, startAnimating) -> + emit(AnimatedValue(value, isAnimating = startAnimating)) + if (startAnimating) { + // Wait for a completion now that we've started animating + completionEvents + .map { Unit } // replace the event so that it's never `null` + .firstOrNull() // `null` indicates an empty flow + // emit the new state if the flow was not empty. + ?.run { emit(AnimatedValue(value, isAnimating = false)) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index 750b6f95d52e..2132904caa84 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -40,12 +40,13 @@ import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; -import com.android.systemui.res.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -68,6 +69,7 @@ public class WalletActivity extends ComponentActivity implements private final Executor mExecutor; private final Handler mHandler; private final FalsingManager mFalsingManager; + private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; private FalsingCollector mFalsingCollector; private final UserTracker mUserTracker; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -91,7 +93,8 @@ public class WalletActivity extends ComponentActivity implements UserTracker userTracker, KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarKeyguardViewManager keyguardViewManager, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) { mKeyguardStateController = keyguardStateController; mKeyguardDismissUtil = keyguardDismissUtil; mActivityStarter = activityStarter; @@ -103,6 +106,7 @@ public class WalletActivity extends ComponentActivity implements mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardViewManager = keyguardViewManager; mUiEventLogger = uiEventLogger; + mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; } @Override @@ -209,6 +213,7 @@ public class WalletActivity extends ComponentActivity implements true, Utils.getColorAttrDefaultColor( this, com.android.internal.R.attr.colorAccentPrimary)); + mKeyguardFaceAuthInteractor.onWalletLaunched(); mKeyguardViewManager.requestFace(true); } diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt index 8990583cf9de..167e3417c162 100644 --- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt +++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt @@ -18,61 +18,94 @@ package com.android import android.app.ActivityManager import android.app.admin.DevicePolicyManager import android.os.UserManager +import android.util.DisplayMetrics +import com.android.internal.logging.MetricsLogger import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.demomode.DemoModeController +import com.android.systemui.dump.DumpManager +import com.android.systemui.keyguard.ScreenLifecycle +import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.BroadcastDispatcherLog import com.android.systemui.log.dagger.SceneFrameworkLog +import com.android.systemui.media.controls.ui.MediaHierarchyManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationListener +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.NotificationShadeDepthController +import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.stack.AmbientState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.SplitShadeStateController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.util.mockito.mock import com.android.wm.shell.bubbles.Bubbles +import dagger.Binds import dagger.Module import dagger.Provides import java.util.Optional -@Module +@Module(includes = [TestMocksModule.Bindings::class]) data class TestMocksModule( @get:Provides val activityStarter: ActivityStarter = mock(), + @get:Provides val ambientState: AmbientState = mock(), @get:Provides val bubbles: Optional<Bubbles> = Optional.of(mock()), - @get:Provides val configurationController: ConfigurationController = mock(), @get:Provides val darkIconDispatcher: DarkIconDispatcher = mock(), @get:Provides val demoModeController: DemoModeController = mock(), @get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(), @get:Provides val dozeParameters: DozeParameters = mock(), + @get:Provides val dumpManager: DumpManager = mock(), @get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(), @get:Provides val keyguardBypassController: KeyguardBypassController = mock(), @get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(), @get:Provides val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(), - @get:Provides val notifListener: NotificationListener = mock(), - @get:Provides val notifMediaManager: NotificationMediaManager = mock(), - @get:Provides val screenOffAnimController: ScreenOffAnimationController = mock(), - @get:Provides val splitShadeStateController: SplitShadeStateController = mock(), - @get:Provides val statusBarStateController: StatusBarStateController = mock(), + @get:Provides val mediaHierarchyManager: MediaHierarchyManager = mock(), + @get:Provides val notificationListener: NotificationListener = mock(), + @get:Provides val notificationLockscreenUserManager: NotificationLockscreenUserManager = mock(), + @get:Provides val notificationMediaManager: NotificationMediaManager = mock(), + @get:Provides val notificationShadeDepthController: NotificationShadeDepthController = mock(), + @get:Provides + val notificationStackScrollLayoutController: NotificationStackScrollLayoutController = mock(), + @get:Provides val notificationStackSizeCalculator: NotificationStackSizeCalculator = mock(), + @get:Provides val notificationWakeUpCoordinator: NotificationWakeUpCoordinator = mock(), + @get:Provides val screenLifecycle: ScreenLifecycle = mock(), + @get:Provides val screenOffAnimationController: ScreenOffAnimationController = mock(), + @get:Provides val scrimController: ScrimController = mock(), + @get:Provides val statusBarStateController: SysuiStatusBarStateController = mock(), @get:Provides val statusBarWindowController: StatusBarWindowController = mock(), - @get:Provides val wakeUpCoordinator: NotificationWakeUpCoordinator = mock(), + @get:Provides val wakefulnessLifecycle: WakefulnessLifecycle = mock(), // log buffers @get:[Provides BroadcastDispatcherLog] val broadcastDispatcherLogger: LogBuffer = mock(), @get:[Provides SceneFrameworkLog] val sceneLogger: LogBuffer = mock(), + @get:Provides val lsShadeTransitionLogger: LSShadeTransitionLogger = mock(), // framework mocks @get:Provides val activityManager: ActivityManager = mock(), @get:Provides val devicePolicyManager: DevicePolicyManager = mock(), + @get:Provides val displayMetrics: DisplayMetrics = mock(), + @get:Provides val metricsLogger: MetricsLogger = mock(), @get:Provides val userManager: UserManager = mock(), -) +) { + @Module + interface Bindings { + @Binds + fun bindStatusBarStateController( + sysuiStatusBarStateController: SysuiStatusBarStateController, + ): StatusBarStateController + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt index f943acd9180e..d8a2c5f76fce 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags @@ -47,7 +46,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardPasswordViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt index e09009389f8b..dc1618d128b3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake @@ -53,7 +52,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardPatternViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java index 8322b37151de..4a24e4a1f40f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java @@ -31,14 +31,13 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.SingleTapClassifier; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.res.R; import org.junit.Before; import org.junit.Test; @@ -47,7 +46,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 2f08804d8c05..9df4dd4df4d2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -26,7 +26,6 @@ import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityModel.SecurityMode import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake @@ -53,7 +52,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardPinViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index f3a1b68a87ae..5102957c71ed 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -69,7 +69,8 @@ public class KeyguardPresentationTest extends SysuiTestCase { when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView); - when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class))) + when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class), + any(Display.class))) .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) .thenReturn(mKeyguardClockSwitchController); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index d54843d39d21..62f9a9dcce70 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -36,7 +36,6 @@ import com.android.internal.logging.UiEventLogger import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback import com.android.keyguard.KeyguardSecurityModel.SecurityMode -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate import com.android.systemui.biometrics.SideFpsController @@ -100,7 +99,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @RunWithLooper class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @@ -812,6 +810,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -827,7 +826,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -844,7 +844,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -862,7 +863,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -878,6 +880,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Lockscreen, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") @@ -895,6 +898,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index aad11d90f5a4..156e06843d15 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -56,11 +56,10 @@ import androidx.constraintlayout.widget.ConstraintSet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.data.source.UserRecord; import com.android.systemui.util.settings.GlobalSettings; @@ -76,7 +75,6 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper() public class KeyguardSecurityContainerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java index b02b1f9df9fb..7bb6ef1c8895 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java @@ -35,10 +35,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.res.R; import org.junit.Before; import org.junit.Rule; @@ -50,7 +49,6 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper() public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index d0bb5a999327..4290b8b28687 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags @@ -44,7 +43,6 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardSimPinViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt index 59cd26cc6b9b..31ee6411f93b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt @@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags @@ -40,7 +39,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class KeyguardSimPukViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index e4e2b0a8d89f..9a908d778943 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.AndroidTestingRunner; 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 class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll public void updatePosition_primaryClockAnimation() { ClockController mockClock = mock(ClockController.class); when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); - when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", false, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true)); mController.updatePosition(10, 15, 20f, true); @@ -96,7 +96,7 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll public void updatePosition_alternateClockAnimation() { ClockController mockClock = mock(ClockController.class); when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); - when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", true, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true)); mController.updatePosition(10, 15, 20f, true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index 21a28222ccf4..d61ca697642a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -39,10 +39,10 @@ import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.doze.util.BurnInHelperKt; import com.android.systemui.dump.DumpManager; @@ -51,6 +51,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; +import com.android.systemui.scene.SceneTestUtils; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -75,6 +77,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected MockitoSession mStaticMockSession; + protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this); + protected @Mock BouncerInteractor mBouncerInteractor; protected @Mock LockIconView mLockIconView; protected @Mock AnimatedStateListDrawable mIconDrawable; protected @Mock Context mContext; @@ -93,6 +97,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected @Mock AuthRippleController mAuthRippleController; protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); protected FakeFeatureFlags mFeatureFlags; + protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; protected LockIconViewController mUnderTest; @@ -148,6 +153,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mFeatureFlags.set(MIGRATE_LOCK_ICON, false); mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false); + mUnderTest = new LockIconViewController( mStatusBarStateController, mKeyguardUpdateMonitor, @@ -168,7 +174,9 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(), mFeatureFlags, mPrimaryBouncerInteractor, - mContext + mContext, + () -> mBouncerInteractor, + mSceneTestUtils.getSceneContainerFlags() ); } @@ -227,8 +235,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { setupLockIconViewMocks(); } - protected void init(boolean useMigrationFlag) { - mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag); + protected void init(boolean useDozeMigrationFlag) { + mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag); mUnderTest.setLockIconView(mLockIconView); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java index 7b920939263e..4bacc3dfca0d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java @@ -26,6 +26,7 @@ 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; @@ -404,6 +405,49 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { // THEN uses perform haptic feedback verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); + } + + @Test + public void longPress_showBouncer_sceneContainerNotEnabled() { + init(/* useMigrationFlag= */ false); + mSceneTestUtils.getSceneContainerFlags().setEnabled(false); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); + + // WHEN longPress + mUnderTest.onLongPress(); + + // THEN show primary bouncer via keyguard view controller, not scene container + verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean()); + verify(mBouncerInteractor, never()).showOrUnlockDevice(any()); + } + + @Test + public void longPress_showBouncer() { + init(/* useMigrationFlag= */ false); + mSceneTestUtils.getSceneContainerFlags().setEnabled(true); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); + + // WHEN longPress + mUnderTest.onLongPress(); + + // THEN show primary bouncer + verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); + verify(mBouncerInteractor).showOrUnlockDevice(any()); + } + + @Test + public void longPress_falsingTriggered_doesNotShowBouncer() { + init(/* useMigrationFlag= */ false); + mSceneTestUtils.getSceneContainerFlags().setEnabled(true); + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true); + + // WHEN longPress + mUnderTest.onLongPress(); + // THEN don't show primary bouncer + verify(mBouncerInteractor, never()).showOrUnlockDevice(any()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt index 59c7e7669b63..8faf715521f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt @@ -166,6 +166,9 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { waitForIdleSync() verify(controller).onLaunchAnimationCancelled() verify(controller, never()).onLaunchAnimationStart(anyBoolean()) + verify(listener).onLaunchAnimationCancelled() + verify(listener, never()).onLaunchAnimationStart() + assertNull(runner.delegate) } @Test @@ -176,6 +179,9 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { waitForIdleSync() verify(controller).onLaunchAnimationCancelled() verify(controller, never()).onLaunchAnimationStart(anyBoolean()) + verify(listener).onLaunchAnimationCancelled() + verify(listener, never()).onLaunchAnimationStart() + assertNull(runner.delegate) } @Test @@ -194,6 +200,15 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { } } + @Test + fun disposeRunner_delegateDereferenced() { + val runner = activityLaunchAnimator.createRunner(controller) + assertNotNull(runner.delegate) + runner.dispose() + waitForIdleSync() + assertNull(runner.delegate) + } + private fun fakeWindow(): RemoteAnimationTarget { val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */) val taskInfo = ActivityManager.RunningTaskInfo() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index 212dad78d5b2..c2e6db362035 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -242,6 +242,40 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test + fun animatesRootOnly() { + setUpRootWithChildren() + + val success = ViewHierarchyAnimator.animate( + rootView, + animateChildren = false + ) + // Change all bounds. + rootView.measure( + View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) + ) + rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */) + + assertTrue(success) + assertNotNull(rootView.getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) + // The initial values for the root view should be those of the previous layout, while the + // children views should be at the final values from the beginning. + checkBounds(rootView, l = 0, t = 0, r = 200, b = 100) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100) + checkBounds(rootView.getChildAt(1), l = 90, t = 0, r = 180, b = 100) + endAnimation(rootView) + assertNull(rootView.getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) + // The end values should be those of the latest layout. + checkBounds(rootView, l = 10, t = 20, r = 200, b = 120) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100) + checkBounds(rootView.getChildAt(1), l = 90, t = 0, r = 180, b = 100) + } + + @Test fun animatesInvisibleViews() { rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) rootView.visibility = View.INVISIBLE diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt index 0283382d02be..6ac84bc503a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt @@ -21,6 +21,7 @@ package com.android.systemui.authentication.data.repository import android.app.admin.DevicePolicyManager import android.content.Intent import android.content.pm.UserInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityModel @@ -40,13 +41,12 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class AuthenticationRepositoryTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index a102890db7b0..2f5f4606c5f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.authentication.domain.interactor import android.app.admin.DevicePolicyManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel @@ -35,11 +36,10 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class AuthenticationInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 2bc0171939b4..885abcb72f1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -17,12 +17,15 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -79,7 +82,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; @@ -114,7 +116,6 @@ import java.util.Random; @RunWith(AndroidJUnit4.class) @RunWithLooper @SmallTest -@RoboPilotTest public class AuthControllerTest extends SysuiTestCase { private static final long REQUEST_ID = 22; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt index 73654609ad7f..d68a3131da4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt @@ -16,36 +16,18 @@ package com.android.systemui.biometrics -import android.app.ActivityManager -import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.internal.logging.UiEventLogger -import com.android.keyguard.KeyguardUpdateMonitor +import com.android.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.telephony.data.repository.FakeTelephonyRepository -import com.android.systemui.telephony.domain.interactor.TelephonyInteractor -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.interactor.GuestUserInteractor -import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode -import com.android.systemui.user.domain.interactor.RefreshUsersScheduler -import com.android.systemui.user.domain.interactor.UserInteractor -import com.android.systemui.util.mockito.mock +import com.android.systemui.user.domain.UserDomainLayerModule +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -58,91 +40,27 @@ import org.mockito.MockitoAnnotations @SmallTest @OptIn(ExperimentalCoroutinesApi::class) class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { - private val utils = SceneTestUtils(this) - private val testScope = utils.testScope - private val testDispatcher = utils.testDispatcher - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val featureFlags = FakeFeatureFlags() - private val keyguardRepository = FakeKeyguardRepository() - private val shadeRepository = FakeShadeRepository() - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - private val userSetupRepository = FakeUserSetupRepository() - private val userRepository = FakeUserRepository() - private val configurationRepository = FakeConfigurationRepository() - private val sharedNotificationContainerInteractor = - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - - private lateinit var detector: AuthDialogPanelInteractionDetector - private lateinit var shadeInteractor: ShadeInteractor - private lateinit var userInteractor: UserInteractor + + private val testComponent: TestComponent = + DaggerAuthDialogPanelInteractionDetectorTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FACE_AUTH_REFACTOR, false) + set(Flags.FULL_SCREEN_USER_SWITCHER, true) + }, + ) @Mock private lateinit var action: Runnable - @Mock private lateinit var activityManager: ActivityManager - @Mock private lateinit var activityStarter: ActivityStarter - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - @Mock private lateinit var guestInteractor: GuestUserInteractor - @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var manager: UserManager - @Mock private lateinit var uiEventLogger: UiEventLogger + + private val testScope = testComponent.testScope + private val shadeRepository = testComponent.shadeRepository + private val detector = testComponent.detector @Before fun setUp() { MockitoAnnotations.initMocks(this) - - featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) - featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) - - val refreshUsersScheduler = - RefreshUsersScheduler( - applicationScope = testScope.backgroundScope, - mainDispatcher = testDispatcher, - repository = userRepository, - ) - userInteractor = - UserInteractor( - applicationContext = context, - repository = userRepository, - activityStarter = activityStarter, - keyguardInteractor = - KeyguardInteractorFactory.create(featureFlags = featureFlags) - .keyguardInteractor, - featureFlags = featureFlags, - manager = manager, - headlessSystemUserMode = headlessSystemUserMode, - applicationScope = testScope.backgroundScope, - telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), - broadcastDispatcher = fakeBroadcastDispatcher, - keyguardUpdateMonitor = keyguardUpdateMonitor, - backgroundDispatcher = testDispatcher, - activityManager = activityManager, - refreshUsersScheduler = refreshUsersScheduler, - guestUserInteractor = guestInteractor, - uiEventLogger = uiEventLogger, - userRestrictionChecker = mock(), - ) - shadeInteractor = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository, - deviceProvisionedController, - userInteractor, - sharedNotificationContainerInteractor, - shadeRepository, - ) - detector = AuthDialogPanelInteractionDetector(testScope, { shadeInteractor }) } @Test @@ -215,4 +133,27 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { // Clean up job detector.disable() } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val detector: AuthDialogPanelInteractionDetector + val shadeRepository: FakeShadeRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + ): TestComponent + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java index 8547fa33c9de..714461b715d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java @@ -38,7 +38,6 @@ import android.view.Surface; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import kotlin.Unit; @@ -53,7 +52,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class BiometricDisplayListenerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt index ab5d8bea585b..39f0d570cb26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.biometrics import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.logging.BiometricMessageDeferralLogger -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import org.junit.Assert.assertEquals @@ -34,7 +33,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class FaceHelpMessageDeferralTest : SysuiTestCase() { val threshold = .75f diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index 57cf834bfa38..ef06e0efa01d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -53,7 +53,6 @@ import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestableContext import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository @@ -98,7 +97,6 @@ private const val SENSOR_ID = 1 private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3 @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class SideFpsControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt index 469f65a6cae5..e2aa984de46b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.biometrics import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager @@ -35,7 +34,6 @@ import org.mockito.Mock import org.mockito.junit.MockitoJUnit @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class UdfpsBpViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 21e614f3db3a..e9e9624580b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -38,7 +38,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams @@ -83,7 +82,6 @@ private const val SENSOR_HEIGHT = 60 @ExperimentalCoroutinesApi @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class UdfpsControllerOverlayTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index ee3bd0d79ad8..a36f4e9ac217 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -20,12 +20,15 @@ import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPR import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; + import static com.android.internal.util.FunctionalUtils.ThrowingConsumer; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; @@ -74,8 +77,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; @@ -94,6 +95,7 @@ import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -125,7 +127,6 @@ import java.util.Optional; import javax.inject.Provider; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class UdfpsControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java index 280bfdf1aa8c..cd9189bef7f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java @@ -27,7 +27,6 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import org.junit.Test; @@ -38,7 +37,6 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest -@RoboPilotTest public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase { @Test public void testUdfpsBottomSpacerHeightForPortrait() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java index 1afb223b4089..5239966f1923 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java @@ -30,7 +30,6 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.concurrency.FakeExecution; @@ -41,7 +40,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class UdfpsDisplayModeTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java index 8508f45b02f3..b018a3e2ca6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java @@ -31,14 +31,12 @@ import android.view.MotionEvent; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.statusbar.StatusBarState; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index 17f435b75d5a..da4548bc14c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -21,7 +21,6 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel -import com.android.systemui.RoboPilotTest import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor @@ -58,7 +57,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) @SmallTest -@RoboPilotTest @TestableLooper.RunWithLooper @kotlinx.coroutines.ExperimentalCoroutinesApi class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt index 6d552544fee7..8b374ae54127 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt @@ -21,7 +21,6 @@ import android.testing.TestableLooper import android.view.MotionEvent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.UdfpsController.UdfpsOverlayController import com.android.systemui.statusbar.commandline.CommandRegistry @@ -40,7 +39,6 @@ import org.mockito.Mockito.`when` as whenEver import org.mockito.junit.MockitoJUnit @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class UdfpsShellTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt index ebadfc78a079..0c8e7a5d356a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt @@ -26,7 +26,6 @@ import android.view.Surface import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.util.mockito.any @@ -50,7 +49,6 @@ private const val SENSOR_Y = 250 private const val SENSOR_RADIUS = 10 @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class UdfpsViewTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index 0d172706b127..2d8adca04a5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.bouncer.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AlternateBouncerInteractorTest : SysuiTestCase() { private lateinit var underTest: AlternateBouncerInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index a9ba36a9fde4..915661b8a776 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -38,11 +39,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BouncerInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt index a81ca86f338d..4aea4f329858 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.bouncer.domain.interactor import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import org.junit.Before import org.junit.Test @@ -29,7 +28,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() { private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt index cb0b74f3015d..2018e614ba00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.ui.BouncerView @@ -43,7 +42,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { private lateinit var repository: FakeKeyguardBouncerRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index b5177e1587ee..8e1f5ac58b68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -29,10 +30,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class AuthMethodBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index b75355a82406..2c97809bf367 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel @@ -35,11 +36,10 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt index 333bd214fe9a..802f8e6043fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -50,7 +49,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardBouncerViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 0926399a8617..ba8dcef683c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -35,11 +36,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PasswordBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 2e7c9aaa67e8..bfaa6edefdca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -38,11 +39,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PatternBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 255bbe3ae231..7873899f2d35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -37,11 +38,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PinBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -92,7 +92,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onShown() - assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(ENTER_YOUR_PIN) assertThat(pin).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(underTest.authenticationMethod) @@ -209,7 +209,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() assertThat(pin).isEmpty() - assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -227,7 +227,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onPinButtonClicked(4) underTest.onPinButtonClicked(5) // PIN is now wrong! underTest.onAuthenticateButtonClicked() - assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) assertThat(pin).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -273,7 +273,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { ) // PIN is now wrong! assertThat(pin).isEmpty() - assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt index 4c279ea08fd7..55016bb1fc07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.bouncer.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.ui.viewmodel.EntryToken.ClearAll @@ -16,14 +17,13 @@ import java.lang.Character.isDigit import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 /** * This test uses a mnemonic code to create and verify PinInput instances: strings of digits [0-9] * for [Digit] tokens, as well as a `C` for the [ClearAll] token. */ @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PinInputViewModelTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt index 7fa828fbfadd..3df9cbb29e4a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt @@ -9,7 +9,6 @@ import android.os.UserHandle import android.os.UserManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.coroutines.collectLastValue @@ -40,7 +39,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CommunalWidgetRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var appWidgetManager: AppWidgetManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index ddf788e47bf2..cdc42e096830 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.communal.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.FakeCommunalRepository import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository @@ -37,7 +36,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class CommunalInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt index a10eb295476e..41a8be9663b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt @@ -5,7 +5,6 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection import org.junit.Before @@ -15,7 +14,6 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt index 8e8cbe4a7cf2..2c80035873f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt @@ -1,6 +1,7 @@ package com.android.systemui.deviceentry.data.repository import android.content.pm.UserInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase @@ -11,6 +12,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking @@ -19,14 +21,14 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DeviceEntryRepositoryTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils @@ -54,6 +56,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { keyguardBypassController = keyguardBypassController, keyguardStateController = keyguardStateController, ) + testScope.runCurrent() } @Test @@ -66,8 +69,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { assertThat(isUnlocked).isFalse() val captor = argumentCaptor<KeyguardStateController.Callback>() - Mockito.verify(keyguardStateController, Mockito.atLeastOnce()) - .addCallback(captor.capture()) + verify(keyguardStateController, Mockito.atLeastOnce()).addCallback(captor.capture()) whenever(keyguardStateController.isUnlocked).thenReturn(true) captor.value.onUnlockedChanged() @@ -98,7 +100,12 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { testScope.runTest { whenever(keyguardBypassController.isBypassEnabled).thenAnswer { false } whenever(keyguardBypassController.bypassEnabled).thenAnswer { false } - assertThat(underTest.isBypassEnabled()).isFalse() + withArgCaptor { + verify(keyguardBypassController).registerOnBypassStateChangedListener(capture()) + } + .onBypassStateChanged(false) + runCurrent() + assertThat(underTest.isBypassEnabled.value).isFalse() } @Test @@ -106,7 +113,12 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { testScope.runTest { whenever(keyguardBypassController.isBypassEnabled).thenAnswer { true } whenever(keyguardBypassController.bypassEnabled).thenAnswer { true } - assertThat(underTest.isBypassEnabled()).isTrue() + withArgCaptor { + verify(keyguardBypassController).registerOnBypassStateChangedListener(capture()) + } + .onBypassStateChanged(true) + runCurrent() + assertThat(underTest.isBypassEnabled.value).isTrue() } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt index 55582e123969..c13fde75a068 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.deviceentry.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -14,11 +15,10 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DeviceEntryInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -218,14 +218,14 @@ class DeviceEntryInteractorTest : SysuiTestCase() { fun isBypassEnabled_enabledInRepository_true() = testScope.runTest { utils.deviceEntryRepository.setBypassEnabled(true) - assertThat(underTest.isBypassEnabled()).isTrue() + assertThat(underTest.isBypassEnabled.value).isTrue() } @Test fun isBypassEnabled_disabledInRepository_false() = testScope.runTest { utils.deviceEntryRepository.setBypassEnabled(false) - assertThat(underTest.isBypassEnabled()).isFalse() + assertThat(underTest.isBypassEnabled.value).isFalse() } private fun switchToScene(sceneKey: SceneKey) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt index 781ad6b10b70..8a35ef11a364 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt @@ -6,7 +6,6 @@ import android.animation.ValueAnimator import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel @@ -30,7 +29,6 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class DreamOverlayAnimationsControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt index 21192fa11da3..2c6c793c97f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.dreams import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -30,7 +29,6 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class DreamOverlayCallbackControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 7c36642f5417..2af6566e993a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -38,7 +38,6 @@ import androidx.test.filters.SmallTest; import com.android.dream.lowlight.LowLightTransitionCoordinator; import com.android.keyguard.BouncerPanelExpansionCalculator; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; @@ -53,7 +52,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java index be7638ef31e6..d379dc6f3dc1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java @@ -26,7 +26,6 @@ import android.service.notification.StatusBarNotification; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; @@ -38,7 +37,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 8379f73402cf..e5f997257cfa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -49,7 +49,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.complication.ComplicationLayoutEngine; import com.android.systemui.dreams.complication.HideComplicationTouchHandler; @@ -72,7 +71,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayServiceTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java index 2ef227c245c3..365f67b5e566 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java @@ -29,7 +29,6 @@ import static org.mockito.Mockito.when; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.complication.Complication; import com.android.systemui.flags.FeatureFlags; @@ -48,7 +47,6 @@ import org.mockito.MockitoAnnotations; import java.util.Collection; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayStateControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java index 12cb332c7cc5..7ff345cdcf7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.verify; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -36,7 +35,6 @@ import org.mockito.MockitoAnnotations; import java.util.List; import java.util.concurrent.Executor; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayStatusBarItemsProviderTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index 9566e81ed08d..39db2beb4c44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -46,11 +46,10 @@ import android.view.View; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.FakeLogBuffer; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.NextAlarmController; @@ -72,7 +71,6 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java index d32788df30be..315a24bfd945 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java @@ -30,7 +30,6 @@ import androidx.concurrent.futures.CallbackToFutureAdapter; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.complication.Complication; import com.android.systemui.dreams.DreamOverlayStateController; @@ -49,7 +48,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class HideComplicationTouchHandlerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java index 4a7700fe1ca2..e0c6ab20c6e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations; import kotlinx.coroutines.CoroutineScope; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class AssistantAttentionConditionTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java index cd2efded7e85..480754c17616 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java @@ -31,7 +31,6 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.shared.condition.Condition; @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations; import kotlinx.coroutines.CoroutineScope; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class DreamConditionTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index ffcaeee52596..3d1efa59a11b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams.touch; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; @@ -40,7 +41,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dreams.touch.scrim.ScrimController; @@ -64,7 +64,6 @@ import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.Optional; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java index ff6d97d3b380..6aa821f15ab1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java @@ -27,7 +27,6 @@ import android.view.MotionEvent; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shared.system.InputChannelCompat; @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class ShadeTouchHandlerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java index da393818c6f7..017fdbe8ac01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java @@ -26,7 +26,6 @@ import android.os.PowerManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.util.concurrency.FakeExecutor; @@ -38,7 +37,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class BouncerlessScrimControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java index 81f6fe3e24d3..4ee4a60fbeaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.when; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -38,7 +37,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4.class) public class ScrimManagerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 291dda20fdc3..a6468234b9b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -16,7 +16,6 @@ package com.android.systemui.keyguard; -import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION; import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT; @@ -635,29 +634,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { } @Test - public void lockAfterSpecifiedAfterDreamStarted() { - int currentUserId = 99; - int userSpecificTimeout = 5999; - KeyguardUpdateMonitor.setCurrentUser(currentUserId); - - // set mDeviceInteractive to true - mViewMediator.onStartedWakingUp(WAKE_REASON_WAKE_MOTION, false); - mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false); - when(mLockPatternUtils.isSecure(currentUserId)).thenReturn(true); - when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L); - when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT, - KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, currentUserId)).thenReturn(userSpecificTimeout); - mSystemClock.setElapsedRealtime(0L); - ArgumentCaptor<PendingIntent> pendingIntent = ArgumentCaptor.forClass(PendingIntent.class); - - mViewMediator.onDreamingStarted(); - - verify(mAlarmManager).setExactAndAllowWhileIdle(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), - eq(Long.valueOf(userSpecificTimeout)), pendingIntent.capture()); - assertEquals(DELAYED_KEYGUARD_ACTION, pendingIntent.getValue().getIntent().getAction()); - } - - @Test public void testHideSurfaceBehindKeyguardMarksKeyguardNotGoingAway() { mViewMediator.hideSurfaceBehindKeyguard(); @@ -1023,7 +999,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator.setShowingLocked(false); when(mKeyguardStateController.isShowing()).thenReturn(false); - mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false); mViewMediator.onDreamingStarted(); assertFalse(mViewMediator.isShowingAndNotOccluded()); } @@ -1034,7 +1009,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator.setShowingLocked(true); when(mKeyguardStateController.isShowing()).thenReturn(true); - mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, true); mViewMediator.onDreamingStarted(); assertTrue(mViewMediator.isShowingAndNotOccluded()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt index cfee3b8f06ea..e20d3afaca53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt @@ -23,7 +23,6 @@ import android.content.Context import android.content.pm.PackageManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.camera.CameraGestureHelper import com.android.systemui.settings.UserTracker @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CameraQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt index 49168d087958..faf97517ac59 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt @@ -26,7 +26,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.EnableZenModeDialog import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription @@ -61,7 +60,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt index c85c7f678536..548b5646f5d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.data.quickaffordance -import com.android.systemui.RoboPilotTest import com.android.systemui.animation.Expandable import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult import kotlinx.coroutines.flow.Flow @@ -25,7 +24,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.yield /** Fake implementation of a quick affordance data source. */ -@RoboPilotTest class FakeKeyguardQuickAffordanceConfig( override val key: String, private val pickerName: String = key, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt index c3e28ae84805..4ae144c03314 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt @@ -21,7 +21,6 @@ import android.content.Context import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.common.shared.model.Icon import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.statusbar.policy.FlashlightController @@ -42,7 +41,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt index ef56a9892060..7d68cc0a3560 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.data.quickaffordance import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.controls.controller.ControlsController @@ -41,7 +40,6 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt index 4f071bd9904f..1e80fb69c107 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt @@ -23,7 +23,6 @@ import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.FakeUserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots @@ -49,7 +48,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt index bd0b71df3e5c..99a01858471c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt @@ -23,7 +23,6 @@ import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.backup.BackupHelper import com.android.systemui.settings.FakeUserTracker @@ -53,7 +52,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt index 0797d07807b4..a1c9f87ee7bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt @@ -21,7 +21,6 @@ import android.content.pm.UserInfo import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.FakeUserTracker import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt index d8c0341e1979..b15352bfe6ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt @@ -21,7 +21,6 @@ import android.content.Context import android.media.AudioManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker @@ -46,7 +45,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class MuteQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index 9d983b8c8943..521dea34513e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.data.quickaffordance import android.content.Intent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult import com.android.systemui.qrcodescanner.controller.QRCodeScannerController @@ -40,7 +39,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt index 613c4ce4ccef..02db0d7a9a50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt @@ -24,7 +24,6 @@ import android.service.quickaccesswallet.WalletCard import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.Expandable @@ -51,7 +50,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt index 1414bace3090..a9b9c9011636 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt @@ -21,7 +21,6 @@ import android.app.admin.DevicePolicyManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.ActivityIntentHelper -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.camera.CameraIntentsWrapper import com.android.systemui.coroutines.collectLastValue @@ -45,7 +44,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt index 944b059450c9..d8cdf29284ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt @@ -32,7 +32,6 @@ import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.data.repository.FaceSensorInfo @@ -79,7 +78,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) class BiometricSettingsRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 307204da1761..819d08a2086e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -44,7 +44,6 @@ import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PAN import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository @@ -122,7 +121,6 @@ import java.io.StringWriter @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt index def016ad8381..a58bc52bf8e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt @@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.coroutines.collectLastValue @@ -49,7 +48,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() { @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt index 7eb8a26d9ab6..9be5558f5cc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.shared.model.DevicePosture @@ -39,7 +38,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DevicePostureRepositoryTest : SysuiTestCase() { private lateinit var underTest: DevicePostureRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt index 126b841610b4..567e0a9717fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -22,7 +22,6 @@ import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig @@ -56,7 +55,6 @@ import org.mockito.ArgumentMatchers.anyString @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 7983e30c66fb..b9119e10b720 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.common.shared.model.Position @@ -64,7 +63,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardRepositoryImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt index 4942cf84a6fc..799bd5ac5739 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt @@ -20,7 +20,6 @@ import android.graphics.Point import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.coroutines.collectLastValue @@ -46,7 +45,6 @@ import org.junit.runner.RunWith import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class LightRevealScrimRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt index 7f784d88da6d..ee47c58f4002 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt @@ -21,7 +21,6 @@ import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.logging.TrustRepositoryLogger -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue @@ -46,7 +45,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class TrustRepositoryTest : SysuiTestCase() { @Mock private lateinit var trustManager: TrustManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt index 181cc884c9f5..d6e19cbcb826 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardDismissActionInteractorTest : SysuiTestCase() { private lateinit var keyguardRepository: FakeKeyguardRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt index c407b14dcf65..a5cfbbfda196 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt @@ -22,7 +22,6 @@ import android.service.trust.TrustAgentService import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.TrustGrantFlags -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.shared.model.DismissAction @@ -40,7 +39,6 @@ import org.junit.runner.RunWith import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardDismissInteractorTest : SysuiTestCase() { private lateinit var dispatcher: TestDispatcher diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index b527510fc56f..06eb0dd364b5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -28,8 +28,10 @@ import com.android.keyguard.FaceWakeUpTriggersConfig import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FaceSensorInfo import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository import com.android.systemui.biometrics.shared.model.LockoutMode +import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -156,7 +158,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { fakeDeviceEntryFingerprintAuthRepository, fakeUserRepository, facePropertyRepository, - fakeKeyguardRepository, faceWakeUpTriggersConfig, powerInteractor, ) @@ -440,6 +441,43 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { } @Test + fun faceAuthIsRequestedWhenWalletIsLaunchedAndIfFaceAuthIsStrong() = + testScope.runTest { + underTest.start() + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG)) + + underTest.onWalletLaunched() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true)) + } + + @Test + fun faceAuthIsNotTriggeredIfFaceAuthIsWeak() = + testScope.runTest { + underTest.start() + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.WEAK)) + + underTest.onWalletLaunched() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + + @Test + fun faceAuthIsNotTriggeredIfFaceAuthIsConvenience() = + testScope.runTest { + underTest.start() + facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.CONVENIENCE)) + + underTest.onWalletLaunched() + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + + @Test fun faceUnlockIsDisabledWhenFpIsLockedOut() = testScope.runTest { underTest.start() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index ca52cdb227e5..9ee22c89405d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.domain.interactor import android.app.StatusBarManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository @@ -47,7 +46,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardInteractorTest : SysuiTestCase() { @@ -206,7 +204,8 @@ class KeyguardInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) runCurrent() assertThat(isAnimate).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt index bbe68234f055..900413c14475 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before import org.junit.Rule import org.junit.Test @@ -46,6 +47,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +@ExperimentalCoroutinesApi @SmallTest @RunWith(AndroidJUnit4::class) class KeyguardKeyEventInteractorTest : SysuiTestCase() { @@ -132,58 +134,73 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { } @Test - fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() { + fun dispatchKeyEvent_menuActionUp_awakeKeyguard_showsPrimaryBouncer() { powerInteractor.setAwakeForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() - verify(shadeController).animateCollapseShadeForced() + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU) } @Test - fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() { + fun dispatchKeyEvent_menuActionUp_awakeShadeLocked_collapsesShade() { powerInteractor.setAwakeForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) - // action down: does NOT collapse the shade - val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() - verify(shadeController, never()).animateCollapseShadeForced() - - // action up: collapses the shade - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() - verify(shadeController).animateCollapseShadeForced() + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU) } @Test - fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() { + fun dispatchKeyEvent_menuActionUp_asleepKeyguard_neverCollapsesShade() { powerInteractor.setAsleepForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true) - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse() - verify(shadeController, never()).animateCollapseShadeForced() + verifyActionsDoNothing(KeyEvent.KEYCODE_MENU) } @Test - fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() { + fun dispatchKeyEvent_spaceActionUp_awakeKeyguard_collapsesShade() { powerInteractor.setAwakeForTest() whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false) - // action down: does NOT collapse the shade - val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE) - assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() - verify(shadeController, never()).animateCollapseShadeForced() + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE) + } - // action up: collapses the shade - val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE) - assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() - verify(shadeController).animateCollapseShadeForced() + @Test + fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() { + powerInteractor.setAwakeForTest() + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE) + } + + @Test + fun dispatchKeyEvent_enterActionUp_awakeKeyguard_showsPrimaryBouncer() { + powerInteractor.setAwakeForTest() + whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER) + } + + @Test + fun dispatchKeyEvent_enterActionUp_awakeKeyguard_primaryBouncerAlreadyShowing() { + powerInteractor.setAwakeForTest() + whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(true) + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + verifyActionsDoNothing(KeyEvent.KEYCODE_ENTER) + } + + @Test + fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() { + powerInteractor.setAwakeForTest() + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + + verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER) } @Test @@ -253,4 +270,42 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { .isFalse() verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any()) } + + private fun verifyActionUpCollapsesTheShade(keycode: Int) { + // action down: does NOT collapse the shade + val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) + assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() + verify(shadeController, never()).animateCollapseShadeForced() + + // action up: collapses the shade + val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) + assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() + verify(shadeController).animateCollapseShadeForced() + } + + private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) { + // action down: does NOT collapse the shade + val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) + assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() + verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + + // action up: collapses the shade + val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) + assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() + verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true)) + } + + private fun verifyActionsDoNothing(keycode: Int) { + // action down: does nothing + val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) + assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() + verify(shadeController, never()).animateCollapseShadeForced() + verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + + // action up: doesNothing + val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) + assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse() + verify(shadeController, never()).animateCollapseShadeForced() + verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt index 13025a016eff..0c74a38fea04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt @@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags @@ -51,7 +50,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardLongPressInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 8c13bb465103..347d580abf5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -23,7 +23,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.common.shared.model.ContentDescription @@ -73,7 +72,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt index fdcc66b6c974..71fcf6f9955c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository @@ -41,7 +40,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index fa93253642e3..29b546bd49ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -42,7 +41,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardTransitionInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt index 906d94859140..c02add1d0ecb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository @@ -45,7 +44,6 @@ import org.mockito.MockitoAnnotations import org.mockito.Spy @SmallTest -@RoboPilotTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class LightRevealScrimInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index 73ecae5af9b3..16f2fa22d5fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -38,7 +37,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations.initMocks @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt index a22f603ae92a..5b29a867ceaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt @@ -20,7 +20,6 @@ import android.testing.TestableLooper.RunWithLooper import android.view.RemoteAnimationTarget import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardViewController -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor @@ -41,7 +40,6 @@ import org.mockito.Mockito.doAnswer import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWithLooper(setAsMainLooper = true) @kotlinx.coroutines.ExperimentalCoroutinesApi class KeyguardSurfaceBehindParamsApplierTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt index 7a17435b9165..9b2db3e5316c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.binder import android.app.IActivityTaskManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.statusbar.policy.KeyguardStateController @@ -35,7 +34,6 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt new file mode 100644 index 000000000000..1768f8c4385b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.google.common.collect.Range +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@ExperimentalCoroutinesApi +@RunWith(JUnit4::class) +@SmallTest +class AlternateBouncerViewModelTest : SysuiTestCase() { + + private lateinit var testScope: TestScope + + @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var falsingManager: FalsingManager + + private lateinit var transitionRepository: FakeKeyguardTransitionRepository + private lateinit var transitionInteractor: KeyguardTransitionInteractor + private lateinit var underTest: AlternateBouncerViewModel + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + testScope = TestScope() + + val transitionInteractorWithDependencies = + KeyguardTransitionInteractorFactory.create(testScope.backgroundScope) + transitionInteractor = transitionInteractorWithDependencies.keyguardTransitionInteractor + transitionRepository = transitionInteractorWithDependencies.repository + underTest = + AlternateBouncerViewModel( + statusBarKeyguardViewManager, + transitionInteractor, + falsingManager, + ) + } + + @Test + fun transitionToAlternateBouncer_scrimAlphaUpdate() = + runTest(UnconfinedTestDispatcher()) { + val scrimAlphas by collectValues(underTest.scrimAlpha) + + transitionRepository.sendTransitionStep( + stepToAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.4f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f)) + + assertThat(scrimAlphas.size).isEqualTo(4) + scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } + } + + @Test + fun transitionFromAlternateBouncer_scrimAlphaUpdate() = + runTest(UnconfinedTestDispatcher()) { + val scrimAlphas by collectValues(underTest.scrimAlpha) + + transitionRepository.sendTransitionStep( + stepFromAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.4f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f)) + + assertThat(scrimAlphas.size).isEqualTo(4) + scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } + } + + @Test + fun clickListenerUpdate() = + runTest(UnconfinedTestDispatcher()) { + val clickListener by collectLastValue(underTest.onClickListener) + + // keyguard state => ALTERNATE_BOUNCER + transitionRepository.sendTransitionStep( + stepToAlternateBouncer(0f, TransitionState.STARTED) + ) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f)) + assertThat(clickListener).isNotNull() + + // ALTERNATE_BOUNCER -> keyguard state + transitionRepository.sendTransitionStep( + stepFromAlternateBouncer(0f, TransitionState.STARTED) + ) + assertThat(clickListener).isNotNull() + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f)) + assertThat(clickListener).isNull() + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f)) + assertThat(clickListener).isNull() + } + + @Test + fun forcePluginOpen() = + runTest(UnconfinedTestDispatcher()) { + val forcePluginOpen by collectLastValue(underTest.forcePluginOpen) + transitionRepository.sendTransitionStep( + stepToAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f)) + assertThat(forcePluginOpen).isTrue() + + transitionRepository.sendTransitionStep( + stepFromAlternateBouncer(0f, TransitionState.STARTED) + ) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f)) + transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f)) + assertThat(forcePluginOpen).isFalse() + } + + private fun stepToAlternateBouncer( + value: Float, + state: TransitionState = TransitionState.RUNNING + ): TransitionStep { + return step( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.ALTERNATE_BOUNCER, + value = value, + transitionState = state, + ) + } + + private fun stepFromAlternateBouncer( + value: Float, + state: TransitionState = TransitionState.RUNNING + ): TransitionStep { + return step( + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.LOCKSCREEN, + value = value, + transitionState = state, + ) + } + + private fun step( + from: KeyguardState, + to: KeyguardState, + value: Float, + transitionState: TransitionState + ): TransitionStep { + return TransitionStep( + from = from, + to = to, + value = value, + transitionState = transitionState, + ownerName = "AlternateBouncerViewModelTest" + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt index bfc6f31c57db..6d47aed58dac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -46,7 +45,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: DreamingToLockscreenTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt index 75c8bff326b0..cf2012989624 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class GoneToDreamingTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: GoneToDreamingTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 6e94691d42a9..7de28de4d436 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -28,10 +29,9 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class LockscreenSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt index 12fe07f0827e..89a1d2b3011d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: LockscreenToDreamingTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt index 83ae631ed164..41f8856d5ca2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: LockscreenToOccludedTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt index 88603999cdd7..ec95cb8c43f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory @@ -37,7 +36,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: OccludedToLockscreenTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index da372eaf158c..93640975901b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.coroutines.collectValues @@ -46,7 +45,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt index 9ab9b3d160d1..32acefebfa68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository @@ -46,7 +45,6 @@ import org.mockito.MockitoAnnotations @ExperimentalCoroutinesApi @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class UdfpsAodViewModelTest : SysuiTestCase() { private val defaultPadding = 12 diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java index d57765c3ecf2..c8c134a9474a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java @@ -16,6 +16,8 @@ package com.android.systemui.qs; +import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; + import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; @@ -41,6 +43,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -49,7 +52,6 @@ import androidx.lifecycle.Lifecycle; import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; @@ -60,6 +62,7 @@ import com.android.systemui.qs.external.TileServiceRequestController; import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.res.R; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.CommandQueue; @@ -110,6 +113,9 @@ public class QSImplTest extends SysuiTestCase { @Mock private FeatureFlagsClassic mFeatureFlags; private View mQsView; + private final CommandQueue mCommandQueue = + new CommandQueue(mContext, new FakeDisplayTracker(mContext)); + private QSImpl mUnderTest; @@ -120,6 +126,16 @@ public class QSImplTest extends SysuiTestCase { mUnderTest.onComponentCreated(mQsComponent, null); } + /* + * Regression test for b/303180152. + */ + @Test + public void testDisableCallbackOnDisabledQuickSettingsUponCreationDoesntCrash() { + QSImpl other = instantiate(); + mCommandQueue.disable(Display.DEFAULT_DISPLAY, 0, DISABLE2_QUICK_SETTINGS); + + other.onComponentCreated(mQsComponent, null); + } @Test public void testSaveState() { @@ -473,7 +489,6 @@ public class QSImplTest extends SysuiTestCase { private QSImpl instantiate() { MockitoAnnotations.initMocks(this); - CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); setupQsComponent(); setUpViews(); @@ -484,11 +499,11 @@ public class QSImplTest extends SysuiTestCase { return new QSImpl( new RemoteInputQuickSettingsDisabler( mContext, - commandQueue, + mCommandQueue, new ResourcesSplitShadeStateController(), mock(ConfigurationController.class)), mStatusBarStateController, - commandQueue, + mCommandQueue, mQSMediaHost, mQQSMediaHost, mBypassController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index e2ac7bc4fc6e..b825c0840e01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -42,7 +43,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.UiEventLoggerFake; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.media.controls.ui.MediaHost; @@ -50,6 +50,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.util.animation.DisappearParameters; @@ -341,11 +342,92 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { } @Test - public void onViewDetached_removesJustTheAssociatedCallback() { + public void onDestroy_removesJustTheAssociatedCallback() { QSPanelControllerBase.TileRecord record = mController.mRecords.get(0); - mController.onViewDetached(); + mController.destroy(); verify(mQSTile).removeCallback(record.callback); verify(mQSTile, never()).removeCallbacks(); + + assertThat(mController.mRecords).isEmpty(); + } + + @Test + public void onViewDettached_callbackNotRemoved() { + QSPanelControllerBase.TileRecord record = mController.mRecords.get(0); + + mController.onViewDetached(); + verify(mQSTile, never()).removeCallback(record.callback); + verify(mQSTile, never()).removeCallbacks(); + } + + @Test + public void onInit_qsHostCallbackAdded() { + verify(mQSHost).addCallback(any()); + } + + @Test + public void onViewDettached_qsHostCallbackNotRemoved() { + mController.onViewDetached(); + verify(mQSHost, never()).removeCallback(any()); + } + + @Test + public void onDestroy_qsHostCallbackRemoved() { + mController.destroy(); + verify(mQSHost).removeCallback(any()); + } + + @Test + public void setTiles_sameTiles_doesntRemoveAndReaddViews() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + mController.setTiles(); + verify(mQSPanel, never()).removeTile(any()); + verify(mQSPanel, never()).addTile(any()); + } + + @Test + public void setTiles_differentTiles_allTilesRemovedAndNewTilesAdded() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile)); + mController.setTiles(); + + verify(mQSPanel, times(2)).removeTile(any()); + verify(mQSPanel).addTile(any()); + } + + @Test + public void detachAndReattach_sameTiles_doesntRemoveAndReAddViews() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + mController.onViewDetached(); + mController.onViewAttached(); + verify(mQSPanel, never()).removeTile(any()); + verify(mQSPanel, never()).addTile(any()); + } + + @Test + public void setTiles_sameTilesDifferentOrder_removesAndReadds() { + when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); + mController.setTiles(); + + clearInvocations(mQSPanel); + + when(mQSHost.getTiles()).thenReturn(List.of(mOtherTile, mQSTile)); + mController.setTiles(); + + verify(mQSPanel, times(2)).removeTile(any()); + verify(mQSPanel, times(2)).addTile(any()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt index 9a55f722c13c..d277fcab3690 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.pipeline.data.repository import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.shared.TileSpec @@ -39,7 +38,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AutoAddSettingsRepositoryTest : SysuiTestCase() { private val secureSettings = FakeSettings() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt index 30f5811c3731..3db676d68f42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt @@ -20,7 +20,6 @@ import android.content.ComponentName import android.content.SharedPreferences import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserFileManager import com.android.systemui.util.FakeSharedPreferences @@ -30,7 +29,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt index 995de662aaf0..070e07a75d23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt @@ -32,7 +32,6 @@ import android.service.quicksettings.TileService import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.util.mockito.any @@ -62,7 +61,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt index dc09a33adc1c..ff8a9bd019fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt @@ -4,7 +4,6 @@ import android.content.Intent import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.FakeBroadcastDispatcher import com.android.systemui.coroutines.collectLastValue @@ -24,7 +23,6 @@ import org.mockito.MockitoAnnotations @SmallTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) -@RoboPilotTest class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() { private val dispatcher = StandardTestDispatcher() private val testScope = TestScope(dispatcher) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt index 08adebb87b1b..f7c3b213730c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt @@ -20,7 +20,6 @@ import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.shared.TileSpec @@ -40,7 +39,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class TileSpecSettingsRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt index 20876237b476..9516c2181ac0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt @@ -2,14 +2,12 @@ package com.android.systemui.qs.pipeline.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.qs.pipeline.shared.TileSpec import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class TilesSettingConverterTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt index 81fd72b11227..36e860e37ffa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt @@ -3,7 +3,6 @@ package com.android.systemui.qs.pipeline.data.repository import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.data.model.RestoreData @@ -24,7 +23,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) -@RoboPilotTest @SmallTest @RunWith(AndroidJUnit4::class) class UserAutoAddRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt index 389580c1326b..d4a9fabd6806 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt @@ -3,7 +3,6 @@ package com.android.systemui.qs.pipeline.data.repository import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.data.model.RestoreData @@ -23,7 +22,6 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@RoboPilotTest @SmallTest @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt index 15e401d24b02..4454a3cb15fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt @@ -20,7 +20,6 @@ import android.content.ComponentName import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.util.mockito.mock @@ -29,7 +28,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AutoAddableSettingListTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt index 7c6dd242a569..d153e9d1d361 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues @@ -36,7 +35,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class AutoAddableSettingTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt index 469eee363889..ec139e4c515e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -36,7 +35,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CallbackControllerAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt index b6eaa3922278..4fae532d4174 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -42,7 +41,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class CastAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt index a755fbbdfd7f..9e2d1f885e2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DataSaverAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt index daacca51a915..0116bd9575d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DeviceControlsAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt index 4b5f7f6d07ef..e7ea9a66450c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class HotspotAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt index 32d9db24a3b2..20fd3601f9ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import android.hardware.display.NightDisplayListener import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dagger.NightDisplayListenerModule @@ -50,7 +49,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class NightDisplayAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt index fb513a6bd197..19ac63c36cab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.ReduceBrightColorsController @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class ReduceBrightColorsAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt index 5ce15fa57b1f..d645ee34619b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt @@ -21,7 +21,6 @@ import android.content.pm.PackageManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues @@ -52,7 +51,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class SafetyCenterAutoAddableTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt index 1c8cb541e613..83ff35d8022d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -38,7 +37,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WalletAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt index de1d29fdcbe5..adccc84e494b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt @@ -23,7 +23,6 @@ import android.content.pm.UserInfo.FLAG_PRIMARY import android.content.pm.UserInfo.FLAG_PROFILE import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal @@ -41,7 +40,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WorkTileAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt index bb18115c960c..41a7ec03408d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.pipeline.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.DumpManager @@ -47,7 +46,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class AutoAddInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index a7505240caeb..a89338a38e89 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -24,7 +24,6 @@ import android.os.UserHandle import android.service.quicksettings.Tile import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.nano.SystemUIProtoDump @@ -71,7 +70,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class CurrentTilesInteractorImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt index 151b256c4f5c..0d9711588a1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.qs.pipeline.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.shade.ShadeController import org.junit.Before @@ -27,7 +26,6 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RoboPilotTest @RunWith(AndroidJUnit4::class) @SmallTest class PanelInteractorImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt index 2e6b50b637dd..f73cab8a10a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt @@ -2,7 +2,6 @@ package com.android.systemui.qs.pipeline.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.data.model.RestoreData @@ -20,7 +19,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.MockitoAnnotations -@RoboPilotTest @RunWith(AndroidJUnit4::class) @SmallTest class RestoreReconciliationInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt index 34c4c9842986..558e7694b54c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt @@ -19,14 +19,12 @@ package com.android.systemui.qs.pipeline.shared import android.content.ComponentName import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class TileSpecTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt index 06b7a9fde873..5659f0173860 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.tiles.base.actions import android.content.Intent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter import org.junit.Before @@ -32,7 +31,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QSTileIntentUserActionHandlerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt index 2c4e10eadfc9..9861606fd1b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt @@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.internal.logging.UiEventLogger -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.qs.QSEvent import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder @@ -34,7 +33,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QSTileAnalyticsTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt index 4f25d12aea49..a6199c25874b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt @@ -24,7 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter import com.android.systemui.util.mockito.any @@ -46,7 +45,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DisabledByPolicyInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt index 4401e0d60da6..f1fcee318141 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.tiles.base.logging import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -39,7 +38,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class QSTileLoggerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt index 4760dfa3561d..2084aeb7fe83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt @@ -20,7 +20,6 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.android.internal.logging.InstanceId -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.common.shared.model.ContentDescription @@ -49,7 +48,6 @@ import org.mockito.MockitoAnnotations // TODO(b/299909368): Add more tests @MediumTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index f1c99d71b4e5..58e36be2b9fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -38,11 +39,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class QuickSettingsSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 91f38656e02d..2e16577bb24f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository @@ -51,6 +52,7 @@ import com.google.common.truth.Truth.assertWithMessage import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -58,7 +60,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 /** * Integration test cases for the Scene Framework. @@ -80,7 +81,7 @@ import org.junit.runners.JUnit4 */ @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneFrameworkIntegrationTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -462,7 +463,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { fromScene = getCurrentSceneInUi(), toScene = to.key, progress = progressFlow, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index ff28d2dbccdd..740c6d9329df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.data.repository +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -28,13 +29,13 @@ import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneContainerRepositoryTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -119,7 +120,8 @@ class SceneContainerRepositoryTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt index 2187f3657848..7ae501d05fcd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.data.repository +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase @@ -24,9 +25,11 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Test +import org.junit.runner.RunWith import org.mockito.Mockito.verify @SmallTest +@RunWith(AndroidJUnit4::class) class WindowRootViewVisibilityRepositoryTest : SysuiTestCase() { private val iStatusBarService = mock<IStatusBarService>() private val executor = FakeExecutor(FakeSystemClock()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index afc0e6944839..31d26c0d88f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -14,10 +14,9 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) - package com.android.systemui.scene.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -26,16 +25,14 @@ import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -86,7 +83,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) @@ -124,7 +122,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = underTest.desiredScene.value.key, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(transitionTo).isEqualTo(SceneKey.Shade) @@ -161,7 +160,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) val transitioning by @@ -180,7 +180,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) underTest.setTransitionState(transitionState) @@ -197,7 +198,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) val transitioning by @@ -225,7 +227,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(transitioning).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt index 3785fd7436c1..8be4eeb7be7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase @@ -36,16 +37,16 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith import org.mockito.Mockito.reset import org.mockito.Mockito.verify @SmallTest -@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) class WindowRootViewVisibilityInteractorTest : SysuiTestCase() { private val testScope = TestScope() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 0216a0a98d84..3b9621e5c6e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.scene.domain.startable import android.os.PowerManager import android.view.Display +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.domain.model.AuthenticationMethodModel @@ -46,14 +47,13 @@ import kotlinx.coroutines.test.runTest import org.junit.Assume.assumeTrue import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneContainerStartableTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -109,7 +109,8 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason") @@ -122,7 +123,8 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Gone, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason") diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt index 0b56a59ca467..c89cd9e0c1f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -29,10 +30,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SceneContainerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt index 1bb2ff825115..263b0017221a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt @@ -42,6 +42,7 @@ class UserTrackerImplReceiveTest : SysuiTestCase() { @Parameterized.Parameters fun data(): Iterable<String> = listOf( + Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_USER_INFO_CHANGED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt index 52b25f85b870..c32d2597bd61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt @@ -177,7 +177,8 @@ class UserTrackerImplTest : SysuiTestCase() { verify(context) .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler)) with(captor.value) { - assertThat(countActions()).isEqualTo(6) + assertThat(countActions()).isEqualTo(7) + assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue() assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue() assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue() assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 7b12931b2b47..8d8c70e26ab2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -79,7 +79,6 @@ import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.keyguard.logging.KeyguardLogger; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -120,6 +119,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSFragmentLegacy; +import com.android.systemui.res.R; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.data.repository.ShadeRepository; @@ -153,7 +153,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; @@ -170,6 +169,7 @@ import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; @@ -182,6 +182,8 @@ import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; +import dagger.Lazy; + import org.junit.After; import org.junit.Before; import org.mockito.ArgumentCaptor; @@ -193,7 +195,6 @@ import org.mockito.stubbing.Answer; import java.util.List; import java.util.Optional; -import dagger.Lazy; import kotlinx.coroutines.CoroutineDispatcher; public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @@ -208,7 +209,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; @Mock protected ViewPropertyAnimator mViewPropertyAnimator; @Mock protected KeyguardBottomAreaView mQsFrame; - @Mock protected HeadsUpManagerPhone mHeadsUpManager; + @Mock protected HeadsUpManager mHeadsUpManager; @Mock protected NotificationShelfController mNotificationShelfController; @Mock protected NotificationGutsManager mGutsManager; @Mock protected KeyguardStatusBarView mKeyguardStatusBar; @@ -527,7 +528,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { NotificationWakeUpCoordinator coordinator = new NotificationWakeUpCoordinator( mDumpManager, - mock(HeadsUpManagerPhone.class), + mock(HeadsUpManager.class), new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager, mInteractionJankMonitor, mShadeExpansionStateManager), mKeyguardBypassController, @@ -547,7 +548,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mLockscreenShadeTransitionController, new FalsingCollectorFake(), mDumpManager); - when(mKeyguardStatusViewComponentFactory.build(any())) + when(mKeyguardStatusViewComponentFactory.build(any(), any())) .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) .thenReturn(mKeyguardClockSwitchController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 2ed20908ba2f..eb006100d535 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -47,18 +47,34 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; +import com.android.keyguard.KeyguardSecurityModel; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.data.repository.FakeCommandQueue; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.res.R; import com.android.systemui.scene.FakeWindowRootViewComponent; import com.android.systemui.scene.SceneTestUtils; +import com.android.systemui.scene.data.repository.SceneContainerRepository; +import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.StatusBarState; @@ -71,9 +87,9 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.user.domain.interactor.UserInteractor; import com.google.common.util.concurrent.MoreExecutors; @@ -109,6 +125,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private SysuiColorExtractor mColorExtractor; @Mock ColorExtractor.GradientColors mGradientColors; @Mock private DumpManager mDumpManager; + @Mock private KeyguardSecurityModel mKeyguardSecurityModel; @Mock private KeyguardStateController mKeyguardStateController; @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private AuthController mAuthController; @@ -123,6 +140,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private float mPreferredRefreshRate = -1; + private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor; + private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor; @Before public void setUp() { @@ -137,22 +156,86 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { when(mDozeParameters.getAlwaysOn()).thenReturn(true); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); + FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository(); + FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic(); + FakeShadeRepository shadeRepository = new FakeShadeRepository(); + FakePowerRepository powerRepository = new FakePowerRepository(); + + PowerInteractor powerInteractor = new PowerInteractor( + powerRepository, + new FalsingCollectorFake(), + mScreenOffAnimationController, + mStatusBarStateController); + + SceneInteractor sceneInteractor = new SceneInteractor( + mTestScope.getBackgroundScope(), + new SceneContainerRepository( + mTestScope.getBackgroundScope(), + mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())), + powerRepository, + mock(SceneLogger.class)); + + FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); + FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); + KeyguardInteractor keyguardInteractor = new KeyguardInteractor( + keyguardRepository, + new FakeCommandQueue(), + powerInteractor, + featureFlags, + sceneContainerFlags, + new FakeDeviceEntryRepository(), + new FakeKeyguardBouncerRepository(), + configurationRepository, + shadeRepository, + () -> sceneInteractor); + + FakeKeyguardTransitionRepository keyguardTransitionRepository = + new FakeKeyguardTransitionRepository(); + + KeyguardTransitionInteractor keyguardTransitionInteractor = + new KeyguardTransitionInteractor( + mTestScope.getBackgroundScope(), + keyguardTransitionRepository, + () -> keyguardInteractor, + () -> mFromLockscreenTransitionInteractor, + () -> mFromPrimaryBouncerTransitionInteractor); + + mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + shadeRepository, + powerInteractor); + + mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mKeyguardSecurityModel, + powerInteractor); + mShadeInteractor = new ShadeInteractor( mTestScope.getBackgroundScope(), + new FakeDeviceProvisioningRepository(), new FakeDisableFlagsRepository(), - new FakeSceneContainerFlags(), - mUtils::sceneInteractor, - new FakeKeyguardRepository(), + mock(DozeParameters.class), + sceneContainerFlags, + () -> sceneInteractor, + keyguardRepository, + keyguardTransitionInteractor, + powerInteractor, new FakeUserSetupRepository(), - mock(DeviceProvisionedController.class), mock(UserInteractor.class), new SharedNotificationContainerInteractor( - new FakeConfigurationRepository(), + configurationRepository, mContext, new ResourcesSplitShadeStateController()), - new FakeShadeRepository() - ); + shadeRepository); mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl( mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 96510721baa5..b4f9e8dcfb39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -83,7 +84,6 @@ import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.TestScope @@ -97,8 +97,9 @@ import org.mockito.Mockito.anyFloat import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import java.util.Optional +import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -144,6 +145,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel @Mock lateinit var keyEventInteractor: KeyEventInteractor + @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor private val notificationExpansionRepository = NotificationExpansionRepository() private lateinit var fakeClock: FakeSystemClock @@ -176,6 +179,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) featureFlags.set(Flags.MIGRATE_NSSL, false) + featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false) testScope = TestScope() fakeClock = FakeSystemClock() @@ -248,6 +252,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { ), BouncerLogger(logcatLogBuffer("BouncerLog")), keyEventInteractor, + primaryBouncerInteractor, + alternateBouncerInteractor, ) underTest.setupExpandedStatusBar() underTest.setDragDownHelper(dragDownHelper) @@ -425,29 +431,37 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { } @Test - fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() { - // GIVEN not dozing - whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false) + fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() { + // GIVEN dozing + whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) // AND alternate bouncer doesn't want the touch whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) .thenReturn(false) - // AND the lock icon doesn't want the touch - whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false) - // AND the notification panel can accept touches - whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true) - whenever(dragDownHelper.isDragDownEnabled).thenReturn(true) - whenever(centralSurfaces.isBouncerShowing()).thenReturn(false) - - // AND the drag down helper doesn't want the touch (to pull the shade down) - whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false) + // AND the lock icon wants the touch + whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)) + .thenReturn(true) featureFlags.set(Flags.MIGRATE_NSSL, true) - // WHEN asked if should intercept touch - interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT) + // THEN touch should NOT be intercepted by NotificationShade + assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse() + } + + @Test + fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() { + // GIVEN dozing + whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) + // AND alternate bouncer doesn't want the touch + whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) + .thenReturn(false) + // AND the lock icon does NOT want the touch + whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)) + .thenReturn(false) + + featureFlags.set(Flags.MIGRATE_NSSL, true) - // Verify that NPVC gets a chance to use the touch - verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT) + // THEN touch should NOT be intercepted by NotificationShade + assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 00230202d941..189c9e25d9b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor @@ -141,6 +142,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { Optional<UnfoldTransitionProgressProvider> @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock private lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel @@ -180,6 +183,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) featureFlags.set(Flags.MIGRATE_NSSL, false) + featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false) testScope = TestScope() controller = NotificationShadeWindowViewController( @@ -250,6 +254,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { ), BouncerLogger(logcatLogBuffer("BouncerLog")), Mockito.mock(KeyEventInteractor::class.java), + primaryBouncerInteractor, + alternateBouncerInteractor, ) controller.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index 8138b328a3c5..65174bab7f63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -32,23 +32,39 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.keyguard.data.repository.FakeCommandQueue; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSFragmentLegacy; import com.android.systemui.res.R; import com.android.systemui.scene.SceneTestUtils; +import com.android.systemui.scene.data.repository.SceneContainerRepository; +import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; @@ -64,19 +80,21 @@ import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFl import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor; +import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.user.domain.interactor.UserInteractor; import com.android.systemui.util.kotlin.JavaAdapter; @@ -103,8 +121,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { protected SceneTestUtils mUtils = new SceneTestUtils(this); protected TestScope mTestScope = mUtils.getTestScope(); - @Mock - protected Resources mResources; + @Mock protected Resources mResources; @Mock protected KeyguardBottomAreaView mQsFrame; @Mock protected KeyguardStatusBarView mKeyguardStatusBar; @Mock protected QS mQs; @@ -126,6 +143,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { @Mock protected NotificationShadeDepthController mNotificationShadeDepthController; @Mock protected ShadeHeaderController mShadeHeaderController; @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock protected DozeParameters mDozeParameters; @Mock protected KeyguardStateController mKeyguardStateController; @Mock protected KeyguardBypassController mKeyguardBypassController; @Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -144,7 +162,6 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { @Mock protected DumpManager mDumpManager; @Mock protected UiEventLogger mUiEventLogger; @Mock protected CastController mCastController; - @Mock protected DeviceProvisionedController mDeviceProvisionedController; @Mock protected UserInteractor mUserInteractor; protected FakeDisableFlagsRepository mDisableFlagsRepository = new FakeDisableFlagsRepository(); @@ -161,6 +178,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { new ShadeExpansionStateManager(); protected FragmentHostManager.FragmentListener mFragmentListener; + private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor; + private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor; @Before public void setup() { @@ -169,21 +188,89 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, mInteractionJankMonitor, mShadeExpansionStateManager); - when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + FakeDeviceProvisioningRepository deviceProvisioningRepository = + new FakeDeviceProvisioningRepository(); + deviceProvisioningRepository.setDeviceProvisioned(true); + FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic(); + FakePowerRepository powerRepository = new FakePowerRepository(); + FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); + + PowerInteractor powerInteractor = new PowerInteractor( + powerRepository, + new FalsingCollectorFake(), + mock(ScreenOffAnimationController.class), + mStatusBarStateController); + + SceneInteractor sceneInteractor = new SceneInteractor( + mTestScope.getBackgroundScope(), + new SceneContainerRepository( + mTestScope.getBackgroundScope(), + mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())), + powerRepository, + mock(SceneLogger.class)); + + FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); + KeyguardInteractor keyguardInteractor = new KeyguardInteractor( + mKeyguardRepository, + new FakeCommandQueue(), + powerInteractor, + featureFlags, + sceneContainerFlags, + new FakeDeviceEntryRepository(), + new FakeKeyguardBouncerRepository(), + configurationRepository, + mShadeRepository, + () -> sceneInteractor); + + FakeKeyguardTransitionRepository keyguardTransitionRepository = + new FakeKeyguardTransitionRepository(); + + KeyguardTransitionInteractor keyguardTransitionInteractor = + new KeyguardTransitionInteractor( + mTestScope.getBackgroundScope(), + keyguardTransitionRepository, + () -> keyguardInteractor, + () -> mFromLockscreenTransitionInteractor, + () -> mFromPrimaryBouncerTransitionInteractor); + + mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mShadeRepository, + powerInteractor); + + mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mock(KeyguardSecurityModel.class), + powerInteractor); + + ResourcesSplitShadeStateController splitShadeStateController = + new ResourcesSplitShadeStateController(); + mShadeInteractor = new ShadeInteractor( mTestScope.getBackgroundScope(), + deviceProvisioningRepository, mDisableFlagsRepository, - new FakeSceneContainerFlags(), - () -> mUtils.sceneInteractor(), + mDozeParameters, + sceneContainerFlags, + () -> sceneInteractor, mKeyguardRepository, + keyguardTransitionInteractor, + powerInteractor, new FakeUserSetupRepository(), - mDeviceProvisionedController, mUserInteractor, new SharedNotificationContainerInteractor( - new FakeConfigurationRepository(), + configurationRepository, mContext, - new ResourcesSplitShadeStateController()), + splitShadeStateController), mShadeRepository ); @@ -262,7 +349,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mShadeInteractor, new JavaAdapter(mTestScope.getBackgroundScope()), mCastController, - new ResourcesSplitShadeStateController() + splitShadeStateController ); mQsController.init(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index b5841a9de150..215f8b1c462f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.shade import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper import android.view.Display import android.view.WindowManager import androidx.test.filters.SmallTest @@ -54,6 +55,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) @SmallTest class ShadeControllerImplTest : SysuiTestCase() { private val executor = FakeExecutor(FakeSystemClock()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt index 2be1c09843c1..bcb060ddb417 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt @@ -16,50 +16,53 @@ package com.android.systemui.shade.data.repository -import android.app.ActivityManager import android.app.StatusBarManager.DISABLE2_NONE import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS import android.content.pm.UserInfo import android.os.UserManager import androidx.test.filters.SmallTest -import com.android.internal.logging.UiEventLogger -import com.android.keyguard.KeyguardUpdateMonitor +import com.android.SysUITestModule +import com.android.TestMocksModule import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeTransitionModel +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState -import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.res.R -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.telephony.data.repository.FakeTelephonyRepository -import com.android.systemui.telephony.domain.interactor.TelephonyInteractor +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.user.data.model.UserSwitcherSettingsModel import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.interactor.GuestUserInteractor -import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode -import com.android.systemui.user.domain.interactor.RefreshUsersScheduler -import com.android.systemui.user.domain.interactor.UserInteractor -import com.android.systemui.util.mockito.mock +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -70,50 +73,55 @@ import org.mockito.MockitoAnnotations @SmallTest @OptIn(ExperimentalCoroutinesApi::class) class ShadeInteractorTest : SysuiTestCase() { - private lateinit var underTest: ShadeInteractor - private val utils = SceneTestUtils(this) - private val testScope = utils.testScope - private val featureFlags = FakeFeatureFlags() - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - private val userSetupRepository = FakeUserSetupRepository() - private val userRepository = FakeUserRepository() - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val keyguardRepository = FakeKeyguardRepository() - private val shadeRepository = FakeShadeRepository() - private val configurationRepository = FakeConfigurationRepository() - private val sharedNotificationContainerInteractor = - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - - @Mock private lateinit var manager: UserManager - @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - @Mock private lateinit var activityStarter: ActivityStarter - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var activityManager: ActivityManager - @Mock private lateinit var uiEventLogger: UiEventLogger - @Mock private lateinit var guestInteractor: GuestUserInteractor - - private lateinit var userInteractor: UserInteractor + @Mock private lateinit var dozeParameters: DozeParameters + + private lateinit var testComponent: TestComponent + + private val configurationRepository + get() = testComponent.configurationRepository + private val deviceProvisioningRepository + get() = testComponent.deviceProvisioningRepository + private val disableFlagsRepository + get() = testComponent.disableFlagsRepository + private val keyguardRepository + get() = testComponent.keyguardRepository + private val keyguardTransitionRepository + get() = testComponent.keygaurdTransitionRepository + private val powerRepository + get() = testComponent.powerRepository + private val sceneInteractor + get() = testComponent.sceneInteractor + private val shadeRepository + get() = testComponent.shadeRepository + private val testScope + get() = testComponent.testScope + private val userRepository + get() = testComponent.userRepository + private val userSetupRepository + get() = testComponent.userSetupRepository + + private lateinit var underTest: ShadeInteractor @Before fun setUp() { MockitoAnnotations.initMocks(this) - featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) - featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) - - val refreshUsersScheduler = - RefreshUsersScheduler( - applicationScope = testScope.backgroundScope, - mainDispatcher = utils.testDispatcher, - repository = userRepository, - ) + testComponent = + DaggerShadeInteractorTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FACE_AUTH_REFACTOR, false) + set(Flags.FULL_SCREEN_USER_SWITCHER, true) + }, + mocks = + TestMocksModule( + dozeParameters = dozeParameters, + ), + ) + underTest = testComponent.underTest runBlocking { val userInfos = @@ -131,44 +139,6 @@ class ShadeInteractorTest : SysuiTestCase() { userRepository.setUserInfos(userInfos) userRepository.setSelectedUserInfo(userInfos[0]) } - userInteractor = - UserInteractor( - applicationContext = context, - repository = userRepository, - activityStarter = activityStarter, - keyguardInteractor = - KeyguardInteractorFactory.create(featureFlags = featureFlags) - .keyguardInteractor, - featureFlags = featureFlags, - manager = manager, - headlessSystemUserMode = headlessSystemUserMode, - applicationScope = testScope.backgroundScope, - telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), - broadcastDispatcher = fakeBroadcastDispatcher, - keyguardUpdateMonitor = keyguardUpdateMonitor, - backgroundDispatcher = utils.testDispatcher, - activityManager = activityManager, - refreshUsersScheduler = refreshUsersScheduler, - guestUserInteractor = guestInteractor, - uiEventLogger = uiEventLogger, - userRestrictionChecker = mock(), - ) - underTest = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository, - deviceProvisionedController, - userInteractor, - sharedNotificationContainerInteractor, - shadeRepository, - ) } @Test @@ -188,7 +158,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_deviceNotProvisioned_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) + deviceProvisioningRepository.setDeviceProvisioned(false) val actual by collectLastValue(underTest.isExpandToQsEnabled) @@ -198,7 +168,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_userNotSetupAndSimpleUserSwitcher_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(false) userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true)) @@ -211,7 +181,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_shadeNotEnabled_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(true) disableFlagsRepository.disableFlags.value = @@ -227,7 +197,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_quickSettingsNotEnabled_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(true) disableFlagsRepository.disableFlags.value = @@ -242,7 +212,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_dozing_false() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetup(true) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -259,7 +229,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_userSetup_true() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -276,7 +246,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_notSimpleUserSwitcher_true() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -293,7 +263,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToDozingUpdates() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -321,7 +291,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToDisableUpdates() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -353,7 +323,7 @@ class ShadeInteractorTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToUserUpdates() = testScope.runTest { - whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -625,7 +595,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -662,7 +633,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -698,7 +670,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -974,7 +947,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1011,7 +985,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = true, + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1048,7 +1023,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1085,7 +1061,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = true, + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1120,8 +1097,9 @@ class ShadeInteractorTest : SysuiTestCase() { ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = SceneKey.QuickSettings, - progress = progress, - isUserInputDriven = true, + progress = MutableStateFlow(0f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -1129,4 +1107,168 @@ class ShadeInteractorTest : SysuiTestCase() { // THEN interacting is false assertThat(interacting).isFalse() } + + @Test + fun isShadeTouchable_isFalse_whenFrpIsActive() = + testScope.runTest { + deviceProvisioningRepository.setFactoryResetProtectionActive(true) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isFalse() + } + + @Test + fun isShadeTouchable_isFalse_whenDeviceAsleepAndNotPulsing() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == false + // TODO: remove? + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_AOD, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isFalse() + } + + @Test + fun isShadeTouchable_isTrue_whenDeviceAsleepAndPulsing() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == false + // TODO: remove? + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_PULSING, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isTrue() + } + + @Test + fun isShadeTouchable_isFalse_whenStartingToSleepAndNotControlScreenOff() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == true + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParameters.shouldControlScreenOff()).thenReturn(false) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isFalse() + } + + @Test + fun isShadeTouchable_isTrue_whenStartingToSleepAndControlScreenOff() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + // goingToSleep == true + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParameters.shouldControlScreenOff()).thenReturn(true) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isTrue() + } + + @Test + fun isShadeTouchable_isTrue_whenNotAsleep() = + testScope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val isShadeTouchable by collectLastValue(underTest.isShadeTouchable) + runCurrent() + assertThat(isShadeTouchable).isTrue() + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: ShadeInteractor + + val configurationRepository: FakeConfigurationRepository + val deviceProvisioningRepository: FakeDeviceProvisioningRepository + val disableFlagsRepository: FakeDisableFlagsRepository + val keyguardRepository: FakeKeyguardRepository + val keygaurdTransitionRepository: FakeKeyguardTransitionRepository + val powerRepository: FakePowerRepository + val sceneInteractor: SceneInteractor + val shadeRepository: FakeShadeRepository + val testScope: TestScope + val userRepository: FakeUserRepository + val userSetupRepository: FakeUserSetupRepository + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + mocks: TestMocksModule, + ): TestComponent + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt index 7463e65d1d6f..6eeafefd0ac1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.shade.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionStateManager @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class ShadeRepositoryImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index 0925858d740d..bb20d94e7d39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.shade.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -16,15 +17,15 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class ShadeHeaderViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope @@ -84,7 +85,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) @@ -102,7 +104,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.QuickSettings, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) @@ -120,7 +123,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 602bd5fded3d..c4237827c3ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel @@ -37,11 +38,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class ShadeSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index e71473681211..ae659f4d2676 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -131,11 +131,10 @@ class ClockRegistryTest : SysuiTestCase() { fun addClock( id: ClockId, - name: String, create: (ClockId) -> ClockController = ::failFactory, getThumbnail: (ClockId) -> Drawable? = ::failThumbnail ): FakeClockPlugin { - metadata.add(ClockMetadata(id, name)) + metadata.add(ClockMetadata(id)) createCallbacks[id] = create thumbnailCallbacks[id] = getThumbnail return this @@ -149,7 +148,7 @@ class ClockRegistryTest : SysuiTestCase() { scope = TestScope(dispatcher) fakeDefaultProvider = FakeClockPlugin() - .addClock(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME, { mockDefaultClock }, { mockThumbnail }) + .addClock(DEFAULT_CLOCK_ID, { mockDefaultClock }, { mockThumbnail }) whenever(mockContext.contentResolver).thenReturn(mockContentResolver) val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>() @@ -183,13 +182,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun pluginRegistration_CorrectState() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = FakeLifecycle("1", plugin1) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3") - .addClock("clock_4", "clock 4") + .addClock("clock_3") + .addClock("clock_4") val lifecycle2 = FakeLifecycle("2", plugin2) pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1) @@ -198,11 +197,11 @@ class ClockRegistryTest : SysuiTestCase() { assertEquals( list.toSet(), setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1"), - ClockMetadata("clock_2", "clock 2"), - ClockMetadata("clock_3", "clock 3"), - ClockMetadata("clock_4", "clock 4") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1"), + ClockMetadata("clock_2"), + ClockMetadata("clock_3"), + ClockMetadata("clock_4") ) ) } @@ -216,13 +215,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail }) - .addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail }) + .addClock("clock_1", { mockClock }, { mockThumbnail }) + .addClock("clock_2", { mockClock }, { mockThumbnail }) val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1) @@ -231,9 +230,9 @@ class ClockRegistryTest : SysuiTestCase() { assertEquals( list.toSet(), setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1"), - ClockMetadata("clock_2", "clock 2") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1"), + ClockMetadata("clock_2") ) ) @@ -248,13 +247,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun createCurrentClock_pluginConnected() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3", { mockClock }) - .addClock("clock_4", "clock 4") + .addClock("clock_3", { mockClock }) + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) registry.applySettings(ClockSettings("clock_3", null)) @@ -268,13 +267,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun activeClockId_changeAfterPluginConnected() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3", { mockClock }) - .addClock("clock_4", "clock 4") + .addClock("clock_3", { mockClock }) + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) registry.applySettings(ClockSettings("clock_3", null)) @@ -289,13 +288,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun createDefaultClock_pluginDisconnected() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3") - .addClock("clock_4", "clock 4") + .addClock("clock_3") + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) registry.applySettings(ClockSettings("clock_3", null)) @@ -310,13 +309,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun pluginRemoved_clockAndListChanged() { val plugin1 = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("clock_2", "clock 2") + .addClock("clock_1") + .addClock("clock_2") val lifecycle1 = spy(FakeLifecycle("1", plugin1)) val plugin2 = FakeClockPlugin() - .addClock("clock_3", "clock 3", { mockClock }) - .addClock("clock_4", "clock 4") + .addClock("clock_3", { mockClock }) + .addClock("clock_4") val lifecycle2 = spy(FakeLifecycle("2", plugin2)) var changeCallCount = 0 @@ -415,13 +414,13 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun pluginAddRemove_concurrentModification() { - val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1") + val plugin1 = FakeClockPlugin().addClock("clock_1") val lifecycle1 = FakeLifecycle("1", plugin1) - val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2") + val plugin2 = FakeClockPlugin().addClock("clock_2") val lifecycle2 = FakeLifecycle("2", plugin2) - val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3") + val plugin3 = FakeClockPlugin().addClock("clock_3") val lifecycle3 = FakeLifecycle("3", plugin3) - val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4") + val plugin4 = FakeClockPlugin().addClock("clock_4") val lifecycle4 = FakeLifecycle("4", plugin4) // Set the current clock to the final clock to load @@ -450,10 +449,10 @@ class ClockRegistryTest : SysuiTestCase() { // Verify all plugins were correctly loaded into the registry assertEquals(registry.getClocks().toSet(), setOf( - ClockMetadata("DEFAULT", "Default Clock"), - ClockMetadata("clock_2", "clock 2"), - ClockMetadata("clock_3", "clock 3"), - ClockMetadata("clock_4", "clock 4") + ClockMetadata("DEFAULT"), + ClockMetadata("clock_2"), + ClockMetadata("clock_3"), + ClockMetadata("clock_4") )) } @@ -527,8 +526,8 @@ class ClockRegistryTest : SysuiTestCase() { featureFlags.set(TRANSIT_CLOCK, flag) registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK) val plugin = FakeClockPlugin() - .addClock("clock_1", "clock 1") - .addClock("DIGITAL_CLOCK_METRO", "metro clock") + .addClock("clock_1") + .addClock("DIGITAL_CLOCK_METRO") val lifecycle = FakeLifecycle("metro", plugin) pluginListener.onPluginLoaded(plugin, mockContext, lifecycle) @@ -536,17 +535,17 @@ class ClockRegistryTest : SysuiTestCase() { if (flag) { assertEquals( setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1"), - ClockMetadata("DIGITAL_CLOCK_METRO", "metro clock") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1"), + ClockMetadata("DIGITAL_CLOCK_METRO") ), list.toSet() ) } else { assertEquals( setOf( - ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), - ClockMetadata("clock_1", "clock 1") + ClockMetadata(DEFAULT_CLOCK_ID), + ClockMetadata("clock_1") ), list.toSet() ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt index 7a2d12269885..b8fe2f911b50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.smartspace import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -33,7 +32,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class BcSmartspaceConfigProviderTest : SysuiTestCase() { @Mock private lateinit var featureFlags: FeatureFlags diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt index f1c181fb2235..e09385934991 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt @@ -26,7 +26,6 @@ import android.view.ViewGroup import android.widget.FrameLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.dreams.smartspace.DreamSmartspaceController import com.android.systemui.plugins.BcSmartspaceConfigPlugin @@ -53,7 +52,6 @@ import org.mockito.MockitoAnnotations import org.mockito.Spy @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class DreamSmartspaceControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt index 7af29ba28df1..886c61ae29dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt @@ -25,7 +25,6 @@ import android.provider.Settings import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserTracker import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter @@ -52,7 +51,6 @@ import org.mockito.MockitoAnnotations @SmallTest @TestableLooper.RunWithLooper -@RoboPilotTest @RunWith(AndroidJUnit4::class) class LockscreenAndDreamTargetFilterTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt index a7c223dc4b8a..0b5aea7d8683 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.smartspace import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.smartspace.preconditions.LockscreenPrecondition import com.android.systemui.statusbar.policy.DeviceProvisionedController @@ -36,7 +35,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class LockscreenPreconditionTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 14e58e5f570a..e8923a5baca8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -5,41 +5,32 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule import com.android.systemui.ExpandHelper -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase -import com.android.systemui.classifier.FalsingCollector -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.dump.DumpManager -import com.android.systemui.keyguard.WakefulnessLifecycle -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags import com.android.systemui.media.controls.ui.MediaHierarchyManager -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS -import com.android.systemui.power.domain.interactor.PowerInteractorFactory -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.res.R import com.android.systemui.shade.ShadeViewController -import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper -import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.ScrimController -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.policy.FakeConfigurationController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.util.mockito.mock +import com.android.systemui.user.domain.UserDomainLayerModule +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import org.junit.After import org.junit.Assert.assertFalse @@ -60,9 +51,8 @@ import org.mockito.Mockito import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +import org.mockito.junit.MockitoJUnit private fun <T> anyObject(): T { return Mockito.anyObject<T>() @@ -73,68 +63,38 @@ private fun <T> anyObject(): T { @RunWith(AndroidTestingRunner::class) @OptIn(ExperimentalCoroutinesApi::class) class LockscreenShadeTransitionControllerTest : SysuiTestCase() { - private val utils = SceneTestUtils(this) - private val testScope = utils.testScope - lateinit var transitionController: LockscreenShadeTransitionController + private lateinit var testComponent: TestComponent + + private val transitionController + get() = testComponent.transitionController + private val configurationController + get() = testComponent.configurationController + private val disableFlagsRepository + get() = testComponent.disableFlagsRepository + private val testScope + get() = testComponent.testScope + lateinit var row: ExpandableNotificationRow - @Mock lateinit var statusbarStateController: SysuiStatusBarStateController - @Mock lateinit var logger: LSShadeTransitionLogger - @Mock lateinit var dumpManager: DumpManager + + @Mock lateinit var centralSurfaces: CentralSurfaces + @Mock lateinit var depthController: NotificationShadeDepthController + @Mock lateinit var expandHelperCallback: ExpandHelper.Callback @Mock lateinit var keyguardBypassController: KeyguardBypassController @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager - @Mock lateinit var falsingCollector: FalsingCollector - @Mock lateinit var ambientState: AmbientState - @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager + @Mock lateinit var nsslController: NotificationStackScrollLayoutController + @Mock lateinit var qS: QS @Mock lateinit var scrimController: ScrimController - @Mock lateinit var falsingManager: FalsingManager @Mock lateinit var shadeViewController: ShadeViewController - @Mock lateinit var nsslController: NotificationStackScrollLayoutController - @Mock lateinit var depthController: NotificationShadeDepthController @Mock lateinit var stackscroller: NotificationStackScrollLayout - @Mock lateinit var expandHelperCallback: ExpandHelper.Callback - @Mock lateinit var mCentralSurfaces: CentralSurfaces - @Mock lateinit var qS: QS - @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller - @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller - @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController - @Mock lateinit var activityStarter: ActivityStarter + @Mock lateinit var statusbarStateController: SysuiStatusBarStateController @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val keyguardRepository = FakeKeyguardRepository() - private val configurationRepository = FakeConfigurationRepository() - private val sharedNotificationContainerInteractor = SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - private val shadeInteractor = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository = FakeUserSetupRepository(), - deviceProvisionedController = mock(), - userInteractor = mock(), - sharedNotificationContainerInteractor, - repository = FakeShadeRepository(), - ) - private val powerInteractor = PowerInteractorFactory.create().powerInteractor - @JvmField @Rule val mockito = MockitoJUnit.rule() - private val configurationController = FakeConfigurationController() + @JvmField @Rule val mockito = MockitoJUnit.rule() @Before fun setup() { - // By default, have the shade enabled - disableFlagsRepository.disableFlags.value = DisableFlagsModel() - testScope.runCurrent() - val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)) row = helper.createRow() context @@ -143,55 +103,9 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { context .getOrCreateTestableResources() .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100) - transitionController = - LockscreenShadeTransitionController( - statusBarStateController = statusbarStateController, - logger = logger, - keyguardBypassController = keyguardBypassController, - lockScreenUserManager = lockScreenUserManager, - falsingCollector = falsingCollector, - ambientState = ambientState, - mediaHierarchyManager = mediaHierarchyManager, - depthController = depthController, - wakefulnessLifecycle = wakefulnessLifecycle, - context = context, - configurationController = configurationController, - falsingManager = falsingManager, - dumpManager = dumpManager, - splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller }, - singleShadeOverScrollerFactory = { singleShadeOverScroller }, - scrimTransitionController = - LockscreenShadeScrimTransitionController( - scrimController, - context, - configurationController, - dumpManager, - ResourcesSplitShadeStateController() - ), - keyguardTransitionControllerFactory = { notificationPanelController -> - LockscreenShadeKeyguardTransitionController( - mediaHierarchyManager, - notificationPanelController, - context, - configurationController, - dumpManager, - ResourcesSplitShadeStateController() - ) - }, - qsTransitionControllerFactory = { qsTransitionController }, - activityStarter = activityStarter, - shadeRepository = FakeShadeRepository(), - shadeInteractor = shadeInteractor, - powerInteractor = powerInteractor, - splitShadeStateController = ResourcesSplitShadeStateController() - ) - transitionController.addCallback(transitionControllerCallback) + whenever(nsslController.view).thenReturn(stackscroller) whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback) - transitionController.shadeViewController = shadeViewController - transitionController.centralSurfaces = mCentralSurfaces - transitionController.qS = qS - transitionController.setStackScroller(nsslController) whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(nsslController.isInLockedDownShade).thenReturn(false) whenever(qS.isFullyCollapsed).thenReturn(true) @@ -199,9 +113,36 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { .thenReturn(true) whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true) whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true) - whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false) whenever(keyguardBypassController.bypassEnabled).thenReturn(false) - clearInvocations(mCentralSurfaces) + + testComponent = + DaggerLockscreenShadeTransitionControllerTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FULL_SCREEN_USER_SWITCHER, false) + }, + mocks = + TestMocksModule( + notificationShadeDepthController = depthController, + keyguardBypassController = keyguardBypassController, + mediaHierarchyManager = mediaHierarchyManager, + notificationLockscreenUserManager = lockScreenUserManager, + notificationStackScrollLayoutController = nsslController, + scrimController = scrimController, + statusBarStateController = statusbarStateController, + ) + ) + + transitionController.addCallback(transitionControllerCallback) + transitionController.shadeViewController = shadeViewController + transitionController.centralSurfaces = centralSurfaces + transitionController.qS = qS + transitionController.setStackScroller(nsslController) + clearInvocations(centralSurfaces) + + testScope.runCurrent() } @After @@ -282,7 +223,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.goToLockedShade(null) verify(statusbarStateController, never()).setState(anyInt()) verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true) - verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject()) + verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject()) } @Test @@ -318,7 +259,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(transitionControllerCallback, never()) .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong()) - verify(qsTransitionController, never()).dragDownAmount = anyFloat() + verify(qS, never()).setTransitionToFullShadeProgress(anyBoolean(), anyFloat(), anyFloat()) } @Test @@ -329,7 +270,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(transitionControllerCallback) .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong()) - verify(qsTransitionController).dragDownAmount = 10f + verify(qS).setTransitionToFullShadeProgress(eq(true), anyFloat(), anyFloat()) verify(depthController).transitionToFullShadeProgress = anyFloat() } @@ -532,8 +473,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.dragDownAmount = 10f - verify(singleShadeOverScroller).expansionDragDownAmount = 10f - verifyZeroInteractions(splitShadeOverScroller) + verify(nsslController).setOverScrollAmount(0) + verify(scrimController, never()).setNotificationsOverScrollAmount(anyInt()) } @Test @@ -542,8 +483,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.dragDownAmount = 10f - verify(splitShadeOverScroller).expansionDragDownAmount = 10f - verifyZeroInteractions(singleShadeOverScroller) + verify(nsslController).setOverScrollAmount(0) + verify(scrimController).setNotificationsOverScrollAmount(0) } @Test @@ -591,6 +532,32 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { progress: Float, lockScreenNotificationsProgress: Float ) { - scrimController.setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress) + setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress) + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val transitionController: LockscreenShadeTransitionController + + val configurationController: FakeConfigurationController + val disableFlagsRepository: FakeDisableFlagsRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + mocks: TestMocksModule, + ): TestComponent + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt index fbb8ebfb3e3b..20e5c43cba19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt @@ -29,9 +29,9 @@ import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.mockito.mock import org.junit.Before import org.junit.Test @@ -52,7 +52,7 @@ class PulseExpansionHandlerTest : SysuiTestCase() { private val collapsedHeight = 300 private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock() private val bypassController: KeyguardBypassController = mock() - private val headsUpManager: HeadsUpManagerPhone = mock() + private val headsUpManager: HeadsUpManager = mock() private val roundnessManager: NotificationRoundnessManager = mock() private val configurationController: ConfigurationController = mock() private val statusBarStateController: StatusBarStateController = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt index d86f8bbbeb15..235ac5c2e9cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt @@ -8,15 +8,15 @@ import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.res.R import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone +import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpUtil -import com.android.systemui.res.R import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import kotlinx.coroutines.test.TestScope @@ -37,7 +37,7 @@ import org.mockito.junit.MockitoJUnit @RunWithLooper class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { @Mock lateinit var notificationListContainer: NotificationListContainer - @Mock lateinit var headsUpManager: HeadsUpManagerPhone + @Mock lateinit var headsUpManager: HeadsUpManager @Mock lateinit var jankMonitor: InteractionJankMonitor @Mock lateinit var onFinishAnimationCallback: Runnable diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 257cc5b1b85c..4f1581cced91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -43,8 +43,8 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.NotificationGroupTestHelper -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -87,7 +87,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private val notifPipeline: NotifPipeline = mock() private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true) - private val headsUpManager: HeadsUpManager = mock() + private val headsUpManager: HeadsUpManagerPhone = mock() private val headsUpViewBinder: HeadsUpViewBinder = mock() private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock() private val remoteInputManager: NotificationRemoteInputManager = mock() @@ -435,7 +435,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private fun addHUN(entry: NotificationEntry) { huns.add(entry) - whenever(headsUpManager.topEntry).thenReturn(entry) + whenever(headsUpManager.getTopEntry()).thenReturn(entry) onHeadsUpChangedListener.onHeadsUpStateChanged(entry, true) notifLifetimeExtender.cancelLifetimeExtension(entry) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt index fbd61f4fe3bd..546abd4ec79a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt @@ -23,7 +23,6 @@ import android.provider.Settings import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.advanceTimeBy import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -40,10 +39,10 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.mockito.any @@ -247,7 +246,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { unseenFilter.onCleanup() // THEN: The SeenNotificationProvider has been updated to reflect the suppression - assertThat(seenNotificationsProvider.hasFilteredOutSeenNotifications).isTrue() + assertThat(notificationListInteractor.hasFilteredOutSeenNotifications.value).isTrue() } } @@ -598,7 +597,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { FakeSettings().apply { putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1) } - val seenNotificationsProvider = SeenNotificationsProviderImpl() + val notificationListInteractor = NotificationListInteractor(NotificationListRepository()) val keyguardCoordinator = KeyguardCoordinator( testDispatcher, @@ -611,7 +610,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { testScope.backgroundScope, sectionHeaderVisibilityProvider, fakeSettings, - seenNotificationsProvider, + notificationListInteractor, statusBarStateController, ) keyguardCoordinator.attach(notifPipeline) @@ -619,7 +618,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { KeyguardCoordinatorTestScope( keyguardCoordinator, testScope, - seenNotificationsProvider, + notificationListInteractor, fakeSettings, ) .testBlock() @@ -629,7 +628,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() { private inner class KeyguardCoordinatorTestScope( private val keyguardCoordinator: KeyguardCoordinator, private val scope: TestScope, - val seenNotificationsProvider: SeenNotificationsProvider, + val notificationListInteractor: NotificationListInteractor, private val fakeSettings: FakeSettings, ) : CoroutineScope by scope { val testScheduler: TestCoroutineScheduler diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt new file mode 100644 index 000000000000..2f8f3bb14ee5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.data.repository + +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() { + + private val testComponent: TestComponent = + DaggerNotificationsKeyguardViewStateRepositoryTest_TestComponent.factory() + .create(test = this) + + @Test + fun areNotifsFullyHidden_reflectsWakeUpCoordinator() = + with(testComponent) { + testScope.runTest { + whenever(mockWakeUpCoordinator.notificationsFullyHidden).thenReturn(false) + val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden) + runCurrent() + + assertThat(notifsFullyHidden).isFalse() + + withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) } + .onFullyHiddenChanged(true) + runCurrent() + + assertThat(notifsFullyHidden).isTrue() + } + } + + @Test + fun isPulseExpanding_reflectsWakeUpCoordinator() = + with(testComponent) { + testScope.runTest { + whenever(mockWakeUpCoordinator.isPulseExpanding()).thenReturn(false) + val isPulseExpanding by collectLastValue(underTest.isPulseExpanding) + runCurrent() + + assertThat(isPulseExpanding).isFalse() + + withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) } + .onPulseExpansionChanged(true) + runCurrent() + + assertThat(isPulseExpanding).isTrue() + } + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationsKeyguardViewStateRepositoryImpl + + val mockWakeUpCoordinator: NotificationWakeUpCoordinator + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt new file mode 100644 index 000000000000..705a5a3f9792 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.android.systemui.statusbar.notification.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@SmallTest +class NotificationsKeyguardInteractorTest : SysuiTestCase() { + + private val testComponent: TestComponent = + DaggerNotificationsKeyguardInteractorTest_TestComponent.factory().create(test = this) + + @Test + fun areNotifsFullyHidden_reflectsRepository() = + with(testComponent) { + testScope.runTest { + repository.setNotificationsFullyHidden(false) + val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden) + runCurrent() + + assertThat(notifsFullyHidden).isFalse() + + repository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(notifsFullyHidden).isTrue() + } + } + + @Test + fun isPulseExpanding_reflectsRepository() = + with(testComponent) { + testScope.runTest { + repository.setPulseExpanding(false) + val isPulseExpanding by collectLastValue(underTest.isPulseExpanding) + runCurrent() + + assertThat(isPulseExpanding).isFalse() + + repository.setPulseExpanding(true) + runCurrent() + + assertThat(isPulseExpanding).isTrue() + } + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationsKeyguardInteractor + + val repository: FakeNotificationsKeyguardViewStateRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create(@BindsInstance test: SysuiTestCase): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt index 7caa5ccc5837..e57986ddfa18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt @@ -26,9 +26,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.user.domain.UserDomainLayerModule -import com.android.systemui.util.mockito.whenever import dagger.BindsInstance import dagger.Component import org.junit.Assert.assertFalse @@ -37,7 +35,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @@ -46,7 +43,6 @@ import org.mockito.MockitoAnnotations class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() { @Mock private lateinit var dozeParams: DozeParameters - @Mock private lateinit var aodIcons: NotificationIconContainer private lateinit var testComponent: TestComponent private val underTest @@ -85,15 +81,6 @@ class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() assertTrue(underTest.shouldShowLowPriorityIcons()) } - @Test - fun testAppearResetsTranslation() { - underTest.setupAodIcons(aodIcons) - whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) - underTest.appearAodIcons() - verify(aodIcons).translationY = 0f - verify(aodIcons).alpha = 1.0f - } - @SysUISingleton @Component( modules = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt new file mode 100644 index 000000000000..31efebbc5b60 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeTransitionModel +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository +import com.android.systemui.user.domain.UserDomainLayerModule +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.ui.AnimatedValue +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { + + @Mock private lateinit var dozeParams: DozeParameters + @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController + + private lateinit var testComponent: TestComponent + private val underTest: NotificationIconContainerAlwaysOnDisplayViewModel + get() = testComponent.underTest + private val deviceEntryRepository: FakeDeviceEntryRepository + get() = testComponent.deviceEntryRepository + private val deviceProvisioningRepository: FakeDeviceProvisioningRepository + get() = testComponent.deviceProvisioningRepository + private val keyguardRepository: FakeKeyguardRepository + get() = testComponent.keyguardRepository + private val keyguardTransitionRepository: FakeKeyguardTransitionRepository + get() = testComponent.keyguardTransitionRepository + private val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository + get() = testComponent.notifsKeyguardRepository + private val powerRepository: FakePowerRepository + get() = testComponent.powerRepository + private val scope: TestScope + get() = testComponent.scope + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + testComponent = + DaggerNotificationIconContainerAlwaysOnDisplayViewModelTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + setDefault(Flags.FACE_AUTH_REFACTOR) + set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) + setDefault(Flags.NEW_AOD_TRANSITION) + }, + mocks = + TestMocksModule( + dozeParameters = dozeParams, + screenOffAnimationController = screenOffAnimController, + ), + ) + + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + deviceProvisioningRepository.setFactoryResetProtectionActive(false) + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + } + + @Test + fun animationsEnabled_isFalse_whenFrpIsActive() = + scope.runTest { + deviceProvisioningRepository.setFactoryResetProtectionActive(true) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_AOD, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_PULSING, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(true) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenNotAsleep() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenKeyguardIsShowing() = + scope.runTest { + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + runCurrent() + + assertThat(animationsEnabled).isTrue() + + keyguardRepository.setKeyguardOccluded(true) + runCurrent() + + assertThat(animationsEnabled).isFalse() + + keyguardRepository.setKeyguardShowing(false) + keyguardRepository.setKeyguardOccluded(true) + runCurrent() + + assertThat(animationsEnabled).isFalse() + } + + @Test + fun isDozing_startAodTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isDozing_startDozeTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.DOZING, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = false)) + } + + @Test + fun isDozing_startDozeToAodTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.DOZING, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isNotDozing_startAodToGoneTransition() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + assertThat(isDozing).isEqualTo(AnimatedValue(false, isAnimating = true)) + } + + @Test + fun isDozing_stopAnimation() = + scope.runTest { + val isDozing by collectLastValue(underTest.isDozing) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + + underTest.completeDozeAnimation() + runCurrent() + + assertThat(isDozing?.isAnimating).isEqualTo(false) + } + + @Test + fun isNotVisible_pulseExpanding() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(true) + runCurrent() + + assertThat(isVisible?.value).isFalse() + } + + @Test + fun isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + to = KeyguardState.GONE, + transitionState = TransitionState.FINISHED, + ) + ) + whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(false, isAnimating = false)) + } + + @Test + fun isVisible_bypassEnabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + deviceEntryRepository.setBypassEnabled(true) + runCurrent() + + assertThat(isVisible?.value).isTrue() + } + + @Test + fun isNotVisible_pulseExpanding_notBypassing() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(true) + deviceEntryRepository.setBypassEnabled(false) + runCurrent() + + assertThat(isVisible?.value).isEqualTo(false) + } + + @Test + fun isVisible_notifsFullyHidden_bypassEnabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + runCurrent() + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(true) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(false) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false)) + } + + @Test + fun isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(true) + whenever(dozeParams.displayNeedsBlanking).thenReturn(true) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false)) + } + + @Test + fun isVisible_notifsFullyHidden_bypassDisabled() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + runCurrent() + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(true) + whenever(dozeParams.displayNeedsBlanking).thenReturn(false) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true)) + } + + @Test + fun isVisible_stopAnimation() = + scope.runTest { + val isVisible by collectLastValue(underTest.isVisible) + notifsKeyguardRepository.setPulseExpanding(false) + deviceEntryRepository.setBypassEnabled(false) + whenever(dozeParams.alwaysOn).thenReturn(true) + whenever(dozeParams.displayNeedsBlanking).thenReturn(false) + notifsKeyguardRepository.setNotificationsFullyHidden(true) + runCurrent() + + underTest.completeVisibilityAnimation() + runCurrent() + + assertThat(isVisible?.isAnimating).isEqualTo(false) + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationIconContainerAlwaysOnDisplayViewModel + + val deviceEntryRepository: FakeDeviceEntryRepository + val deviceProvisioningRepository: FakeDeviceProvisioningRepository + val keyguardRepository: FakeKeyguardRepository + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository + val powerRepository: FakePowerRepository + val scope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + mocks: TestMocksModule, + featureFlags: FakeFeatureFlagsClassicModule, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt new file mode 100644 index 000000000000..e1e7f92265f0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeTransitionModel +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository +import com.android.systemui.user.domain.UserDomainLayerModule +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { + + @Mock lateinit var dozeParams: DozeParameters + + private lateinit var testComponent: TestComponent + private val underTest: NotificationIconContainerStatusBarViewModel + get() = testComponent.underTest + private val deviceProvisioningRepository + get() = testComponent.deviceProvisioningRepository + private val keyguardTransitionRepository + get() = testComponent.keyguardTransitionRepository + private val keyguardRepository + get() = testComponent.keyguardRepository + private val powerRepository + get() = testComponent.powerRepository + private val scope + get() = testComponent.scope + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + testComponent = + DaggerNotificationIconContainerStatusBarViewModelTest_TestComponent.factory() + .create( + test = this, + // Configurable bindings + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FACE_AUTH_REFACTOR, value = false) + set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) + }, + mocks = + TestMocksModule( + dozeParameters = dozeParams, + ), + ) + + keyguardRepository.setKeyguardShowing(false) + deviceProvisioningRepository.setFactoryResetProtectionActive(false) + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + } + + @Test + fun animationsEnabled_isFalse_whenFrpIsActive() = + scope.runTest { + deviceProvisioningRepository.setFactoryResetProtectionActive(true) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_AOD, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_PULSING, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } + + @Test + fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(true) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenNotAsleep() = + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } + + @Test + fun animationsEnabled_isTrue_whenKeyguardIsNotShowing() = + scope.runTest { + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) + ) + keyguardRepository.setKeyguardShowing(true) + runCurrent() + + assertThat(animationsEnabled).isFalse() + + keyguardRepository.setKeyguardShowing(false) + runCurrent() + + assertThat(animationsEnabled).isTrue() + } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + // Real impls + BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: NotificationIconContainerStatusBarViewModel + + val deviceProvisioningRepository: FakeDeviceProvisioningRepository + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + val keyguardRepository: FakeKeyguardRepository + val powerRepository: FakePowerRepository + val scope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + mocks: TestMocksModule, + featureFlags: FakeFeatureFlagsClassicModule, + ): TestComponent + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java index c9b77c5372df..9c20e541e35f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java @@ -54,9 +54,9 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -132,7 +132,8 @@ public class FeedbackInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), - mMockNotificationRow, mAssistantFeedbackController); + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, + mNotificationGutsManager); final TextView textView = mFeedbackInfo.findViewById(R.id.pkg_name); assertTrue(textView.getText().toString().contains("App Name")); } @@ -143,7 +144,8 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), - mMockNotificationRow, mAssistantFeedbackController); + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, + mNotificationGutsManager); final ImageView iconView = mFeedbackInfo.findViewById(R.id.pkg_icon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -153,7 +155,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_SILENCED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically demoted to Silent by the system. " + "Let the developer know your feedback. Was this correct?", @@ -165,7 +167,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_PROMOTED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically ranked higher in your shade. " + "Let the developer know your feedback. Was this correct?", @@ -177,7 +179,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_ALERTED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically promoted to Default by the system. " + "Let the developer know your feedback. Was this correct?", @@ -189,7 +191,7 @@ public class FeedbackInfoTest extends SysuiTestCase { when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) .thenReturn(STATUS_DEMOTED); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically ranked lower in your shade. " + "Let the developer know your feedback. Was this correct?", @@ -199,7 +201,7 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testPositiveFeedback() { mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final View yes = mFeedbackInfo.findViewById(R.id.yes); yes.performClick(); @@ -216,7 +218,7 @@ public class FeedbackInfoTest extends SysuiTestCase { .thenReturn(true); mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, - mAssistantFeedbackController); + mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final View no = mFeedbackInfo.findViewById(R.id.no); no.performClick(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 9f2afdf1b168..8a730cfd7ddd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -33,7 +33,6 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -89,8 +88,8 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; @@ -152,7 +151,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private AssistantFeedbackController mAssistantFeedbackController; @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; @Mock private StatusBarStateController mStatusBarStateController; - @Mock private HeadsUpManagerPhone mHeadsUpManagerPhone; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private ActivityStarter mActivityStarter; @Mock private UserManager mUserManager; @@ -171,7 +170,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mTestScope.getBackgroundScope(), new WindowRootViewVisibilityRepository(mBarService, mExecutor), new FakeKeyguardRepository(), - mHeadsUpManagerPhone, + mHeadsUpManager, PowerInteractorFactory.create().getPowerInteractor()); mGutsManager = new NotificationGutsManager( @@ -196,9 +195,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mWindowRootViewVisibilityInteractor, mNotificationLockscreenUserManager, mStatusBarStateController, + mBarService, mDeviceProvisionedController, mMetricsLogger, - mHeadsUpManagerPhone, + mHeadsUpManager, mActivityStarter); mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer, mOnSettingsClickListener); @@ -239,7 +239,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { anyInt(), anyBoolean(), any(Runnable.class)); - verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), true); + verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), true); assertEquals(View.VISIBLE, guts.getVisibility()); mGutsManager.closeAndSaveGuts(false, false, true, 0, 0, false); @@ -247,7 +247,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean()); verify(row, times(1)).setGutsView(any()); mTestableLooper.processAllMessages(); - verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), false); + verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), false); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index ac680e6c902e..cb731082b89b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -61,6 +61,7 @@ import com.android.systemui.flags.Flags; import com.android.systemui.media.controls.util.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -81,14 +82,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; -import com.android.systemui.res.R; import com.android.systemui.wmshell.BubblesManager; import com.android.systemui.wmshell.BubblesTestActivity; @@ -123,7 +123,7 @@ public class NotificationTestHelper { private final GroupMembershipManager mGroupMembershipManager; private final GroupExpansionManager mGroupExpansionManager; private ExpandableNotificationRow mRow; - private final HeadsUpManagerPhone mHeadsUpManager; + private final HeadsUpManager mHeadsUpManager; private final NotifBindPipeline mBindPipeline; private final NotifCollectionListener mBindPipelineEntryListener; private final RowContentBindStage mBindStage; @@ -161,7 +161,7 @@ public class NotificationTestHelper { mKeyguardBypassController = mock(KeyguardBypassController.class); mGroupMembershipManager = mock(GroupMembershipManager.class); mGroupExpansionManager = mock(GroupExpansionManager.class); - mHeadsUpManager = mock(HeadsUpManagerPhone.class); + mHeadsUpManager = mock(HeadsUpManager.class); mIconManager = new IconManager( mock(CommonNotifCollection.class), mock(LauncherApps.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index ffe312be8fae..20197e3ed547 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -77,7 +77,6 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; -import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.NotifStats; @@ -88,13 +87,15 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent; import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback; +import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository; +import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; @@ -124,7 +125,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private NotificationGutsManager mNotificationGutsManager; @Mock private NotificationsController mNotificationsController; @Mock private NotificationVisibilityProvider mVisibilityProvider; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private TunerService mTunerService; @Mock private DeviceProvisionedController mDeviceProvisionedController; @@ -170,8 +171,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; - private final SeenNotificationsProviderImpl mSeenNotificationsProvider = - new SeenNotificationsProviderImpl(); + private final NotificationListInteractor mNotificationListInteractor = + new NotificationListInteractor(new NotificationListRepository()); private NotificationStackScrollLayoutController mController; @@ -503,7 +504,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Test public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() { initController(/* viewIsAttached= */ true); - mSeenNotificationsProvider.setHasFilteredOutSeenNotifications(true); + mNotificationListInteractor.setHasFilteredOutSeenNotifications(true); mController.getNotifStackController().setNotifStats(NotifStats.getEmpty()); verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true); verify(mNotificationStackScrollLayout).updateFooter(); @@ -703,7 +704,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mUiEventLogger, mRemoteInputManager, mVisibilityLocationProviderDelegator, - mSeenNotificationsProvider, + mNotificationListInteractor, mShadeController, mJankMonitor, mStackLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index e254dd085b2e..ac11ff29b83a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -19,36 +19,35 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R +import com.android.SysUITestModule +import com.android.TestMocksModule import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.SharedNotificationContainerPosition import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FakeFeatureFlagsClassicModule +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.res.R import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController -import com.android.systemui.user.domain.interactor.UserInteractor +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -60,29 +59,27 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class SharedNotificationContainerViewModelTest : SysuiTestCase() { - private val utils = SceneTestUtils(this) - - private val testScope = utils.testScope - - private val disableFlagsRepository = FakeDisableFlagsRepository() - private val userSetupRepository = FakeUserSetupRepository() - private val shadeRepository = FakeShadeRepository() - private val keyguardRepository = FakeKeyguardRepository() - private val sceneContainerFlags = FakeSceneContainerFlags() - private val sceneInteractor = utils.sceneInteractor() - - private lateinit var configurationRepository: FakeConfigurationRepository - private lateinit var sharedNotificationContainerInteractor: - SharedNotificationContainerInteractor - private lateinit var underTest: SharedNotificationContainerViewModel - private lateinit var keyguardInteractor: KeyguardInteractor - private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor - private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository - private lateinit var shadeInteractor: ShadeInteractor + + private lateinit var testComponent: TestComponent + + private val shadeRepository + get() = testComponent.shadeRepository + private val keyguardRepository + get() = testComponent.keyguardRepository + private val configurationRepository + get() = testComponent.configurationRepository + private val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor + get() = testComponent.sharedNotificationContainerInteractor + private val underTest: SharedNotificationContainerViewModel + get() = testComponent.underTest + private val keyguardInteractor: KeyguardInteractor + get() = testComponent.keyguardInteractor + private val keyguardTransitionRepository + get() = testComponent.keyguardTransitionRepository + private val testScope + get() = testComponent.testScope @Mock private lateinit var notificationStackSizeCalculator: NotificationStackSizeCalculator - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - @Mock private lateinit var userInteractor: UserInteractor @Mock private lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController @@ -94,43 +91,21 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { whenever(notificationStackScrollLayoutController.getView()).thenReturn(mock()) whenever(notificationStackScrollLayoutController.getShelfHeight()).thenReturn(0) - configurationRepository = FakeConfigurationRepository() - KeyguardTransitionInteractorFactory.create( - scope = testScope.backgroundScope, - ) - .also { - keyguardInteractor = it.keyguardInteractor - keyguardTransitionInteractor = it.keyguardTransitionInteractor - keyguardTransitionRepository = it.repository - } - sharedNotificationContainerInteractor = - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - shadeInteractor = - ShadeInteractor( - testScope.backgroundScope, - disableFlagsRepository, - sceneContainerFlags, - { sceneInteractor }, - keyguardRepository, - userSetupRepository, - deviceProvisionedController, - userInteractor, - sharedNotificationContainerInteractor, - shadeRepository, - ) - underTest = - SharedNotificationContainerViewModel( - sharedNotificationContainerInteractor, - keyguardInteractor, - keyguardTransitionInteractor, - notificationStackSizeCalculator, - notificationStackScrollLayoutController, - shadeInteractor - ) + testComponent = + DaggerSharedNotificationContainerViewModelTest_TestComponent.factory() + .create( + test = this, + featureFlags = + FakeFeatureFlagsClassicModule { + set(Flags.FULL_SCREEN_USER_SWITCHER, true) + }, + mocks = + TestMocksModule( + notificationStackSizeCalculator = notificationStackSizeCalculator, + notificationStackScrollLayoutController = + notificationStackScrollLayoutController, + ) + ) } @Test @@ -404,4 +379,34 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { ) ) } + + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + UserDomainLayerModule::class, + ] + ) + interface TestComponent { + + val underTest: SharedNotificationContainerViewModel + + val configurationRepository: FakeConfigurationRepository + val keyguardRepository: FakeKeyguardRepository + val keyguardInteractor: KeyguardInteractor + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + val shadeRepository: FakeShadeRepository + val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + featureFlags: FakeFeatureFlagsClassicModule, + mocks: TestMocksModule, + ): TestComponent + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index a5d348404240..e7dad6a2908f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -57,6 +57,7 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; @@ -85,7 +86,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { private final MetricsLogger mMetricsLogger = new FakeMetricsLogger(); @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private KeyguardStateController mKeyguardStateController; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private WakefulnessLifecycle mWakefulnessLifecycle; @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index e33fa22bfc0c..f18af61dd314 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -85,7 +85,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.TestScopeProvider; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -117,6 +116,7 @@ import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.brightness.BrightnessSliderController; @@ -219,7 +219,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private KeyguardIndicationController mKeyguardIndicationController; @Mock private NotificationStackScrollLayout mStackScroller; @Mock private NotificationStackScrollLayoutController mStackScrollerController; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private NotificationPanelViewController mNotificationPanelViewController; @Mock private ShadeLogger mShadeLogger; @Mock private NotificationPanelView mNotificationPanelView; @@ -336,6 +336,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false); // Set default value to avoid IllegalStateException. mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false); + mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR); // For the Shade to respond to Back gesture, we must enable the event routing mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true); // For the Shade to animate during the Back gesture, we must enable the animation flag. @@ -343,6 +344,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true); // Turn AOD on and toggle feature flag for jank fixes mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false); when(mDozeParameters.getAlwaysOn()).thenReturn(true); IThermalService thermalService = mock(IThermalService.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index ff6f40d539fc..593c587c0f51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import org.junit.Before; import org.junit.Test; @@ -72,7 +73,7 @@ public class DozeServiceHostTest extends SysuiTestCase { private DozeServiceHost mDozeServiceHost; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private ScrimController mScrimController; @Mock private DozeScrimController mDozeScrimController; @Mock private StatusBarStateControllerImpl mStatusBarStateController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index ec6286b66ed2..d84bb728a856 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -72,7 +73,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow; private NotificationEntry mEntry; private HeadsUpStatusBarView mHeadsUpStatusBarView; - private HeadsUpManagerPhone mHeadsUpManager; + private HeadsUpManager mHeadsUpManager; private View mOperatorNameView; private StatusBarStateController mStatusbarStateController; private PhoneStatusBarTransitions mPhoneStatusBarTransitions; @@ -93,7 +94,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mEntry = mRow.getEntry(); mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class), mock(TextView.class)); - mHeadsUpManager = mock(HeadsUpManagerPhone.class); + mHeadsUpManager = mock(HeadsUpManager.class); mOperatorNameView = new View(mContext); mStatusbarStateController = mock(StatusBarStateController.class); mPhoneStatusBarTransitions = mock(PhoneStatusBarTransitions.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 1bc522d72213..cda2a74609bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -32,8 +32,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; -import com.android.systemui.res.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.collection.provider.VisualSta import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import org.junit.After; @@ -71,7 +72,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper; @Mock private ShadeExpansionStateManager mShadeExpansionStateManager; @Mock private UiEventLogger mUiEventLogger; - private boolean mLivesPastNormalTime; private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone { TestableHeadsUpManagerPhone( @@ -149,7 +149,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testSnooze() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry entry = createEntry(/* id = */ 0); hmp.showNotification(entry); @@ -160,7 +160,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testSwipedOutNotification() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry entry = createEntry(/* id = */ 0); hmp.showNotification(entry); @@ -176,7 +176,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testCanRemoveImmediately_swipedOut() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry entry = createEntry(/* id = */ 0); hmp.showNotification(entry); @@ -189,7 +189,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Ignore("b/141538055") @Test public void testCanRemoveImmediately_notTopEntry() { - final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone(); + final HeadsUpManager hmp = createHeadsUpManagerPhone(); final NotificationEntry earlierEntry = createEntry(/* id = */ 0); final NotificationEntry laterEntry = createEntry(/* id = */ 1); laterEntry.setRow(mRow); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index bac857910329..b36d09df5929 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -18,6 +18,9 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE; + +import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -33,7 +36,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher; import android.service.trust.TrustAgentService; import android.testing.AndroidTestingRunner; @@ -175,6 +177,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false); mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, true); mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false); + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false); when(mNotificationShadeWindowController.getWindowRootView()) .thenReturn(mNotificationShadeWindowView); @@ -761,6 +764,30 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() { + mStatusBarKeyguardViewManager.addCallback(mCallback); + + // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + + // THEN the touch is not acted upon + verify(mCallback, never()).onTouch(any()); + } + + @Test + public void onInterceptTouch_alternateBouncerViewFlagEnabled() { + // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible + mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + + // THEN the touch is not intercepted + assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( + MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + )); + } + + @Test public void handleDispatchTouchEvent_alternateBouncerNotVisible() { mStatusBarKeyguardViewManager.addCallback(mCallback); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 8013e5eef44b..beac995c893b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -92,6 +92,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -218,7 +219,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mScreenOffAnimationController, mStatusBarStateController).getPowerInteractor(); - HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class); + HeadsUpManager headsUpManager = mock(HeadsUpManager.class); NotificationLaunchAnimatorControllerProvider notificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( new NotificationExpansionRepository(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 233f407813b2..ee4f2089c05c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -15,6 +15,7 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -59,6 +60,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -106,7 +108,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mContext, shadeViewController, mock(QuickSettingsController.class), - mock(HeadsUpManagerPhone.class), + mock(HeadsUpManager.class), notificationShadeWindowView, mock(ActivityStarter.class), stackScrollLayoutController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt index dbaa29bb3688..d06a6e26b4ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt @@ -20,7 +20,6 @@ import android.net.ConnectivityManager import android.net.wifi.WifiManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.demomode.DemoMode import com.android.systemui.demomode.DemoModeController @@ -53,7 +52,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiRepositorySwitcherTest : SysuiTestCase() { private lateinit var underTest: WifiRepositorySwitcher diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt index 206ac1d37074..ce00250467f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel @@ -28,7 +27,6 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class DisabledWifiRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index c2e75aa85fcb..cf20ba87e8c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -35,7 +35,6 @@ import android.net.wifi.WifiManager.UNKNOWN_SSID import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.table.TableLogBuffer @@ -73,7 +72,6 @@ import org.mockito.MockitoAnnotations @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiRepositoryImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt index 1db80651bf9b..7fbbfc77300e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor import android.net.wifi.WifiManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot @@ -43,7 +42,6 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiInteractorImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt index 49a2648a7cac..2d1a27f1b133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION -import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription import com.android.systemui.coroutines.collectLastValue @@ -53,7 +52,6 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RoboPilotTest @RunWith(AndroidJUnit4::class) class WifiViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index 64ebcd9e3abf..4f3f56423eb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -41,10 +41,13 @@ import android.app.PendingIntent; import android.app.Person; import android.content.Context; import android.content.Intent; +import android.graphics.Region; import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; @@ -73,7 +76,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer())); @Mock private AccessibilityManagerWrapper mAccessibilityMgr; - private final class TestableHeadsUpManager extends HeadsUpManager { + private final class TestableHeadsUpManager extends BaseHeadsUpManager { TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger, Handler handler, @@ -85,9 +88,78 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME; } + + // The following are only implemented by HeadsUpManagerPhone. If you need them, use that. + @Override + public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void addSwipedOutNotification(@NonNull String key) { + throw new UnsupportedOperationException(); + } + + @Override + public void extendHeadsUp() { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public Region getTouchableRegion() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isHeadsUpGoingAway() { + throw new UnsupportedOperationException(); + } + + @Override + public void onExpandingFinished() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeNotification(@NonNull String key, boolean releaseImmediately, + boolean animate) { + throw new UnsupportedOperationException(); + } + + @Override + public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) { + throw new UnsupportedOperationException(); + } + + @Override + public void setHeadsUpGoingAway(boolean headsUpGoingAway) { + throw new UnsupportedOperationException(); + } + + @Override + public void setRemoteInputActive(@NonNull NotificationEntry entry, + boolean remoteInputActive) { + throw new UnsupportedOperationException(); + } + + @Override + public void setTrackingHeadsUp(boolean tracking) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean shouldSwallowClick(@NonNull String key) { + throw new UnsupportedOperationException(); + } } - private HeadsUpManager createHeadsUpManager() { + private BaseHeadsUpManager createHeadsUpManager() { return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mAccessibilityMgr, mUiEventLoggerFake); } @@ -165,9 +237,10 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testHunRemovedLogging() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); - final HeadsUpManager.HeadsUpEntry headsUpEntry = mock(HeadsUpManager.HeadsUpEntry.class); + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = mock( + BaseHeadsUpManager.HeadsUpEntry.class); headsUpEntry.mEntry = notifEntry; hum.onAlertEntryRemoved(headsUpEntry); @@ -177,35 +250,37 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0); // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); - headsUpEntry.wasUnpinned = false; + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); + headsUpEntry.mWasUnpinned = false; assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry)); } @Test public void testShouldHeadsUpBecomePinned_wasUnpinned_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0); // Add notifEntry to ANM mAlertEntries map and make it unpinned hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); - headsUpEntry.wasUnpinned = true; + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); + headsUpEntry.mWasUnpinned = true; assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry)); } @Test public void testShouldHeadsUpBecomePinned_noFSI_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); assertFalse(hum.shouldHeadsUpBecomePinned(entry)); @@ -214,7 +289,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -228,7 +303,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_autoDismissesWithDefaultTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -242,7 +317,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -256,7 +331,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_sticky_neverAutoDismisses() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -278,7 +353,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_autoDismissesWithAccessibilityTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(true); @@ -292,7 +367,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(true); @@ -306,7 +381,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testRemoveNotification_beforeMinimumDisplayTime() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -329,7 +404,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testRemoveNotification_afterMinimumDisplayTime() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -366,7 +441,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testRemoveNotification_releaseImmediately() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createEntry(/* id = */ 0); hum.showNotification(entry); @@ -382,14 +457,15 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_rowPinnedAndExpanded_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); when(mRow.isPinned()).thenReturn(true); notifEntry.setRow(mRow); hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); headsUpEntry.setExpanded(true); assertTrue(hum.isSticky(notifEntry.getKey())); @@ -397,20 +473,21 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_remoteInputActive_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); - headsUpEntry.remoteInputActive = true; + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); + headsUpEntry.mRemoteInputActive = true; assertTrue(hum.isSticky(notifEntry.getKey())); } @Test public void testIsSticky_hasFullScreenIntent_true() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0); hum.showNotification(notifEntry); @@ -421,7 +498,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_stickyForSomeTime_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); hum.showNotification(entry); @@ -432,21 +509,22 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testIsSticky_false() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); hum.showNotification(notifEntry); - final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey()); + final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + notifEntry.getKey()); headsUpEntry.setExpanded(false); - headsUpEntry.remoteInputActive = false; + headsUpEntry.mRemoteInputActive = false; assertFalse(hum.isSticky(notifEntry.getKey())); } @Test public void testCompareTo_withNullEntries() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); hum.showNotification(alertEntry); @@ -458,7 +536,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testCompareTo_withNonAlertEntries() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag( "nae1").build(); @@ -474,9 +552,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); - final HeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry(); ongoingCall.setEntry(new NotificationEntryBuilder() .setSbn(createSbn(/* id = */ 0, new Notification.Builder(mContext, "") @@ -484,9 +562,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { .setOngoing(true))) .build()); - final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); activeRemoteInput.setEntry(createEntry(/* id = */ 1)); - activeRemoteInput.remoteInputActive = true; + activeRemoteInput.mRemoteInputActive = true; assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0); assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0); @@ -494,9 +572,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); - final HeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry(); final Person person = new Person.Builder().setName("person").build(); final PendingIntent intent = mock(PendingIntent.class); incomingCall.setEntry(new NotificationEntryBuilder() @@ -506,9 +584,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { .forIncomingCall(person, intent, intent)))) .build()); - final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); activeRemoteInput.setEntry(createEntry(/* id = */ 1)); - activeRemoteInput.remoteInputActive = true; + activeRemoteInput.mRemoteInputActive = true; assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0); assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0); @@ -516,10 +594,10 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Test public void testPinEntry_logsPeek() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); // Needs full screen intent in order to be pinned - final HeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(); + final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(); entryToPin.setEntry(createFullScreenIntentEntry(/* id = */ 0)); // Note: the standard way to show a notification would be calling showNotification rather @@ -530,13 +608,13 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { hum.onAlertEntryAdded(entryToPin); assertEquals(1, mUiEventLoggerFake.numLogs()); - assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), + assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(0)); } @Test public void testSetUserActionMayIndirectlyRemove() { - final HeadsUpManager hum = createHeadsUpManager(); + final BaseHeadsUpManager hum = createHeadsUpManager(); final NotificationEntry notifEntry = createEntry(/* id = */ 0); hum.showNotification(notifEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt new file mode 100644 index 000000000000..12694ae998c1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.policy.data.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { + + @Mock lateinit var deviceProvisionedController: DeviceProvisionedController + + lateinit var underTest: DeviceProvisioningRepositoryImpl + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = + DeviceProvisioningRepositoryImpl( + deviceProvisionedController, + ) + } + + @Test + fun isDeviceProvisioned_reflectsCurrentControllerState() = runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned) + assertThat(deviceProvisioned).isTrue() + } + + @Test + fun isDeviceProvisioned_updatesWhenControllerStateChanges_toTrue() = runTest { + val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned) + runCurrent() + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onDeviceProvisionedChanged() + assertThat(deviceProvisioned).isTrue() + } + + @Test + fun isDeviceProvisioned_updatesWhenControllerStateChanges_toFalse() = runTest { + val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned) + runCurrent() + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onDeviceProvisionedChanged() + assertThat(deviceProvisioned).isFalse() + } + + @Test + fun isFrpActive_reflectsCurrentControllerState() = runTest { + whenever(deviceProvisionedController.isFrpActive).thenReturn(true) + val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive) + assertThat(frpActive).isTrue() + } + + @Test + fun isFrpActive_updatesWhenControllerStateChanges_toTrue() = runTest { + val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive) + runCurrent() + whenever(deviceProvisionedController.isFrpActive).thenReturn(true) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onFrpActiveChanged() + assertThat(frpActive).isTrue() + } + + @Test + fun isFrpActive_updatesWhenControllerStateChanges_toFalse() = runTest { + val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive) + runCurrent() + whenever(deviceProvisionedController.isFrpActive).thenReturn(false) + withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } + .onFrpActiveChanged() + assertThat(frpActive).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt index af941d03f191..c56266dde752 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt @@ -155,6 +155,9 @@ class UserInteractorTest : SysuiTestCase() { @Test fun createUserInteractor_nonProcessUser_startsSecondaryService() { + val userId = Process.myUserHandle().identifier + 1 + whenever(manager.aliveUsers).thenReturn(listOf(createUserInfo(userId, "abc"))) + createUserInteractor(false /* startAsProcessUser */) verify(spyContext).startServiceAsUser(any(), any()) } @@ -655,9 +658,10 @@ class UserInteractorTest : SysuiTestCase() { @Test fun userSwitchedBroadcast() { - createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) + whenever(manager.aliveUsers).thenReturn(userInfos) + createUserInteractor() userRepository.setUserInfos(userInfos) userRepository.setSelectedUserInfo(userInfos[0]) userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) @@ -728,6 +732,26 @@ class UserInteractorTest : SysuiTestCase() { } @Test + fun localeChanged_refreshUsers() { + createUserInteractor() + testScope.runTest { + val userInfos = createUserInfos(count = 2, includeGuest = false) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + runCurrent() + val refreshUsersCallCount = userRepository.refreshUsersCallCount + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + spyContext, + Intent(Intent.ACTION_LOCALE_CHANGED) + ) + runCurrent() + + assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1) + } + } + + @Test fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() { createUserInteractor() testScope.runTest { @@ -985,6 +1009,13 @@ class UserInteractorTest : SysuiTestCase() { } } + @Test + fun initWithNoAliveUsers() { + whenever(manager.aliveUsers).thenReturn(listOf()) + createUserInteractor() + verify(spyContext, never()).startServiceAsUser(any(), any()) + } + private fun assertUsers( models: List<UserModel>?, count: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index 6932f5ed4b30..c236b12d723f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Text +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -347,6 +348,24 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test + fun isFinishRequested_finishesWhenUserButtonIsClicked() = + testScope.runTest { + setUsers(count = 2) + val isFinishRequested = mutableListOf<Boolean>() + val job = + launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } + + val userViewModels = collectLastValue(underTest.users) + assertThat(isFinishRequested.last()).isFalse() + + userViewModels.invoke()?.firstOrNull()?.onClicked?.invoke() + + assertThat(isFinishRequested.last()).isTrue() + + job.cancel() + } + + @Test fun guestSelected_nameIsExitGuest() = testScope.runTest { val userInfos = diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt new file mode 100644 index 000000000000..aaf8d0761dce --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.ui + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class AnimatedValueTest : SysuiTestCase() { + + @Test + fun animatableEvent_updatesValue() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = false)) + + assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false)) + } + + @Test + fun animatableEvent_startAnimation() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = true)) + + assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = true)) + } + + @Test + fun animatableEvent_startAnimation_alreadyAnimating() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = true)) + events.emit(AnimatableEvent(value = 2, startAnimating = true)) + + assertThat(value).isEqualTo(AnimatedValue(value = 2, isAnimating = true)) + } + + @Test + fun animatedValue_stopAnimating() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val stopEvent = MutableSharedFlow<Unit>() + val values = events.toAnimatedValueFlow(completionEvents = stopEvent) + val value by collectLastValue(values) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = true)) + stopEvent.emit(Unit) + + assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false)) + } + + @Test + fun animatedValue_stopAnimating_notAnimating() = runTest { + val events = MutableSharedFlow<AnimatableEvent<Int>>() + val stopEvent = MutableSharedFlow<Unit>() + val values = events.toAnimatedValueFlow(completionEvents = stopEvent) + values.launchIn(backgroundScope) + runCurrent() + + events.emit(AnimatableEvent(value = 1, startAnimating = false)) + + assertThat(stopEvent.subscriptionCount.value).isEqualTo(0) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 409ba4801d0d..c8327029026d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -86,20 +86,36 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; +import com.android.keyguard.KeyguardSecurityModel; import com.android.launcher3.icons.BubbleIconFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.data.repository.FakeCommandQueue; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.scene.FakeWindowRootViewComponent; import com.android.systemui.scene.SceneTestUtils; +import com.android.systemui.scene.data.repository.SceneContainerRepository; +import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -139,6 +155,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.user.domain.interactor.UserInteractor; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; @@ -329,6 +346,8 @@ public class BubblesTest extends SysuiTestCase { private UserHandle mUser0; private FakeBubbleProperties mBubbleProperties; + private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor; + private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor; @Before public void setUp() throws Exception { @@ -350,21 +369,94 @@ public class BubblesTest extends SysuiTestCase { when(mNotificationShadeWindowView.getViewTreeObserver()) .thenReturn(mock(ViewTreeObserver.class)); - mShadeInteractor = new ShadeInteractor( + + FakeDeviceProvisioningRepository deviceProvisioningRepository = + new FakeDeviceProvisioningRepository(); + deviceProvisioningRepository.setDeviceProvisioned(true); + FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository(); + FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic(); + FakeShadeRepository shadeRepository = new FakeShadeRepository(); + FakePowerRepository powerRepository = new FakePowerRepository(); + FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); + + PowerInteractor powerInteractor = new PowerInteractor( + powerRepository, + new FalsingCollectorFake(), + mock(ScreenOffAnimationController.class), + mStatusBarStateController); + + SceneInteractor sceneInteractor = new SceneInteractor( mTestScope.getBackgroundScope(), - new FakeDisableFlagsRepository(), - new FakeSceneContainerFlags(), - mUtils::sceneInteractor, - new FakeKeyguardRepository(), - new FakeUserSetupRepository(), - mock(DeviceProvisionedController.class), - mock(UserInteractor.class), - new SharedNotificationContainerInteractor( - new FakeConfigurationRepository(), - mContext, - new ResourcesSplitShadeStateController()), - new FakeShadeRepository() - ); + new SceneContainerRepository( + mTestScope.getBackgroundScope(), + mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())), + powerRepository, + mock(SceneLogger.class)); + + FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); + KeyguardInteractor keyguardInteractor = new KeyguardInteractor( + keyguardRepository, + new FakeCommandQueue(), + powerInteractor, + featureFlags, + sceneContainerFlags, + new FakeDeviceEntryRepository(), + new FakeKeyguardBouncerRepository(), + configurationRepository, + shadeRepository, + () -> sceneInteractor); + + FakeKeyguardTransitionRepository keyguardTransitionRepository = + new FakeKeyguardTransitionRepository(); + + KeyguardTransitionInteractor keyguardTransitionInteractor = + new KeyguardTransitionInteractor( + mTestScope.getBackgroundScope(), + keyguardTransitionRepository, + () -> keyguardInteractor, + () -> mFromLockscreenTransitionInteractor, + () -> mFromPrimaryBouncerTransitionInteractor); + + mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + shadeRepository, + powerInteractor); + + mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor( + keyguardTransitionRepository, + keyguardTransitionInteractor, + mTestScope.getBackgroundScope(), + keyguardInteractor, + featureFlags, + mock(KeyguardSecurityModel.class), + powerInteractor); + + ResourcesSplitShadeStateController splitShadeStateController = + new ResourcesSplitShadeStateController(); + + mShadeInteractor = + new ShadeInteractor( + mTestScope.getBackgroundScope(), + deviceProvisioningRepository, + new FakeDisableFlagsRepository(), + mDozeParameters, + sceneContainerFlags, + () -> sceneInteractor, + keyguardRepository, + keyguardTransitionInteractor, + powerInteractor, + new FakeUserSetupRepository(), + mock(UserInteractor.class), + new SharedNotificationContainerInteractor( + configurationRepository, + mContext, + splitShadeStateController), + new FakeShadeRepository() + ); mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl( mContext, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt index 0e594964bb30..dc5fd953239d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt @@ -15,21 +15,29 @@ */ package com.android.systemui +import com.android.systemui.classifier.FakeClassifierModule import com.android.systemui.data.FakeSystemUiDataLayerModule import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.log.FakeUiEventLoggerModule import com.android.systemui.scene.FakeSceneModule import com.android.systemui.settings.FakeSettingsModule +import com.android.systemui.statusbar.policy.FakeConfigurationControllerModule +import com.android.systemui.statusbar.policy.FakeSplitShadeStateControllerModule import com.android.systemui.util.concurrency.FakeExecutorModule +import com.android.systemui.util.time.FakeSystemClockModule import dagger.Module @Module( includes = [ + FakeClassifierModule::class, + FakeConfigurationControllerModule::class, FakeExecutorModule::class, FakeFeatureFlagsClassicModule::class, - FakeSettingsModule::class, FakeSceneModule::class, + FakeSettingsModule::class, + FakeSplitShadeStateControllerModule::class, + FakeSystemClockModule::class, FakeSystemUiDataLayerModule::class, FakeUiEventLoggerModule::class, ] diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt new file mode 100644 index 000000000000..8fa6695cab99 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.authentication.data + +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepositoryModule +import dagger.Module + +@Module(includes = [FakeAuthenticationRepositoryModule::class]) +object FakeAuthenticationDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index 4fc3e3f66e6d..ddfe79aedce6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -24,10 +24,17 @@ import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import dagger.Binds +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.currentTime class FakeAuthenticationRepository( private val deviceEntryRepository: FakeDeviceEntryRepository, @@ -201,3 +208,19 @@ class FakeAuthenticationRepository( } } } + +@OptIn(ExperimentalCoroutinesApi::class) +@Module(includes = [FakeAuthenticationRepositoryModule.Bindings::class]) +object FakeAuthenticationRepositoryModule { + @Provides + @SysUISingleton + fun provideFake( + deviceEntryRepository: FakeDeviceEntryRepository, + scope: TestScope, + ) = FakeAuthenticationRepository(deviceEntryRepository, currentTime = { scope.currentTime }) + + @Module + interface Bindings { + @Binds fun bindFake(fake: FakeAuthenticationRepository): AuthenticationRepository + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt new file mode 100644 index 000000000000..23bad39a262e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.classifier + +import dagger.Module + +@Module(includes = [FakeFalsingCollectorModule::class, FakeFalsingManagerModule::class]) +object FakeClassifierModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt new file mode 100644 index 000000000000..92acc9465bb0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.classifier + +import dagger.Binds +import dagger.Module + +@Module +interface FakeFalsingCollectorModule { + @Binds @FalsingCollectorActual fun bindFake(fake: FalsingCollectorFake): FalsingCollector + @Binds fun bindFakeLegacy(fake: FalsingCollectorFake): FalsingCollector +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt new file mode 100644 index 000000000000..554fc75ec1db --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.classifier + +import com.android.systemui.plugins.FalsingManager +import dagger.Binds +import dagger.Module + +@Module +interface FakeFalsingManagerModule { + @Binds fun bindFake(fake: FalsingManagerFake): FalsingManager +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java index d47e88fc9385..5038285aef00 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -21,15 +21,19 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.net.Uri; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.FalsingManager; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + /** * Simple Fake for testing where {@link FalsingManager} is required. */ +@SysUISingleton public class FalsingManagerFake implements FalsingManager { private boolean mIsFalseTouch; private boolean mIsSimpleTap; @@ -46,6 +50,10 @@ public class FalsingManagerFake implements FalsingManager { private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); private final List<FalsingTapListener> mTapListeners = new ArrayList<>(); + @Inject + public FalsingManagerFake() { + } + @Override public void onSuccessfulUnlock() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt index f866932309bb..cffbf0271c29 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt @@ -15,8 +15,10 @@ */ package com.android.systemui.data +import com.android.systemui.authentication.data.FakeAuthenticationDataLayerModule import com.android.systemui.bouncer.data.repository.FakeBouncerDataLayerModule import com.android.systemui.common.ui.data.FakeCommonDataLayerModule +import com.android.systemui.deviceentry.data.FakeDeviceEntryDataLayerModule import com.android.systemui.keyguard.data.FakeKeyguardDataLayerModule import com.android.systemui.power.data.FakePowerDataLayerModule import com.android.systemui.shade.data.repository.FakeShadeDataLayerModule @@ -28,8 +30,10 @@ import dagger.Module @Module( includes = [ - FakeCommonDataLayerModule::class, + FakeAuthenticationDataLayerModule::class, FakeBouncerDataLayerModule::class, + FakeCommonDataLayerModule::class, + FakeDeviceEntryDataLayerModule::class, FakeKeyguardDataLayerModule::class, FakePowerDataLayerModule::class, FakeShadeDataLayerModule::class, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt new file mode 100644 index 000000000000..ef02bdd9c35c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.deviceentry.data + +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule +import dagger.Module + +@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt new file mode 100644 index 000000000000..f4feee19df65 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.deviceentry.data.repository + +import dagger.Module + +@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt index 5e60a09e006b..f0293489cb87 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt @@ -1,22 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.systemui.deviceentry.data.repository +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** Fake implementation of [DeviceEntryRepository] */ -class FakeDeviceEntryRepository : DeviceEntryRepository { +@SysUISingleton +class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { private var isInsecureLockscreenEnabled = true - private var isBypassEnabled = false + + private val _isBypassEnabled = MutableStateFlow(false) + override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled private val _isUnlocked = MutableStateFlow(false) override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() - override fun isBypassEnabled(): Boolean { - return isBypassEnabled - } - override suspend fun isInsecureLockscreenEnabled(): Boolean { return isInsecureLockscreenEnabled } @@ -30,6 +48,11 @@ class FakeDeviceEntryRepository : DeviceEntryRepository { } fun setBypassEnabled(isBypassEnabled: Boolean) { - this.isBypassEnabled = isBypassEnabled + _isBypassEnabled.value = isBypassEnabled } } + +@Module +interface FakeDeviceEntryRepositoryModule { + @Binds fun bindFake(fake: FakeDeviceEntryRepository): DeviceEntryRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt index 1bec82b0875b..e59f642071fb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt @@ -16,14 +16,18 @@ package com.android.systemui.statusbar.data import com.android.systemui.statusbar.disableflags.data.FakeStatusBarDisableFlagsDataLayerModule +import com.android.systemui.statusbar.notification.data.FakeStatusBarNotificationsDataLayerModule import com.android.systemui.statusbar.pipeline.data.FakeStatusBarPipelineDataLayerModule +import com.android.systemui.statusbar.policy.data.FakeStatusBarPolicyDataLayerModule import dagger.Module @Module( includes = [ FakeStatusBarDisableFlagsDataLayerModule::class, + FakeStatusBarNotificationsDataLayerModule::class, FakeStatusBarPipelineDataLayerModule::class, + FakeStatusBarPolicyDataLayerModule::class, ] ) object FakeStatusBarDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt new file mode 100644 index 000000000000..788e3aa9c41a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.data + +import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardStateRepositoryModule +import dagger.Module + +@Module(includes = [FakeNotificationsKeyguardStateRepositoryModule::class]) +object FakeStatusBarNotificationsDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt new file mode 100644 index 000000000000..5d3cb4db9c7e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.data.repository + +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +@SysUISingleton +class FakeNotificationsKeyguardViewStateRepository @Inject constructor() : + NotificationsKeyguardViewStateRepository { + private val _notificationsFullyHidden = MutableStateFlow(false) + override val areNotificationsFullyHidden: Flow<Boolean> = _notificationsFullyHidden + + private val _isPulseExpanding = MutableStateFlow(false) + override val isPulseExpanding: Flow<Boolean> = _isPulseExpanding + + fun setNotificationsFullyHidden(fullyHidden: Boolean) { + _notificationsFullyHidden.value = fullyHidden + } + + fun setPulseExpanding(expanding: Boolean) { + _isPulseExpanding.value = expanding + } +} + +@Module +interface FakeNotificationsKeyguardStateRepositoryModule { + @Binds + fun bindFake( + fake: FakeNotificationsKeyguardViewStateRepository + ): NotificationsKeyguardViewStateRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt index 16a326869562..23477d86807c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt @@ -1,9 +1,14 @@ package com.android.systemui.statusbar.policy import android.content.res.Configuration +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject /** Fake implementation of [ConfigurationController] for tests. */ -class FakeConfigurationController : ConfigurationController { +@SysUISingleton +class FakeConfigurationController @Inject constructor() : ConfigurationController { private var listeners = mutableListOf<ConfigurationController.ConfigurationListener>() @@ -33,3 +38,8 @@ class FakeConfigurationController : ConfigurationController { override fun isLayoutRtl(): Boolean = false } + +@Module +interface FakeConfigurationControllerModule { + @Binds fun bindFake(fake: FakeConfigurationController): ConfigurationController +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt new file mode 100644 index 000000000000..14bab84e62b2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.statusbar.policy + +import dagger.Binds +import dagger.Module + +@Module +interface FakeSplitShadeStateControllerModule { + @Binds fun bindFake(fake: ResourcesSplitShadeStateController): SplitShadeStateController +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt new file mode 100644 index 000000000000..5aece1bbbd31 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy.data + +import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepositoryModule +import dagger.Module + +@Module(includes = [FakeDeviceProvisioningRepositoryModule::class]) +object FakeStatusBarPolicyDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt new file mode 100644 index 000000000000..300229954044 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy.data.repository + +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +@SysUISingleton +class FakeDeviceProvisioningRepository @Inject constructor() : DeviceProvisioningRepository { + private val _isDeviceProvisioned = MutableStateFlow(false) + override val isDeviceProvisioned: Flow<Boolean> = _isDeviceProvisioned + private val _isFactoryResetProtectionActive = MutableStateFlow(false) + override val isFactoryResetProtectionActive: Flow<Boolean> = _isFactoryResetProtectionActive + fun setDeviceProvisioned(isProvisioned: Boolean) { + _isDeviceProvisioned.value = isProvisioned + } + fun setFactoryResetProtectionActive(isActive: Boolean) { + _isFactoryResetProtectionActive.value = isActive + } +} + +@Module +interface FakeDeviceProvisioningRepositoryModule { + @Binds fun bindFake(fake: FakeDeviceProvisioningRepository): DeviceProvisioningRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt index 5de05c27ba2e..1f48d940f91c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.util.concurrency +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.util.time.FakeSystemClock import dagger.Binds @@ -22,14 +23,12 @@ import dagger.Module import dagger.Provides import java.util.concurrent.Executor -@Module(includes = [FakeExecutorModule.Bindings::class]) -class FakeExecutorModule( - @get:Provides val clock: FakeSystemClock = FakeSystemClock(), -) { - @get:Provides val executor = FakeExecutor(clock) +@Module +interface FakeExecutorModule { + @Binds @Main @SysUISingleton fun bindMainExecutor(executor: FakeExecutor): Executor - @Module - interface Bindings { - @Binds @Main fun bindMainExecutor(executor: FakeExecutor): Executor + companion object { + @Provides + fun provideFake(clock: FakeSystemClock) = FakeExecutor(clock) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt index 3fff136db03a..3e3d7cbb40b5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt @@ -13,19 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.systemui.util.time -package com.android.systemui; +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import dagger.Provides -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +@Module +interface FakeSystemClockModule { + @Binds fun bindFake(fake: FakeSystemClock): SystemClock -/** - * 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 { + companion object { + @Provides @SysUISingleton fun providesFake() = FakeSystemClock() + } } diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp index 155dc1a68295..18f783146c72 100644 --- a/packages/WallpaperBackup/Android.bp +++ b/packages/WallpaperBackup/Android.bp @@ -49,7 +49,7 @@ android_test { "androidx.test.core", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], resource_dirs: ["test/res"], certificate: "platform", diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp index b781602399a3..0244c0fe0533 100644 --- a/packages/overlays/tests/Android.bp +++ b/packages/overlays/tests/Android.bp @@ -34,7 +34,7 @@ android_test { "androidx.test.rules", "androidx.test.espresso.core", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], dxflags: ["--multi-dex"], } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 7e09b5ea9fa5..258820a5a03c 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -24,6 +24,7 @@ import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -1660,8 +1661,21 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku synchronized (mLock) { ensureGroupStateLoadedLocked(userId); + final String pkg = componentName.getPackageName(); + final ProviderId id; + if (!mPackageManagerInternal.isSameApp(pkg, callingUid, userId)) { + // If the calling process is requesting to pin appwidgets from another process, + // check if the calling process has the necessary permission. + if (!injectHasAccessWidgetsPermission(Binder.getCallingPid(), callingUid)) { + return false; + } + id = new ProviderId(mPackageManagerInternal.getPackageUid( + pkg, 0 /* flags */, userId), componentName); + } else { + id = new ProviderId(callingUid, componentName); + } // Look for the widget associated with the caller. - Provider provider = lookupProviderLocked(new ProviderId(callingUid, componentName)); + Provider provider = lookupProviderLocked(id); if (provider == null || provider.zombie) { return false; } @@ -1675,6 +1689,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku .requestPinAppWidget(callingPackage, info, extras, resultSender, userId); } + /** + * Returns true if the caller has the proper permission to access app widgets. + */ + private boolean injectHasAccessWidgetsPermission(int callingPid, int callingUid) { + return mContext.checkPermission(Manifest.permission.CLEAR_APP_USER_DATA, + callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; + } + @Override public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter, int profileId, String packageName) { @@ -4131,7 +4153,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku return false; } - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public AppWidgetProviderInfo getInfoLocked(Context context) { if (!mInfoParsed) { // parse @@ -4159,18 +4181,18 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku * be completely parsed and only contain placeHolder information like * {@link AppWidgetProviderInfo#providerInfo} */ - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public AppWidgetProviderInfo getPartialInfoLocked() { return info; } - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public void setPartialInfoLocked(AppWidgetProviderInfo info) { this.info = info; mInfoParsed = false; } - @GuardedBy("mLock") + @GuardedBy("AppWidgetServiceImpl.mLock") public void setInfoLocked(AppWidgetProviderInfo info) { this.info = info; mInfoParsed = true; diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp index d43a219e6205..eb23f2f9b435 100644 --- a/services/autofill/Android.bp +++ b/services/autofill/Android.bp @@ -19,19 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.autofill-sources"], libs: ["services.core"], - static_libs: ["autofill_flags_java_lib"], -} - -aconfig_declarations { - name: "autofill_flags", - package: "android.service.autofill", - srcs: [ - "bugfixes.aconfig", - "features.aconfig", - ], -} - -java_aconfig_library { - name: "autofill_flags_java_lib", - aconfig_declarations: "autofill_flags", } diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ef237546378e..123b65c039ba 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -5,4 +5,11 @@ flag { namespace: "autofill" description: "Test flag " bug: "297380045" -}
\ No newline at end of file +} + +flag { + name: "relayout" + namespace: "autofill" + description: "Mitigation for relayout issue" + bug: "294330426" +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 0220deca18c1..265ed4652bfb 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2437,7 +2437,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } - fillInIntent = createAuthFillInIntentLocked(requestId, extras); + fillInIntent = createAuthFillInIntentLocked(requestId, extras, /* authExtras= */ null); if (fillInIntent == null) { forceRemoveFromServiceLocked(); return; @@ -5558,7 +5558,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetAuthenticationType( AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false); - final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState); + final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState, + dataset.getAuthenticationExtras()); if (fillInIntent == null) { forceRemoveFromServiceLocked(); return; @@ -5574,7 +5575,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO: this should never be null, but we got at least one occurrence, probably due to a race. @GuardedBy("mLock") @Nullable - private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) { + private Intent createAuthFillInIntentLocked(int requestId, Bundle extras, + @Nullable Bundle authExtras) { final Intent fillInIntent = new Intent(); final FillContext context = getFillContextByRequestIdLocked(requestId); @@ -5591,6 +5593,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure()); fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras); + if (authExtras != null) { + fillInIntent.putExtra(AutofillManager.EXTRA_AUTH_STATE, authExtras); + } return fillInIntent; } diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 9bab261bb0f4..93dca2f4f192 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -359,8 +359,10 @@ public final class PinnerService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri) { if (userSetupCompleteUri.equals(uri)) { - sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(), - true /* force */); + if (mConfiguredToPinHome) { + sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(), + true /* force */); + } } } }, UserHandle.USER_ALL); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8cc2665b3562..962f38f10b5d 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -214,6 +214,9 @@ class StorageManagerService extends IStorageManager.Stub // external storage service. public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10; + /** Extended timeout for the system server watchdog. */ + private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 60 * 1000; + @GuardedBy("mLock") private final Set<Integer> mFuseMountedUser = new ArraySet<>(); @@ -1230,6 +1233,8 @@ class StorageManagerService extends IStorageManager.Stub private void onUserStopped(int userId) { Slog.d(TAG, "onUserStopped " + userId); + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow"); try { mVold.onUserStopped(userId); mStoraged.onUserStopped(userId); @@ -1312,6 +1317,8 @@ class StorageManagerService extends IStorageManager.Stub unlockedUsers.add(userId); } } + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow"); for (Integer userId : unlockedUsers) { try { mVold.onUserStopped(userId); @@ -3600,6 +3607,8 @@ class StorageManagerService extends IStorageManager.Stub @Override public ParcelFileDescriptor open() throws AppFuseMountException { + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow"); try { final FileDescriptor fd = mVold.mountAppFuse(uid, mountId); mMounted = true; @@ -3612,6 +3621,8 @@ class StorageManagerService extends IStorageManager.Stub @Override public ParcelFileDescriptor openFile(int mountId, int fileId, int flags) throws AppFuseMountException { + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow"); try { return new ParcelFileDescriptor( mVold.openAppFuseFile(uid, mountId, fileId, flags)); @@ -3622,6 +3633,8 @@ class StorageManagerService extends IStorageManager.Stub @Override public void close() throws Exception { + Watchdog.getInstance().setOneOffTimeoutForMonitors( + SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow"); if (mMounted) { mVold.unmountAppFuse(uid, mountId); mMounted = false; diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index b05b397a45de..55aa7164a67b 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -644,6 +644,16 @@ public class Watchdog implements Dumpable { } /** + * Sets a one-off timeout for the next run of the watchdog for the monitor thread. + * + * <p>Simiar to {@link setOneOffTimeoutForCurrentThread} but used for monitors added through + * {@link #addMonitor} + */ + public void setOneOffTimeoutForMonitors(int oneOffTimeoutMillis, String reason) { + mMonitorChecker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason); + } + + /** * Pauses Watchdog action for the currently running thread. Useful before executing long running * operations that could falsely trigger the watchdog. Each call to this will require a matching * call to {@link #resumeWatchingCurrentThread}. diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java index 378a38602211..9ba49ce35dad 100644 --- a/services/core/java/com/android/server/am/AnrTimer.java +++ b/services/core/java/com/android/server/am/AnrTimer.java @@ -108,6 +108,14 @@ class AnrTimer<V> { private static final boolean ENABLE_TRACING = false; /** + * Return true if the feature is enabled. By default, the value is take from the Flags class + * but it can be changed for local testing. + */ + private static boolean anrTimerServiceEnabled() { + return Flags.anrTimerServiceEnabled(); + } + + /** * The status of an ANR timer. TIMER_INVALID status is returned when an error is detected. */ private static final int TIMER_INVALID = 0; @@ -327,18 +335,33 @@ class AnrTimer<V> { */ @VisibleForTesting static class Injector { + private final Handler mReferenceHandler; + + Injector(@NonNull Handler handler) { + mReferenceHandler = handler; + } + /** - * Return a handler for the given Callback. + * Return a handler for the given Callback, based on the reference handler. The handler + * might be mocked, in which case it does not have a valid Looper. In this case, use the + * main Looper. */ + @NonNull Handler getHandler(@NonNull Handler.Callback callback) { - return null; + Looper looper = mReferenceHandler.getLooper(); + if (looper == null) looper = Looper.getMainLooper(); + return new Handler(looper, callback); }; - /** - * Return a CpuTracker. - */ + /** Return a CpuTracker. */ + @NonNull CpuTracker getTracker() { - return null; + return new CpuTracker(); + } + + /** Return true if the feature is enabled. */ + boolean getFeatureEnabled() { + return anrTimerServiceEnabled(); } } @@ -375,12 +398,6 @@ class AnrTimer<V> { /** The interface to fetch process statistics that might extend an ANR timeout. */ private final CpuTracker mCpu; - /** Create a HandlerTimerService based on the input handler. */ - HandlerTimerService(@NonNull Handler handler) { - mHandler = new Handler(handler.getLooper(), this::expires); - mCpu = new CpuTracker(); - } - /** Create a HandlerTimerService that directly uses the supplied handler and tracker. */ @VisibleForTesting HandlerTimerService(@NonNull Injector injector) { @@ -491,38 +508,56 @@ class AnrTimer<V> { private final boolean mLenientCancel = true; /** + * The top-level switch for the feature enabled or disabled. + */ + private final FeatureSwitch mFeature; + + /** * The common constructor. A null injector results in a normal, production timer. */ @VisibleForTesting AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend, - @Nullable Injector injector) { + @NonNull Injector injector) { mHandler = handler; mWhat = what; mLabel = label; mExtend = extend; - if (injector == null) { - mTimerService = new HandlerTimerService(handler); + boolean enabled = injector.getFeatureEnabled(); + if (!enabled) { + mFeature = new FeatureDisabled(); + mTimerService = null; } else { + mFeature = new FeatureEnabled(); mTimerService = new HandlerTimerService(injector); + + synchronized (sAnrTimerList) { + sAnrTimerList.add(new WeakReference(this)); + } } - synchronized (sAnrTimerList) { - sAnrTimerList.add(new WeakReference(this)); - } - Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService.toString(), label)); + Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService, label)); } /** * Create one timer instance for production. The client can ask for extensible timeouts. */ AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) { - this(handler, what, label, extend, null); + this(handler, what, label, extend, new Injector(handler)); } /** * Create one timer instance for production. There are no extensible timeouts. */ AnrTimer(@NonNull Handler handler, int what, @NonNull String label) { - this(handler, what, label, false, null); + this(handler, what, label, false); + } + + /** + * Return true if the service is enabled on this instance. Clients should use this method to + * decide if the feature is enabled, and not read the flags directly. This method should be + * deleted if and when the feature is enabled permanently. + */ + boolean serviceEnabled() { + return mFeature.enabled(); } /** @@ -613,93 +648,186 @@ class AnrTimer<V> { Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg)); } - /** - * Start a timer. + /** + * The FeatureSwitch class provides a quick switch between feature-enabled behavior and + * feature-disabled behavior. */ - boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { - final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, this); - synchronized (mLock) { - Timer old = mTimerMap.get(arg); - if (old != null) { - // There is an existing timer. This is a protocol error in the client. Record - // the error and then clean up by canceling running timers and discarding expired - // timers. - restartedLocked(old.status, arg); - if (old.status == TIMER_EXPIRED) { - discard(arg); + private abstract class FeatureSwitch { + abstract boolean start(@NonNull V arg, int pid, int uid, long timeoutMs); + abstract boolean cancel(@NonNull V arg); + abstract boolean accept(@NonNull V arg); + abstract boolean discard(@NonNull V arg); + abstract boolean enabled(); + } + + /** + * The FeatureDisabled class bypasses almost all AnrTimer logic. It is used when the AnrTimer + * service is disabled via Flags.anrTimerServiceEnabled. + */ + private class FeatureDisabled extends FeatureSwitch { + /** Start a timer by sending a message to the client's handler. */ + boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + final Message msg = mHandler.obtainMessage(mWhat, arg); + mHandler.sendMessageDelayed(msg, timeoutMs); + return true; + } + + /** Cancel a timer by removing the message from the client's handler. */ + boolean cancel(@NonNull V arg) { + mHandler.removeMessages(mWhat, arg); + return true; + } + + /** accept() is a no-op when the feature is disabled. */ + boolean accept(@NonNull V arg) { + return true; + } + + /** discard() is a no-op when the feature is disabled. */ + boolean discard(@NonNull V arg) { + return true; + } + + /** The feature is not enabled. */ + boolean enabled() { + return false; + } + } + + /** + * The FeatureEnabled class enables the AnrTimer logic. It is used when the AnrTimer service + * is enabled via Flags.anrTimerServiceEnabled. + */ + private class FeatureEnabled extends FeatureSwitch { + + /** + * Start a timer. + */ + boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, AnrTimer.this); + synchronized (mLock) { + Timer old = mTimerMap.get(arg); + if (old != null) { + // There is an existing timer. This is a protocol error in the client. + // Record the error and then clean up by canceling running timers and + // discarding expired timers. + restartedLocked(old.status, arg); + if (old.status == TIMER_EXPIRED) { + discard(arg); + } else { + cancel(arg); + } + } + if (mTimerService.start(timer)) { + timer.status = TIMER_RUNNING; + mTimerMap.put(arg, timer); + mTotalStarted++; + mMaxStarted = Math.max(mMaxStarted, mTimerMap.size()); + if (DEBUG) report(timer, "start"); + return true; } else { - cancel(arg); + Log.e(TAG, "AnrTimer.start failed"); + return false; } } - if (mTimerService.start(timer)) { - timer.status = TIMER_RUNNING; - mTimerMap.put(arg, timer); - mTotalStarted++; - mMaxStarted = Math.max(mMaxStarted, mTimerMap.size()); - if (DEBUG) report(timer, "start"); + } + + /** + * Cancel a timer. Return false if the timer was not found. + */ + boolean cancel(@NonNull V arg) { + synchronized (mLock) { + Timer timer = removeLocked(arg); + if (timer == null) { + if (!mLenientCancel) notFoundLocked("cancel", arg); + return false; + } + mTimerService.cancel(timer); + // There may be an expiration message in flight. Cancel it. + mHandler.removeMessages(mWhat, arg); + if (DEBUG) report(timer, "cancel"); + timer.release(); return true; - } else { - Log.e(TAG, "AnrTimer.start failed"); - return false; } } + + /** + * Accept a timer in the framework-level handler. The timeout has been accepted and the + * timeout handler is executing. Return false if the timer was not found. + */ + boolean accept(@NonNull V arg) { + synchronized (mLock) { + Timer timer = removeLocked(arg); + if (timer == null) { + notFoundLocked("accept", arg); + return false; + } + mTimerService.accept(timer); + traceEnd(timer); + if (DEBUG) report(timer, "accept"); + timer.release(); + return true; + } + } + + /** + * Discard a timer in the framework-level handler. For whatever reason, the timer is no + * longer interesting. No statistics are collected. Return false if the time was not + * found. + */ + boolean discard(@NonNull V arg) { + synchronized (mLock) { + Timer timer = removeLocked(arg); + if (timer == null) { + notFoundLocked("discard", arg); + return false; + } + mTimerService.discard(timer); + traceEnd(timer); + if (DEBUG) report(timer, "discard"); + timer.release(); + return true; + } + } + + /** The feature is enabled. */ + boolean enabled() { + return true; + } } /** - * Cancel a timer. Return false if the timer was not found. + * Start a timer associated with arg. If a timer already exists with the same arg, then that + * timer is canceled and a new timer is created. This returns false if the timer cannot be + * created. + */ + boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + return mFeature.start(arg, pid, uid, timeoutMs); + } + + /** + * Cancel a running timer and remove it from any list. This returns true if the timer was + * found and false otherwise. It is not an error to cancel a non-existent timer. It is also + * not an error to cancel an expired timer. */ boolean cancel(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - if (!mLenientCancel) notFoundLocked("cancel", arg); - return false; - } - mTimerService.cancel(timer); - // There may be an expiration message in flight. Cancel it. - mHandler.removeMessages(mWhat, arg); - if (DEBUG) report(timer, "cancel"); - timer.release(); - return true; - } + return mFeature.cancel(arg); } /** - * Accept a timer in the framework-level handler. The timeout has been accepted and the - * timeout handler is executing. Return false if the timer was not found. + * Accept an expired timer. This returns false if the timer was not found or if the timer was + * not expired. */ boolean accept(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - notFoundLocked("accept", arg); - return false; - } - mTimerService.accept(timer); - traceEnd(timer); - if (DEBUG) report(timer, "accept"); - timer.release(); - return true; - } + return mFeature.accept(arg); } /** - * Discard a timer in the framework-level handler. For whatever reason, the timer is no - * longer interesting. No statistics are collected. Return false if the time was not found. + * Discard an expired timer. This returns false if the timer was not found or if the timer was + * not expired. */ boolean discard(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - notFoundLocked("discard", arg); - return false; - } - mTimerService.discard(timer); - traceEnd(timer); - if (DEBUG) report(timer, "discard"); - timer.release(); - return true; - } + return mFeature.discard(arg); } /** diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 907069de8c97..147f8d1a1e32 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -580,7 +580,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> batteryStatsInternal); curDuration += curStart - lastUidBatteryUsageStartTs; try { - statsCommit.close(); + if (statsCommit != null) { + statsCommit.close(); + } else { + Slog.w(TAG, "Stat was null"); + } } catch (IOException e) { Slog.w(TAG, "Failed to close a stat"); } @@ -660,7 +664,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> } } try { - stats.close(); + if (stats != null) { + stats.close(); + } else { + Slog.w(TAG, "Stat was null"); + } } catch (IOException e) { Slog.w(TAG, "Failed to close a stat"); } @@ -684,7 +692,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> final BatteryUsageStats stats = statsList.get(0); for (int i = 1; i < statsList.size(); i++) { try { - statsList.get(i).close(); + if (statsList.get(i) != null) { + statsList.get(i).close(); + } else { + Slog.w(TAG, "Stat was null"); + } } catch (IOException e) { Slog.w(TAG, "Failed to close a stat in BatteryUsageStats List"); } diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index 5d31d1545b8d..e07c2bcaaaed 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -106,6 +106,14 @@ class BroadcastProcessQueue { private boolean mTimeoutScheduled; /** + * Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically + * used when deciding if we should extend the soft ANR timeout. + * + * Required when Flags.anrTimerServiceEnabled is false. + */ + long lastCpuDelayTime; + + /** * Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before * dispatching the current broadcast to the receiver in this process. */ diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index eb219a8819c6..a42890707368 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -258,6 +258,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6; private static final int MSG_UID_STATE_CHANGED = 7; + // Required when Flags.anrTimerServiceEnabled is false. + private static final int MSG_DELIVERY_TIMEOUT_SOFT = 8; + private void enqueueUpdateRunningList() { mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST); mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST); @@ -271,6 +274,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { updateRunningList(); return true; } + // Required when Flags.anrTimerServiceEnabled is false. + case MSG_DELIVERY_TIMEOUT_SOFT: { + synchronized (mService) { + deliveryTimeoutSoftLocked((BroadcastProcessQueue) msg.obj, msg.arg1); + return true; + } + } case MSG_DELIVERY_TIMEOUT: { deliveryTimeout((BroadcastProcessQueue) msg.obj); return true; @@ -1030,7 +1040,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { queue.setTimeoutScheduled(true); final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT : mBgConstants.TIMEOUT); - mAnrTimer.start(queue, softTimeoutMillis); + startDeliveryTimeoutLocked(queue, softTimeoutMillis); } else { queue.setTimeoutScheduled(false); } @@ -1110,7 +1120,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // If we were trying to deliver a manifest broadcast, throw the error as we need // to try redelivering the broadcast to this receiver. if (receiver instanceof ResolveInfo) { - mAnrTimer.cancel(queue); + cancelDeliveryTimeoutLocked(queue); throw new BroadcastDeliveryFailedException(e); } finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, @@ -1159,6 +1169,41 @@ class BroadcastQueueModernImpl extends BroadcastQueue { r.resultTo = null; } + // Required when Flags.anrTimerServiceEnabled is false. + private void startDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue, + int softTimeoutMillis) { + if (mAnrTimer.serviceEnabled()) { + mAnrTimer.start(queue, softTimeoutMillis); + } else { + queue.lastCpuDelayTime = queue.app.getCpuDelayTime(); + mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler, + MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis); + } + } + + // Required when Flags.anrTimerServiceEnabled is false. + private void cancelDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue) { + mAnrTimer.cancel(queue); + if (!mAnrTimer.serviceEnabled()) { + mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue); + } + } + + // Required when Flags.anrTimerServiceEnabled is false. + private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue, + int softTimeoutMillis) { + if (queue.app != null) { + // Instead of immediately triggering an ANR, extend the timeout by + // the amount of time the process was runnable-but-waiting; we're + // only willing to do this once before triggering an hard ANR + final long cpuDelayTime = queue.app.getCpuDelayTime() - queue.lastCpuDelayTime; + final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis); + mAnrTimer.start(queue, hardTimeoutMillis); + } else { + deliveryTimeoutLocked(queue); + } + } + private void deliveryTimeout(@NonNull BroadcastProcessQueue queue) { synchronized (mService) { deliveryTimeoutLocked(queue); @@ -1292,7 +1337,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mAnrTimer.discard(queue); } } else if (queue.timeoutScheduled()) { - mAnrTimer.cancel(queue); + cancelDeliveryTimeoutLocked(queue); } // Given that a receiver just finished, check if the "waitingFor" conditions are met. diff --git a/services/core/java/com/android/server/am/OomConnection.java b/services/core/java/com/android/server/am/OomConnection.java new file mode 100644 index 000000000000..17a4ce5b921c --- /dev/null +++ b/services/core/java/com/android/server/am/OomConnection.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.os.OomKillRecord; +import android.util.Slog; + +/** Connection to the out-of-memory (OOM) events' file */ +public final class OomConnection { + private static final String TAG = "OomConnection"; + + /** Connection listener interface */ + public interface OomConnectionListener { + + /** + * Callback function to handle the newest OOM kills. + * + * @param oomKills List of oom kills received from `waitOom()` + */ + void handleOomEvent(OomKillRecord[] oomKills); + } + + private final OomConnectionListener mOomListener; + + private final OomConnectionThread mOomConnectionThread; + + private static native OomKillRecord[] waitOom(); + + public OomConnection(OomConnectionListener listener) { + mOomListener = listener; + mOomConnectionThread = new OomConnectionThread(); + mOomConnectionThread.start(); + } + + private final class OomConnectionThread extends Thread { + public void run() { + while (true) { + OomKillRecord[] oom_kills = null; + try { + oom_kills = waitOom(); + mOomListener.handleOomEvent(oom_kills); + } catch (RuntimeException e) { + Slog.e(TAG, "failed waiting for OOM events: " + e); + break; + } + } + } + } +} diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index a97675f1d776..4572766371ec 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -97,6 +97,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.OomKillRecord; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -412,6 +413,8 @@ public final class ProcessList { private static LmkdConnection sLmkdConnection = null; + private static OomConnection sOomConnection = null; + private boolean mOomLevelsSet = false; private boolean mAppDataIsolationEnabled = false; @@ -855,6 +858,21 @@ public final class ProcessList { THREAD_PRIORITY_BACKGROUND, true /* allowIo */); sKillThread.start(); sKillHandler = new KillHandler(sKillThread.getLooper()); + sOomConnection = new OomConnection(new OomConnection.OomConnectionListener() { + @Override + public void handleOomEvent(OomKillRecord[] oomKills) { + for (OomKillRecord oomKill: oomKills) { + synchronized (mProcLock) { + noteAppKill( + oomKill.getPid(), + oomKill.getUid(), + ApplicationExitInfo.REASON_LOW_MEMORY, + ApplicationExitInfo.SUBREASON_OOM_KILL, + "oom"); + } + } + } + }); sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(), new LmkdConnection.LmkdConnectionListener() { @Override diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 4a0bc4b9ca6c..1ba1f55af71b 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -131,13 +131,17 @@ public class SettingsToPropertiesMapper { "car_telemetry", "codec_fwk", "companion", + "content_protection", "context_hub", "core_experiments_team_internal", "core_graphics", "haptics", "hardware_backed_security_mainline", + "input", "machine_learning", + "mainline_sdk", "media_audio", + "media_drm", "media_solutions", "nfc", "pixel_audio_android", diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index b03cc6295b8d..26d99d843c7e 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -6,4 +6,12 @@ flag { description: "Utilize new OomAdjuster implementation" bug: "298055811" is_fixed_read_only: true -}
\ No newline at end of file +} + +flag { + name: "anr_timer_service_enabled" + namespace: "system_performance" + is_fixed_read_only: true + description: "Feature flag for the ANR timer service" + bug: "282428924" +} diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 9805fd3d1316..333f62a81e94 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -1845,7 +1845,7 @@ public class SyncStorageEngine { private void parseListenForTickles(TypedXmlPullParser parser) { int userId = 0; try { - parser.getAttributeInt(null, XML_ATTR_USER); + userId = parser.getAttributeInt(null, XML_ATTR_USER); } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the user for listen-for-tickles", e); } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 9e92c8d7342d..cfbe0c69b320 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -60,6 +60,9 @@ import com.android.server.display.config.LuxThrottling; import com.android.server.display.config.NitsMap; import com.android.server.display.config.NonNegativeFloatToFloatPoint; import com.android.server.display.config.Point; +import com.android.server.display.config.PowerThrottlingConfig; +import com.android.server.display.config.PowerThrottlingMap; +import com.android.server.display.config.PowerThrottlingPoint; import com.android.server.display.config.PredefinedBrightnessLimitNames; import com.android.server.display.config.RefreshRateConfigs; import com.android.server.display.config.RefreshRateRange; @@ -139,6 +142,30 @@ import javax.xml.datatype.DatatypeConfigurationException; * </screenBrightnessMap> * * <screenBrightnessDefault>0.65</screenBrightnessDefault> + * <powerThrottlingConfig> + * <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed> + * <pollingWindowMillis>15</pollingWindowMillis> + * <powerThrottlingMap> + * <powerThrottlingPoint> + * <thermalStatus>severe</thermalStatus> + * <powerQuotaMilliWatts>200.6</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * <powerThrottlingPoint> + * <thermalStatus>critical</thermalStatus> + * <powerQuotaMilliWatts>300</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * </powerThrottlingMap> + * <powerThrottlingMap id="id_2"> // optional attribute, leave blank for default + * <powerThrottlingPoint> + * <thermalStatus>moderate</thermalStatus> + * <powerQuotaMilliWatts>400</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * <powerThrottlingPoint> + * <thermalStatus>severe</thermalStatus> + * <powerQuotaMilliWatts>250</powerQuotaMilliWatts> + * </powerThrottlingPoint> + * </powerThrottlingMap> + * </powerThrottlingConfig> * * <thermalThrottling> * <brightnessThrottlingMap> @@ -669,6 +696,8 @@ public class DisplayDeviceConfig { private List<String> mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; + @Nullable + private PowerThrottlingConfigData mPowerThrottlingConfigData; private DensityMapping mDensityMapping; private String mLoadedFrom = null; private Spline mSdrToHdrRatioSpline; @@ -781,6 +810,9 @@ public class DisplayDeviceConfig { private final HashMap<String, ThermalBrightnessThrottlingData> mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>(); + private final HashMap<String, PowerThrottlingData> + mPowerThrottlingDataMapByThrottlingId = new HashMap<>(); + private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>> mRefreshRateThrottlingMap = new HashMap<>(); @@ -1458,6 +1490,14 @@ public class DisplayDeviceConfig { return hbmData; } + /** + * @return Power throttling configuration data for the display. + */ + @Nullable + public PowerThrottlingConfigData getPowerThrottlingConfigData() { + return mPowerThrottlingConfigData; + } + @NonNull public Map<BrightnessLimitMapType, Map<Float, Float>> getLuxThrottlingData() { return mLuxThrottlingData; @@ -1491,6 +1531,14 @@ public class DisplayDeviceConfig { } /** + * @return power throttling configuration data for this display, for each throttling id. + **/ + public HashMap<String, PowerThrottlingData> + getPowerThrottlingDataMapByThrottlingId() { + return mPowerThrottlingDataMapByThrottlingId; + } + + /** * @return Auto brightness darkening light debounce */ public long getAutoBrightnessDarkeningLightDebounce() { @@ -1702,6 +1750,9 @@ public class DisplayDeviceConfig { + ", mThermalBrightnessThrottlingDataMapByThrottlingId=" + mThermalBrightnessThrottlingDataMapByThrottlingId + "\n" + + ", mPowerThrottlingDataMapByThrottlingId=" + + mPowerThrottlingDataMapByThrottlingId + + "\n" + "mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease @@ -1853,6 +1904,7 @@ public class DisplayDeviceConfig { loadBrightnessConstraintsFromConfigXml(); loadBrightnessMap(config); loadThermalThrottlingConfig(config); + loadPowerThrottlingConfigData(config); loadHighBrightnessModeData(config); loadLuxThrottling(config); loadQuirks(config); @@ -2171,6 +2223,59 @@ public class DisplayDeviceConfig { } } + private boolean loadPowerThrottlingMaps(PowerThrottlingConfig throttlingConfig) { + final List<PowerThrottlingMap> maps = throttlingConfig.getPowerThrottlingMap(); + if (maps == null || maps.isEmpty()) { + Slog.i(TAG, "No power throttling map found"); + return false; + } + + for (PowerThrottlingMap map : maps) { + final List<PowerThrottlingPoint> points = map.getPowerThrottlingPoint(); + // At least 1 point is guaranteed by the display device config schema + List<PowerThrottlingData.ThrottlingLevel> throttlingLevels = + new ArrayList<>(points.size()); + + boolean badConfig = false; + for (PowerThrottlingPoint point : points) { + ThermalStatus status = point.getThermalStatus(); + if (!thermalStatusIsValid(status)) { + badConfig = true; + break; + } + + throttlingLevels.add(new PowerThrottlingData.ThrottlingLevel( + convertThermalStatus(status), + point.getPowerQuotaMilliWatts().floatValue())); + } + + if (!badConfig) { + String id = map.getId() == null ? DEFAULT_ID : map.getId(); + if (mPowerThrottlingDataMapByThrottlingId.containsKey(id)) { + throw new RuntimeException("Power throttling data with ID " + id + + " already exists"); + } + mPowerThrottlingDataMapByThrottlingId.put(id, + PowerThrottlingData.create(throttlingLevels)); + } + } + return true; + } + + private void loadPowerThrottlingConfigData(DisplayConfiguration config) { + final PowerThrottlingConfig powerThrottlingCfg = config.getPowerThrottlingConfig(); + if (powerThrottlingCfg == null) { + return; + } + if (!loadPowerThrottlingMaps(powerThrottlingCfg)) { + return; + } + float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue(); + int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue(); + mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap, + pollingWindowMillis); + } + private void loadRefreshRateSetting(DisplayConfiguration config) { final RefreshRateConfigs refreshRateConfigs = (config == null) ? null : config.getRefreshRate(); @@ -3379,6 +3484,148 @@ public class DisplayDeviceConfig { } /** + * Container for Power throttling configuration data. + * TODO(b/302814899): extract to separate class. + */ + public static class PowerThrottlingConfigData { + /** Lowest brightness cap allowed for this device. */ + public final float brightnessLowestCapAllowed; + /** Time window for polling power in seconds. */ + public final int pollingWindowMillis; + public PowerThrottlingConfigData(float brightnessLowestCapAllowed, + int pollingWindowMillis) { + this.brightnessLowestCapAllowed = brightnessLowestCapAllowed; + this.pollingWindowMillis = pollingWindowMillis; + } + + @Override + public String toString() { + return "PowerThrottlingConfigData{" + + "brightnessLowestCapAllowed: " + + brightnessLowestCapAllowed + + ", pollingWindowMillis: " + pollingWindowMillis + + "} "; + } + } + + /** + * Container for power throttling data. + * TODO(b/302814899): extract to separate class and unify with ThermalBrightnessThrottlingData. + */ + public static class PowerThrottlingData { + public List<ThrottlingLevel> throttlingLevels; + + /** + * thermal status to power quota mapping. + */ + public static class ThrottlingLevel { + public @PowerManager.ThermalStatus int thermalStatus; + public float powerQuotaMilliWatts; + + public ThrottlingLevel( + @PowerManager.ThermalStatus int thermalStatus, float powerQuotaMilliWatts) { + this.thermalStatus = thermalStatus; + this.powerQuotaMilliWatts = powerQuotaMilliWatts; + } + + @Override + public String toString() { + return "[" + thermalStatus + "," + powerQuotaMilliWatts + "]"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ThrottlingLevel)) { + return false; + } + ThrottlingLevel otherThrottlingLevel = (ThrottlingLevel) obj; + + return otherThrottlingLevel.thermalStatus == this.thermalStatus + && otherThrottlingLevel.powerQuotaMilliWatts == this.powerQuotaMilliWatts; + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + thermalStatus; + result = 31 * result + Float.hashCode(powerQuotaMilliWatts); + return result; + } + } + + + /** + * Creates multiple temperature based throttling levels of power quota. + */ + public static PowerThrottlingData create( + List<ThrottlingLevel> throttlingLevels) { + if (throttlingLevels == null || throttlingLevels.size() == 0) { + Slog.e(TAG, "PowerThrottlingData received null or empty throttling levels"); + return null; + } + + ThrottlingLevel prevLevel = throttlingLevels.get(0); + final int numLevels = throttlingLevels.size(); + for (int i = 1; i < numLevels; i++) { + ThrottlingLevel thisLevel = throttlingLevels.get(i); + + if (thisLevel.thermalStatus <= prevLevel.thermalStatus) { + Slog.e(TAG, "powerThrottlingMap must be strictly increasing, ignoring " + + "configuration. ThermalStatus " + thisLevel.thermalStatus + " <= " + + prevLevel.thermalStatus); + return null; + } + + if (thisLevel.powerQuotaMilliWatts >= prevLevel.powerQuotaMilliWatts) { + Slog.e(TAG, "powerThrottlingMap must be strictly decreasing, ignoring " + + "configuration. powerQuotaMilliWatts " + + thisLevel.powerQuotaMilliWatts + " >= " + + prevLevel.powerQuotaMilliWatts); + return null; + } + + prevLevel = thisLevel; + } + return new PowerThrottlingData(throttlingLevels); + } + + @Override + public String toString() { + return "PowerThrottlingData{" + + "throttlingLevels:" + throttlingLevels + + "} "; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof PowerThrottlingData)) { + return false; + } + + PowerThrottlingData otherData = (PowerThrottlingData) obj; + return throttlingLevels.equals(otherData.throttlingLevels); + } + + @Override + public int hashCode() { + return throttlingLevels.hashCode(); + } + + @VisibleForTesting + PowerThrottlingData(List<ThrottlingLevel> inLevels) { + throttlingLevels = new ArrayList<>(inLevels.size()); + for (ThrottlingLevel level : inLevels) { + throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, + level.powerQuotaMilliWatts)); + } + } + } + + /** * Container for brightness throttling data. */ public static class ThermalBrightnessThrottlingData { diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java index 652e6cfd67be..39f0b13f716a 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java +++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java @@ -105,16 +105,21 @@ public class HdrClamper { public void resetHdrConfig(HdrBrightnessData data, int width, int height, float minimumHdrPercentOfScreen, IBinder displayToken) { mHdrBrightnessData = data; - mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen; + mHdrListener.mHdrMinPixels = minimumHdrPercentOfScreen <= 0 ? -1 + : (float) (width * height) * minimumHdrPercentOfScreen; if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe mHdrListener.unregister(mRegisteredDisplayToken); mHdrVisible = false; + mRegisteredDisplayToken = null; } - if (displayToken != null) { // new token not null, subscribe + // new token not null and hdr min % of the screen is set, subscribe. + // e.g. for virtual display, HBM data will be missing and HdrListener + // should not be registered + if (displayToken != null && mHdrListener.mHdrMinPixels > 0) { mHdrListener.register(displayToken); + mRegisteredDisplayToken = displayToken; } - mRegisteredDisplayToken = displayToken; } recalculateBrightnessCap(data, mAmbientLux, mHdrVisible); } diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index b6273e1daf82..e66fa5b3d516 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -51,22 +51,10 @@ public class DisplayManagerFlags { Flags.FLAG_ENABLE_DISPLAY_OFFLOAD, Flags::enableDisplayOffload); - private final FlagState mDisplayResolutionRangeVotingState = new FlagState( - Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING, - Flags::enableDisplayResolutionRangeVoting); - - private final FlagState mUserPreferredModeVoteState = new FlagState( - Flags.FLAG_ENABLE_USER_PREFERRED_MODE_VOTE, - Flags::enableUserPreferredModeVote); - private final FlagState mExternalDisplayLimitModeState = new FlagState( Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY, Flags::enableModeLimitForExternalDisplay); - private final FlagState mDisplaysRefreshRatesSynchronizationState = new FlagState( - Flags.FLAG_ENABLE_DISPLAYS_REFRESH_RATES_SYNCHRONIZATION, - Flags::enableDisplaysRefreshRatesSynchronization); - /** Returns whether connected display management is enabled or not. */ public boolean isConnectedDisplayManagementEnabled() { return mConnectedDisplayManagementFlagState.isEnabled(); @@ -90,7 +78,7 @@ public class DisplayManagerFlags { /** Returns whether resolution range voting feature is enabled or not. */ public boolean isDisplayResolutionRangeVotingEnabled() { - return mDisplayResolutionRangeVotingState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** @@ -98,7 +86,7 @@ public class DisplayManagerFlags { * {@link com.android.server.display.mode.DisplayModeDirector} */ public boolean isUserPreferredModeVoteEnabled() { - return mUserPreferredModeVoteState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** @@ -112,7 +100,7 @@ public class DisplayManagerFlags { * @return Whether displays refresh rate synchronization is enabled. */ public boolean isDisplaysRefreshRatesSynchronizationEnabled() { - return mDisplaysRefreshRatesSynchronizationState.isEnabled(); + return isExternalDisplayLimitModeEnabled(); } /** Returns whether displayoffload is enabled on not */ @@ -150,19 +138,20 @@ public class DisplayManagerFlags { } private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) { - // TODO(b/299462337) Remove when the infrastructure is ready. - if ((Build.IS_ENG || Build.IS_USERDEBUG) - && SystemProperties.getBoolean("persist.sys." + flagName, false)) { - return true; - } + boolean flagValue = false; try { - return flagFunction.get(); + flagValue = flagFunction.get(); } catch (Throwable ex) { if (DEBUG) { Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex); } - return false; } + // TODO(b/299462337) Remove when the infrastructure is ready. + if (Build.IS_ENG || Build.IS_USERDEBUG) { + return SystemProperties.getBoolean("persist.sys." + flagName + "-override", + flagValue); + } + return flagValue; } } } diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java index 2ede56dcecd9..a2c8748a9142 100644 --- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java +++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java @@ -62,10 +62,10 @@ class GestureMonitorSpyWindow { mWindowHandle.ownerUid = uid; mWindowHandle.scaleFactor = 1.0f; mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); - mWindowHandle.inputConfig = - InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY; + mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SPY; final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR); t.setPosition(mInputSurface, 0, 0); diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 6b399def4d73..2533e0297679 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -117,6 +117,7 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.input.InputManagerInternal.LidSwitchCallback; +import com.android.server.input.debug.FocusEventDebugView; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.policy.WindowManagerPolicy; diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 0eb620f3f4df..bad6bf0f0141 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -71,6 +71,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.InputMethodSubtypeHandle; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; @@ -99,7 +100,7 @@ import java.util.stream.Stream; * * @hide */ -final class KeyboardLayoutManager implements InputManager.InputDeviceListener { +class KeyboardLayoutManager implements InputManager.InputDeviceListener { private static final String TAG = "KeyboardLayoutManager"; @@ -1295,7 +1296,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } @SuppressLint("MissingPermission") - private List<ImeInfo> getImeInfoListForLayoutMapping() { + @VisibleForTesting + public List<ImeInfo> getImeInfoListForLayoutMapping() { List<ImeInfo> imeInfoList = new ArrayList<>(); UserManager userManager = Objects.requireNonNull( mContext.getSystemService(UserManager.class)); @@ -1402,7 +1404,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } - private static class ImeInfo { + @VisibleForTesting + public static class ImeInfo { @UserIdInt int mUserId; @NonNull InputMethodSubtypeHandle mImeSubtypeHandle; @Nullable InputMethodSubtype mImeSubtype; diff --git a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java index 67c221f77037..2b21e49a4e03 100644 --- a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java +++ b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.input; +package com.android.server.input.debug; import android.view.Display; import android.view.InputEvent; @@ -22,6 +22,7 @@ import android.view.InputEventReceiver; import android.view.MotionEvent; import com.android.server.UiThread; +import com.android.server.input.InputManagerService; /** * Receives input events before they are dispatched and reports them to FocusEventDebugView. diff --git a/services/core/java/com/android/server/input/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java index 4b8fabde7d35..6eec0dee9152 100644 --- a/services/core/java/com/android/server/input/FocusEventDebugView.java +++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.input; +package com.android.server.input.debug; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.util.TypedValue.COMPLEX_UNIT_SP; @@ -24,11 +24,9 @@ import android.animation.LayoutTransition; import android.annotation.AnyThread; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.ColorMatrixColorFilter; -import android.graphics.Paint; import android.graphics.Typeface; import android.util.DisplayMetrics; import android.util.Pair; @@ -40,7 +38,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RoundedCorner; import android.view.View; -import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.animation.AccelerateInterpolator; import android.widget.HorizontalScrollView; @@ -50,19 +47,17 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.input.InputManagerService; import java.util.HashMap; -import java.util.Iterator; -import java.util.Locale; import java.util.Map; -import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** * Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on * the screen. */ -class FocusEventDebugView extends RelativeLayout { +public class FocusEventDebugView extends RelativeLayout { private static final String TAG = FocusEventDebugView.class.getSimpleName(); @@ -112,7 +107,7 @@ class FocusEventDebugView extends RelativeLayout { mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm); } - FocusEventDebugView(Context c, InputManagerService service) { + public FocusEventDebugView(Context c, InputManagerService service) { this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c)); } @@ -149,11 +144,13 @@ class FocusEventDebugView extends RelativeLayout { return super.dispatchKeyEvent(event); } + /** Determines whether to show the key presses visualization. */ @AnyThread public void updateShowKeyPresses(boolean enabled) { post(() -> handleUpdateShowKeyPresses(enabled)); } + /** Determines whether to show the rotary input visualization. */ @AnyThread public void updateShowRotaryInput(boolean enabled) { post(() -> handleUpdateShowRotaryInput(enabled)); @@ -358,13 +355,6 @@ class FocusEventDebugView extends RelativeLayout { return mRotaryInputValueView != null; } - /** - * Converts a dimension in scaled pixel units to integer display pixels. - */ - private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) { - return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm); - } - private static class PressedKeyView extends TextView { private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{ @@ -473,376 +463,4 @@ class FocusEventDebugView extends RelativeLayout { invalidate(); } } - - // TODO(b/286086154): move RotaryInputGraphView and RotaryInputValueView to a subpackage. - - /** Draws the most recent rotary input value and indicates whether the source is active. */ - @VisibleForTesting - static class RotaryInputValueView extends TextView { - - private static final int INACTIVE_TEXT_COLOR = 0xffff00ff; - private static final int ACTIVE_TEXT_COLOR = 0xff420f28; - private static final int TEXT_SIZE_SP = 8; - private static final int SIDE_PADDING_SP = 4; - /** Determines how long the active status lasts. */ - private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */; - private static final ColorFilter ACTIVE_BACKGROUND_FILTER = - new ColorMatrixColorFilter(new float[]{ - 0, 0, 0, 0, 255, // red - 0, 0, 0, 0, 0, // green - 0, 0, 0, 0, 255, // blue - 0, 0, 0, 0, 200 // alpha - }); - - private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false); - private final float mScaledVerticalScrollFactor; - - @VisibleForTesting - RotaryInputValueView(Context c) { - super(c); - - DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); - mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); - - setText(getFormattedValue(0)); - setTextColor(INACTIVE_TEXT_COLOR); - setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm)); - setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0, - applyDimensionSp(SIDE_PADDING_SP, dm), 0); - setTypeface(null, Typeface.BOLD); - setBackgroundResource(R.drawable.focus_event_rotary_input_background); - } - - void updateValue(float value) { - removeCallbacks(mUpdateActivityStatusCallback); - - setText(getFormattedValue(value * mScaledVerticalScrollFactor)); - - updateActivityStatus(true); - postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION); - } - - @VisibleForTesting - void updateActivityStatus(boolean active) { - if (active) { - setTextColor(ACTIVE_TEXT_COLOR); - getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER); - } else { - setTextColor(INACTIVE_TEXT_COLOR); - getBackground().clearColorFilter(); - } - } - - private static String getFormattedValue(float value) { - return String.format("%s%.1f", value < 0 ? "-" : "+", Math.abs(value)); - } - } - - /** - * Shows a graph with the rotary input values as a function of time. - * The graph gets reset if no action is received for a certain amount of time. - */ - @VisibleForTesting - static class RotaryInputGraphView extends View { - - private static final int FRAME_COLOR = 0xbf741b47; - private static final int FRAME_WIDTH_SP = 2; - private static final int FRAME_BORDER_GAP_SP = 10; - private static final int FRAME_TEXT_SIZE_SP = 10; - private static final int FRAME_TEXT_OFFSET_SP = 2; - private static final int GRAPH_COLOR = 0xffff00ff; - private static final int GRAPH_LINE_WIDTH_SP = 1; - private static final int GRAPH_POINT_RADIUS_SP = 4; - private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5); - private static final float DEFAULT_FRAME_CENTER_POSITION = 0; - private static final int MAX_GRAPH_VALUES_SIZE = 400; - /** Maximum time between values so that they are considered part of the same gesture. */ - private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1); - - private final DisplayMetrics mDm; - /** - * Distance in position units (amount scrolled in display pixels) from the center to the - * top/bottom frame lines. - */ - private final float mFrameCenterToBorderDistance; - private final float mScaledVerticalScrollFactor; - private final Locale mDefaultLocale; - private final Paint mFramePaint = new Paint(); - private final Paint mFrameTextPaint = new Paint(); - private final Paint mGraphLinePaint = new Paint(); - private final Paint mGraphPointPaint = new Paint(); - - private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE); - /** Position at which graph values are placed at the center of the graph. */ - private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; - - @VisibleForTesting - RotaryInputGraphView(Context c) { - super(c); - - mDm = mContext.getResources().getDisplayMetrics(); - // This makes the center-to-border distance equivalent to the display height, meaning - // that the total height of the graph is equivalent to 2x the display height. - mFrameCenterToBorderDistance = mDm.heightPixels; - mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); - mDefaultLocale = Locale.getDefault(); - - mFramePaint.setColor(FRAME_COLOR); - mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm)); - - mFrameTextPaint.setColor(GRAPH_COLOR); - mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm)); - - mGraphLinePaint.setColor(GRAPH_COLOR); - mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm)); - mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND); - mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND); - - mGraphPointPaint.setColor(GRAPH_COLOR); - mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm)); - mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND); - mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND); - } - - /** - * Reads new scroll axis value and updates the list accordingly. Old positions are - * kept at the front (what you would get with getFirst), while the recent positions are - * kept at the back (what you would get with getLast). Also updates the frame center - * position to handle out-of-bounds cases. - */ - void addValue(float scrollAxisValue, long eventTime) { - // Remove values that are too old. - while (mGraphValues.getSize() > 0 - && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) { - mGraphValues.removeFirst(); - } - - // If there are no recent values, reset the frame center. - if (mGraphValues.getSize() == 0) { - mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; - } - - // Handle new value. We multiply the scroll axis value by the scaled scroll factor to - // get the amount of pixels to be scrolled. We also compute the accumulated position - // by adding the current value to the last one (if not empty). - final float displacement = scrollAxisValue * mScaledVerticalScrollFactor; - final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos); - final float pos = prevPos + displacement; - - mGraphValues.add(pos, eventTime); - - // The difference between the distance of the most recent position from the center - // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center - // frame (mFrameCenterToBorderDistance). - final float verticalDiff = Math.abs(pos - mFrameCenterPosition) - - mFrameCenterToBorderDistance; - // If needed, translate frame. - if (verticalDiff > 0) { - final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1; - // Here, we update the center frame position by the exact amount needed for us to - // stay within the maximum allowed distance from the center frame. - mFrameCenterPosition += sign * verticalDiff; - } - - // Redraw canvas. - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - // Note: vertical coordinates in Canvas go from top to bottom, - // that is bottomY > middleY > topY. - final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm); - final int topY = verticalMargin; - final int bottomY = getHeight() - verticalMargin; - final int middleY = (topY + bottomY) / 2; - - // Note: horizontal coordinates in Canvas go from left to right, - // that is rightX > leftX. - final int leftX = 0; - final int rightX = getWidth(); - - // Draw the frame, which includes 3 lines that show the maximum, - // minimum and middle positions of the graph. - canvas.drawLine(leftX, topY, rightX, topY, mFramePaint); - canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint); - canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint); - - // Draw the position that each frame line corresponds to. - final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm); - canvas.drawText( - String.format(mDefaultLocale, "%.1f", - mFrameCenterPosition + mFrameCenterToBorderDistance), - leftX, - topY - frameTextOffset, mFrameTextPaint - ); - canvas.drawText( - String.format(mDefaultLocale, "%.1f", mFrameCenterPosition), - leftX, - middleY - frameTextOffset, mFrameTextPaint - ); - canvas.drawText( - String.format(mDefaultLocale, "%.1f", - mFrameCenterPosition - mFrameCenterToBorderDistance), - leftX, - bottomY - frameTextOffset, mFrameTextPaint - ); - - // If there are no graph values to be drawn, stop here. - if (mGraphValues.getSize() == 0) { - return; - } - - // Draw the graph using the times and positions. - // We start at the most recent value (which should be drawn at the right) and move - // to the older values (which should be drawn to the left of more recent ones). Negative - // indices are handled by circuling back to the end of the buffer. - final long mostRecentTime = mGraphValues.getLast().mTime; - float prevCoordX = 0; - float prevCoordY = 0; - float prevAge = 0; - for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) { - final GraphValue value = iter.next(); - - final int age = (int) (mostRecentTime - value.mTime); - final float pos = value.mPos; - - // We get the horizontal coordinate in time units from left to right with - // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas - // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL) - // and by multiplying it by the canvas length (rightX - leftX). Finally, we - // offset the coordinate by adding it to leftX. - final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age) - / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX); - - // We get the vertical coordinate in position units from middle to top with - // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas - // units by dividing it by half of the position-domain length - // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas - // length (middleY - topY). Finally, we offset the coordinate by subtracting - // it from middleY (we can't "add" here because the coordinate grows from top - // to bottom). - final float coordY = middleY - ((pos - mFrameCenterPosition) - / mFrameCenterToBorderDistance) * (middleY - topY); - - // Draw a point for this value. - canvas.drawPoint(coordX, coordY, mGraphPointPaint); - - // If this value is part of the same gesture as the previous one, draw a line - // between them. We ignore the first value (with age = 0). - if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) { - canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint); - } - - prevCoordX = coordX; - prevCoordY = coordY; - prevAge = age; - } - } - - @VisibleForTesting - float getFrameCenterPosition() { - return mFrameCenterPosition; - } - - /** - * Holds data needed to draw each entry in the graph. - */ - private static class GraphValue { - /** Position. */ - float mPos; - /** Time when this value was added. */ - long mTime; - - GraphValue(float pos, long time) { - this.mPos = pos; - this.mTime = time; - } - } - - /** - * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the - * old values with new ones to avoid creating new objects. - */ - private static class CyclicBuffer { - private final GraphValue[] mValues; - private final int mCapacity; - private int mSize = 0; - private int mLastIndex = 0; - - // The iteration index and counter are here to make it easier to reset them. - /** Determines the value currently pointed by the iterator. */ - private int mIteratorIndex; - /** Counts how many values have been iterated through. */ - private int mIteratorCount; - - /** Used traverse the values in reverse order. */ - private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() { - @Override - public boolean hasNext() { - return mIteratorCount <= mSize; - } - - @Override - public GraphValue next() { - // Returns the value currently pointed by the iterator and moves the iterator to - // the previous one. - mIteratorCount++; - return mValues[(mIteratorIndex-- + mCapacity) % mCapacity]; - } - }; - - CyclicBuffer(int capacity) { - mCapacity = capacity; - mValues = new GraphValue[capacity]; - } - - /** - * Add new graph value. If there is an existing object, we replace its data with the - * new one. With this, we re-use old objects instead of creating new ones. - */ - void add(float pos, long time) { - mLastIndex = (mLastIndex + 1) % mCapacity; - if (mValues[mLastIndex] == null) { - mValues[mLastIndex] = new GraphValue(pos, time); - } else { - final GraphValue oldValue = mValues[mLastIndex]; - oldValue.mPos = pos; - oldValue.mTime = time; - } - - // If needed, account for new value in the buffer size. - if (mSize != mCapacity) { - mSize++; - } - } - - int getSize() { - return mSize; - } - - GraphValue getFirst() { - final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1; - final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity; - return mValues[firstIndex]; - } - - GraphValue getLast() { - return mValues[mLastIndex]; - } - - void removeFirst() { - mSize--; - } - - /** Returns an iterator pointing at the last value. */ - Iterator<GraphValue> reverseIterator() { - mIteratorIndex = mLastIndex; - mIteratorCount = 1; - return mReverseIterator; - } - } - } } diff --git a/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java new file mode 100644 index 000000000000..95635d925855 --- /dev/null +++ b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java @@ -0,0 +1,342 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input.debug; + +import static android.util.TypedValue.COMPLEX_UNIT_SP; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewConfiguration; + +import java.util.Iterator; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +/** + * Shows a graph with the rotary input values as a function of time. + * The graph gets reset if no action is received for a certain amount of time. + */ +public class RotaryInputGraphView extends View { + + private static final int FRAME_COLOR = 0xbf741b47; + private static final int FRAME_WIDTH_SP = 2; + private static final int FRAME_BORDER_GAP_SP = 10; + private static final int FRAME_TEXT_SIZE_SP = 10; + private static final int FRAME_TEXT_OFFSET_SP = 2; + private static final int GRAPH_COLOR = 0xffff00ff; + private static final int GRAPH_LINE_WIDTH_SP = 1; + private static final int GRAPH_POINT_RADIUS_SP = 4; + private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5); + private static final float DEFAULT_FRAME_CENTER_POSITION = 0; + private static final int MAX_GRAPH_VALUES_SIZE = 400; + /** Maximum time between values so that they are considered part of the same gesture. */ + private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1); + + private final DisplayMetrics mDm; + /** + * Distance in position units (amount scrolled in display pixels) from the center to the + * top/bottom frame lines. + */ + private final float mFrameCenterToBorderDistance; + private final float mScaledVerticalScrollFactor; + private final Locale mDefaultLocale = Locale.getDefault(); + private final Paint mFramePaint = new Paint(); + private final Paint mFrameTextPaint = new Paint(); + private final Paint mGraphLinePaint = new Paint(); + private final Paint mGraphPointPaint = new Paint(); + + private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE); + /** Position at which graph values are placed at the center of the graph. */ + private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; + + public RotaryInputGraphView(Context c) { + super(c); + + mDm = mContext.getResources().getDisplayMetrics(); + // This makes the center-to-border distance equivalent to the display height, meaning + // that the total height of the graph is equivalent to 2x the display height. + mFrameCenterToBorderDistance = mDm.heightPixels; + mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); + + mFramePaint.setColor(FRAME_COLOR); + mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm)); + + mFrameTextPaint.setColor(GRAPH_COLOR); + mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm)); + + mGraphLinePaint.setColor(GRAPH_COLOR); + mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm)); + mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND); + mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND); + + mGraphPointPaint.setColor(GRAPH_COLOR); + mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm)); + mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND); + mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND); + } + + /** + * Reads new scroll axis value and updates the list accordingly. Old positions are + * kept at the front (what you would get with getFirst), while the recent positions are + * kept at the back (what you would get with getLast). Also updates the frame center + * position to handle out-of-bounds cases. + */ + public void addValue(float scrollAxisValue, long eventTime) { + // Remove values that are too old. + while (mGraphValues.getSize() > 0 + && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) { + mGraphValues.removeFirst(); + } + + // If there are no recent values, reset the frame center. + if (mGraphValues.getSize() == 0) { + mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION; + } + + // Handle new value. We multiply the scroll axis value by the scaled scroll factor to + // get the amount of pixels to be scrolled. We also compute the accumulated position + // by adding the current value to the last one (if not empty). + final float displacement = scrollAxisValue * mScaledVerticalScrollFactor; + final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos); + final float pos = prevPos + displacement; + + mGraphValues.add(pos, eventTime); + + // The difference between the distance of the most recent position from the center + // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center + // frame (mFrameCenterToBorderDistance). + final float verticalDiff = Math.abs(pos - mFrameCenterPosition) + - mFrameCenterToBorderDistance; + // If needed, translate frame. + if (verticalDiff > 0) { + final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1; + // Here, we update the center frame position by the exact amount needed for us to + // stay within the maximum allowed distance from the center frame. + mFrameCenterPosition += sign * verticalDiff; + } + + // Redraw canvas. + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // Note: vertical coordinates in Canvas go from top to bottom, + // that is bottomY > middleY > topY. + final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm); + final int topY = verticalMargin; + final int bottomY = getHeight() - verticalMargin; + final int middleY = (topY + bottomY) / 2; + + // Note: horizontal coordinates in Canvas go from left to right, + // that is rightX > leftX. + final int leftX = 0; + final int rightX = getWidth(); + + // Draw the frame, which includes 3 lines that show the maximum, + // minimum and middle positions of the graph. + canvas.drawLine(leftX, topY, rightX, topY, mFramePaint); + canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint); + canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint); + + // Draw the position that each frame line corresponds to. + final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", + mFrameCenterPosition + mFrameCenterToBorderDistance), + leftX, + topY - frameTextOffset, mFrameTextPaint + ); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", mFrameCenterPosition), + leftX, + middleY - frameTextOffset, mFrameTextPaint + ); + canvas.drawText( + String.format(mDefaultLocale, "%.1f", + mFrameCenterPosition - mFrameCenterToBorderDistance), + leftX, + bottomY - frameTextOffset, mFrameTextPaint + ); + + // If there are no graph values to be drawn, stop here. + if (mGraphValues.getSize() == 0) { + return; + } + + // Draw the graph using the times and positions. + // We start at the most recent value (which should be drawn at the right) and move + // to the older values (which should be drawn to the left of more recent ones). Negative + // indices are handled by circuling back to the end of the buffer. + final long mostRecentTime = mGraphValues.getLast().mTime; + float prevCoordX = 0; + float prevCoordY = 0; + float prevAge = 0; + for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) { + final GraphValue value = iter.next(); + + final int age = (int) (mostRecentTime - value.mTime); + final float pos = value.mPos; + + // We get the horizontal coordinate in time units from left to right with + // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas + // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL) + // and by multiplying it by the canvas length (rightX - leftX). Finally, we + // offset the coordinate by adding it to leftX. + final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age) + / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX); + + // We get the vertical coordinate in position units from middle to top with + // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas + // units by dividing it by half of the position-domain length + // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas + // length (middleY - topY). Finally, we offset the coordinate by subtracting + // it from middleY (we can't "add" here because the coordinate grows from top + // to bottom). + final float coordY = middleY - ((pos - mFrameCenterPosition) + / mFrameCenterToBorderDistance) * (middleY - topY); + + // Draw a point for this value. + canvas.drawPoint(coordX, coordY, mGraphPointPaint); + + // If this value is part of the same gesture as the previous one, draw a line + // between them. We ignore the first value (with age = 0). + if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) { + canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint); + } + + prevCoordX = coordX; + prevCoordY = coordY; + prevAge = age; + } + } + + public float getFrameCenterPosition() { + return mFrameCenterPosition; + } + + /** + * Converts a dimension in scaled pixel units to integer display pixels. + */ + private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) { + return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm); + } + + /** + * Holds data needed to draw each entry in the graph. + */ + private static class GraphValue { + /** Position. */ + float mPos; + /** Time when this value was added. */ + long mTime; + + GraphValue(float pos, long time) { + this.mPos = pos; + this.mTime = time; + } + } + + /** + * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the + * old values with new ones to avoid creating new objects. + */ + private static class CyclicBuffer { + private final GraphValue[] mValues; + private final int mCapacity; + private int mSize = 0; + private int mLastIndex = 0; + + // The iteration index and counter are here to make it easier to reset them. + /** Determines the value currently pointed by the iterator. */ + private int mIteratorIndex; + /** Counts how many values have been iterated through. */ + private int mIteratorCount; + + /** Used traverse the values in reverse order. */ + private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() { + @Override + public boolean hasNext() { + return mIteratorCount <= mSize; + } + + @Override + public GraphValue next() { + // Returns the value currently pointed by the iterator and moves the iterator to + // the previous one. + mIteratorCount++; + return mValues[(mIteratorIndex-- + mCapacity) % mCapacity]; + } + }; + + CyclicBuffer(int capacity) { + mCapacity = capacity; + mValues = new GraphValue[capacity]; + } + + /** + * Add new graph value. If there is an existing object, we replace its data with the + * new one. With this, we re-use old objects instead of creating new ones. + */ + void add(float pos, long time) { + mLastIndex = (mLastIndex + 1) % mCapacity; + if (mValues[mLastIndex] == null) { + mValues[mLastIndex] = new GraphValue(pos, time); + } else { + final GraphValue oldValue = mValues[mLastIndex]; + oldValue.mPos = pos; + oldValue.mTime = time; + } + + // If needed, account for new value in the buffer size. + if (mSize != mCapacity) { + mSize++; + } + } + + int getSize() { + return mSize; + } + + GraphValue getFirst() { + final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1; + final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity; + return mValues[firstIndex]; + } + + GraphValue getLast() { + return mValues[mLastIndex]; + } + + void removeFirst() { + mSize--; + } + + /** Returns an iterator pointing at the last value. */ + Iterator<GraphValue> reverseIterator() { + mIteratorIndex = mLastIndex; + mIteratorCount = 1; + return mReverseIterator; + } + } +} diff --git a/services/core/java/com/android/server/input/debug/RotaryInputValueView.java b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java new file mode 100644 index 000000000000..9fadac57cef9 --- /dev/null +++ b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java @@ -0,0 +1,103 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input.debug; + +import static android.util.TypedValue.COMPLEX_UNIT_SP; + +import android.content.Context; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Typeface; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.ViewConfiguration; +import android.widget.TextView; + +import com.android.internal.R; + +import java.util.Locale; + +/** + * Draws the most recent rotary input value and indicates whether the source is active. + */ +public class RotaryInputValueView extends TextView { + + private static final int INACTIVE_TEXT_COLOR = 0xffff00ff; + private static final int ACTIVE_TEXT_COLOR = 0xff420f28; + private static final int TEXT_SIZE_SP = 8; + private static final int SIDE_PADDING_SP = 4; + /** Determines how long the active status lasts. */ + private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */; + private static final ColorFilter ACTIVE_BACKGROUND_FILTER = + new ColorMatrixColorFilter(new float[]{ + 0, 0, 0, 0, 255, // red + 0, 0, 0, 0, 0, // green + 0, 0, 0, 0, 255, // blue + 0, 0, 0, 0, 200 // alpha + }); + + private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false); + private final float mScaledVerticalScrollFactor; + private final Locale mDefaultLocale = Locale.getDefault(); + + public RotaryInputValueView(Context c) { + super(c); + + DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); + mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor(); + + setText(getFormattedValue(0)); + setTextColor(INACTIVE_TEXT_COLOR); + setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm)); + setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0, + applyDimensionSp(SIDE_PADDING_SP, dm), 0); + setTypeface(null, Typeface.BOLD); + setBackgroundResource(R.drawable.focus_event_rotary_input_background); + } + + /** Updates the shown text with the formatted value. */ + public void updateValue(float value) { + removeCallbacks(mUpdateActivityStatusCallback); + + setText(getFormattedValue(value * mScaledVerticalScrollFactor)); + + updateActivityStatus(true); + postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION); + } + + /** Updates whether or not there's active rotary input. */ + public void updateActivityStatus(boolean active) { + if (active) { + setTextColor(ACTIVE_TEXT_COLOR); + getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER); + } else { + setTextColor(INACTIVE_TEXT_COLOR); + getBackground().clearColorFilter(); + } + } + + private String getFormattedValue(float value) { + return String.format(mDefaultLocale, "%s%.1f", value < 0 ? "-" : "+", Math.abs(value)); + } + + /** + * Converts a dimension in scaled pixel units to integer display pixels. + */ + private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) { + return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm); + } +} diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index 7726f40fa2ae..dbbbed31df76 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -57,13 +57,13 @@ final class HandwritingEventReceiverSurface { InputConfig.NOT_FOCUSABLE | InputConfig.NOT_TOUCHABLE | InputConfig.SPY - | InputConfig.INTERCEPTS_STYLUS - | InputConfig.TRUSTED_OVERLAY; + | InputConfig.INTERCEPTS_STYLUS; // Configure the surface to receive stylus events across the entire display. mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE); t.setPosition(mInputSurface, 0, 0); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 93c66a19c89d..08cf3f775cc3 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -1431,13 +1431,17 @@ public class ContextHubService extends IContextHubService.Stub { mContextHubWrapper.onBtMainSettingChanged(btEnabled); } } else { - Log.d(TAG, "BT adapter not available. Defaulting to disabled"); - if (forceUpdate || mIsBtMainEnabled) { - mIsBtMainEnabled = false; + Log.d(TAG, "BT adapter not available. Getting permissions from user settings"); + boolean btEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.BLUETOOTH_ON, 0) == 1; + boolean btScanEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1; + if (forceUpdate || mIsBtMainEnabled != btEnabled) { + mIsBtMainEnabled = btEnabled; mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled); } - if (forceUpdate || mIsBtScanningEnabled) { - mIsBtScanningEnabled = false; + if (forceUpdate || mIsBtScanningEnabled != btScanEnabled) { + mIsBtScanningEnabled = btScanEnabled; mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled); } } diff --git a/services/core/java/com/android/server/notification/NotificationBitmapJobService.java b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java index 4335a1dcfa75..e1a07076cb2d 100644 --- a/services/core/java/com/android/server/notification/NotificationBitmapJobService.java +++ b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java @@ -29,7 +29,12 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; -import java.util.Calendar; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.time.ZoneId; /** * This service runs everyday at 2am local time to remove expired bitmaps. @@ -69,26 +74,25 @@ public class NotificationBitmapJobService extends JobService { * @return Milliseconds until the next time the job should run. */ private static long getRunAfterMs() { - Calendar cal = Calendar.getInstance(); // Uses local time zone - final long now = cal.getTimeInMillis(); + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime now = Instant.now().atZone(zoneId); - cal.set(Calendar.HOUR_OF_DAY, 2); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.MILLISECOND, 0); - final long today2AM = cal.getTimeInMillis(); + LocalDate today = now.toLocalDate(); + LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0); - cal.add(Calendar.DAY_OF_YEAR, 1); - final long tomorrow2AM = cal.getTimeInMillis(); + ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId); + ZonedDateTime tomorrow2AM = today2AM.plusDays(1); return getTimeUntilRemoval(now, today2AM, tomorrow2AM); } @VisibleForTesting - static long getTimeUntilRemoval(long now, long today2AM, long tomorrow2AM) { - if (now < today2AM) { - return today2AM - now; + static long getTimeUntilRemoval(ZonedDateTime now, ZonedDateTime today2AM, + ZonedDateTime tomorrow2AM) { + if (Duration.between(now, today2AM).isNegative()) { + return Duration.between(now, tomorrow2AM).toMillis(); } - return tomorrow2AM - now; + return Duration.between(now, today2AM).toMillis(); } @Override diff --git a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java index 3776ad7a0799..c9317d17be37 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java @@ -27,6 +27,7 @@ import android.content.Context; import android.os.CancellationSignal; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import java.util.concurrent.TimeUnit; @@ -77,5 +78,11 @@ public class NotificationHistoryJobService extends JobService { } return false; } + + @Override + @VisibleForTesting + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index db69d93583d4..87c30674bfb7 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -396,7 +396,7 @@ public class NotificationManagerService extends SystemService { static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7; static final int MESSAGE_ON_PACKAGE_CHANGED = 8; - static final long BITMAP_EXPIRATION_TIME_MS = TimeUnit.HOURS.toMillis(24); + static final Duration BITMAP_DURATION = Duration.ofHours(24); // ranking thread messages private static final int MESSAGE_RECONSIDER_RANKING = 1000; @@ -6705,7 +6705,7 @@ public class NotificationManagerService extends SystemService { final long timePostedMs = r.getSbn().getPostTime(); final long timeNowMs = System.currentTimeMillis(); - if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_EXPIRATION_TIME_MS)) { + if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_DURATION.toMillis())) { removeBitmapAndRepost(r); } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 82d3e9157986..68a8e40d0528 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -286,6 +286,8 @@ public class UserManagerService extends IUserManager.Stub { private static final int USER_VERSION = 11; + private static final int MAX_USER_STRING_LENGTH = 500; + private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms static final int WRITE_USER_MSG = 1; @@ -4374,15 +4376,17 @@ public class UserManagerService extends IUserManager.Stub { // Write seed data if (userData.persistSeedData) { if (userData.seedAccountName != null) { - serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, userData.seedAccountName); + serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, + truncateString(userData.seedAccountName)); } if (userData.seedAccountType != null) { - serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType); + serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, + truncateString(userData.seedAccountType)); } } if (userInfo.name != null) { serializer.startTag(null, TAG_NAME); - serializer.text(userInfo.name); + serializer.text(truncateString(userInfo.name)); serializer.endTag(null, TAG_NAME); } synchronized (mRestrictionsLock) { @@ -4431,6 +4435,13 @@ public class UserManagerService extends IUserManager.Stub { serializer.endDocument(); } + private String truncateString(String original) { + if (original == null || original.length() <= MAX_USER_STRING_LENGTH) { + return original; + } + return original.substring(0, MAX_USER_STRING_LENGTH); + } + /* * Writes the user list file in this format: * @@ -4869,7 +4880,7 @@ public class UserManagerService extends IUserManager.Stub { @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t, @Nullable Object token) throws UserManager.CheckedUserOperationException { - + String truncatedName = truncateString(name); final UserTypeDetails userTypeDetails = mUserTypes.get(userType); if (userTypeDetails == null) { throwCheckedUserOperationException( @@ -4904,8 +4915,8 @@ public class UserManagerService extends IUserManager.Stub { // Try to use a pre-created user (if available). if (!preCreate && parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails)) { - final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, name, - token); + final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, + truncatedName, token); if (preCreatedUser != null) { return preCreatedUser; } @@ -5000,7 +5011,7 @@ public class UserManagerService extends IUserManager.Stub { flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE; } - userInfo = new UserInfo(userId, name, null, flags, userType); + userInfo = new UserInfo(userId, truncatedName, null, flags, userType); userInfo.serialNumber = mNextSerialNumber++; userInfo.creationTime = getCreationTime(); userInfo.partial = true; @@ -6456,8 +6467,8 @@ public class UserManagerService extends IUserManager.Stub { Slog.e(LOG_TAG, "No such user for settings seed data u=" + userId); return; } - userData.seedAccountName = accountName; - userData.seedAccountType = accountType; + userData.seedAccountName = truncateString(accountName); + userData.seedAccountType = truncateString(accountType); userData.seedAccountOptions = accountOptions; userData.persistSeedData = persist; } diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index ebc7163cd05c..b83421fe78d7 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -42,6 +42,7 @@ import android.os.PackageTagsList; import android.os.Process; import android.os.SystemProperties; import android.os.UserHandle; +import android.permission.flags.Flags; import android.service.voice.VoiceInteractionManagerInternal; import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; import android.text.TextUtils; @@ -75,9 +76,6 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat private static final boolean SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED = SystemProperties.getBoolean("ro.hotword.detection_service_required", false); - //TODO(b/289087412): import this from the flag value in set up in device config. - private static final boolean IS_VOICE_ACTIVATION_OP_ENABLED = false; - @NonNull private final Object mLock = new Object(); @@ -212,7 +210,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat * @return the op that should be noted for the voice activations of the app by detected hotword. */ public static int getVoiceActivationOp() { - if (IS_VOICE_ACTIVATION_OP_ENABLED) { + if (Flags.voiceActivationPermissionApis()) { return OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; } return OP_RECORD_AUDIO_HOTWORD; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 097656cac7f7..3a6664a72439 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -333,6 +333,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0; static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1; + // must match: config_shortPressOnSettingsBehavior in config.xml + static final int SHORT_PRESS_SETTINGS_NOTHING = 0; + static final int SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL = 1; + static final int LAST_SHORT_PRESS_SETTINGS_BEHAVIOR = SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL; + static final int PENDING_KEY_NULL = -1; // Must match: config_shortPressOnStemPrimaryBehavior in config.xml @@ -611,6 +616,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // What we do when the user double-taps on home int mDoubleTapOnHomeBehavior; + // What we do when the user presses on settings + int mShortPressOnSettingsBehavior; + // Must match config_primaryShortPressTargetActivity in config.xml ComponentName mPrimaryShortPressTargetActivity; @@ -2766,6 +2774,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE; } + + mShortPressOnSettingsBehavior = res.getInteger( + com.android.internal.R.integer.config_shortPressOnSettingsBehavior); + if (mShortPressOnSettingsBehavior < SHORT_PRESS_SETTINGS_NOTHING + || mShortPressOnSettingsBehavior > LAST_SHORT_PRESS_SETTINGS_BEHAVIOR) { + mShortPressOnSettingsBehavior = SHORT_PRESS_SETTINGS_NOTHING; + } } private void updateSettings() { @@ -3632,6 +3647,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in" + " interceptKeyBeforeQueueing"); return true; + case KeyEvent.KEYCODE_SETTINGS: + if (mShortPressOnSettingsBehavior == SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL) { + if (!down) { + toggleNotificationPanel(); + logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL); + } + return true; + } + break; } if (isValidGlobalKey(keyCode) && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { @@ -6272,6 +6296,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print("mLongPressOnPowerBehavior="); pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior)); pw.print(prefix); + pw.print("mShortPressOnSettingsBehavior="); + pw.println(shortPressOnSettingsBehaviorToString(mShortPressOnSettingsBehavior)); + pw.print(prefix); pw.print("mLongPressOnPowerAssistantTimeoutMs="); pw.println(mLongPressOnPowerAssistantTimeoutMs); pw.print(prefix); @@ -6470,6 +6497,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private static String shortPressOnSettingsBehaviorToString(int behavior) { + switch (behavior) { + case SHORT_PRESS_SETTINGS_NOTHING: + return "SHORT_PRESS_SETTINGS_NOTHING"; + case SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL: + return "SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL"; + default: + return Integer.toString(behavior); + } + } + private static String veryLongPressOnPowerBehaviorToString(int behavior) { switch (behavior) { case VERY_LONG_PRESS_POWER_NOTHING: diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java index 3d89afaae88f..becbbf2ea76a 100644 --- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java +++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java @@ -25,6 +25,8 @@ import android.os.VibratorInfo; import android.util.Slog; import android.util.SparseArray; import android.view.HapticFeedbackConstants; +import android.view.flags.FeatureFlags; +import android.view.flags.FeatureFlagsImpl; import com.android.internal.annotations.VisibleForTesting; @@ -54,6 +56,7 @@ public final class HapticFeedbackVibrationProvider { // If present and valid, a vibration here will be used for an effect. // Otherwise, the system's default vibration will be used. @Nullable private final SparseArray<VibrationEffect> mHapticCustomizations; + private final FeatureFlags mViewFeatureFlags; /** @hide */ public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) { @@ -62,14 +65,16 @@ public final class HapticFeedbackVibrationProvider { /** @hide */ public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) { - this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo)); + this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo), + new FeatureFlagsImpl()); } /** @hide */ @VisibleForTesting HapticFeedbackVibrationProvider( Resources res, VibratorInfo vibratorInfo, - @Nullable SparseArray<VibrationEffect> hapticCustomizations) { + @Nullable SparseArray<VibrationEffect> hapticCustomizations, + FeatureFlags viewFeatureFlags) { mVibratorInfo = vibratorInfo; mHapticTextHandleEnabled = res.getBoolean( com.android.internal.R.bool.config_enableHapticTextHandle); @@ -78,6 +83,7 @@ public final class HapticFeedbackVibrationProvider { hapticCustomizations = null; } mHapticCustomizations = hapticCustomizations; + mViewFeatureFlags = viewFeatureFlags; mSafeModeEnabledVibrationEffect = effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED) @@ -201,12 +207,16 @@ public final class HapticFeedbackVibrationProvider { default: attrs = TOUCH_VIBRATION_ATTRIBUTES; } + + int flags = 0; if (bypassVibrationIntensitySetting) { - attrs = new VibrationAttributes.Builder(attrs) - .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .build(); + flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF; + } + if (shouldBypassInterruptionPolicy(effectId, mViewFeatureFlags)) { + flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY; } - return attrs; + + return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build(); } /** Dumps relevant state. */ @@ -295,4 +305,19 @@ public final class HapticFeedbackVibrationProvider { return null; } } + + private static boolean shouldBypassInterruptionPolicy( + int effectId, FeatureFlags viewFeatureFlags) { + switch (effectId) { + case HapticFeedbackConstants.SCROLL_TICK: + case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: + case HapticFeedbackConstants.SCROLL_LIMIT: + // The SCROLL_* constants should bypass interruption filter, so that scroll haptics + // can play regardless of focus modes like DND. Guard this behavior by the feature + // flag controlling the general scroll feedback APIs. + return viewFeatureFlags.scrollFeedbackApi(); + default: + return false; + } + } } diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index ee3d697cddb8..45bd1521bc35 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -448,6 +448,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { String reason, IBinder token) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason); try { + attrs = fixupVibrationAttributes(attrs, effect); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.VIBRATE, "vibrate"); return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token); @@ -457,7 +458,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg, - @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, + @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason); try { @@ -468,7 +469,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } private HalVibration vibrateInternal(int uid, int displayId, String opPkg, - @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, + @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token) { if (token == null) { Slog.e(TAG, "token must not be null"); @@ -478,7 +479,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (!isEffectValid(effect)) { return null; } - attrs = fixupVibrationAttributes(attrs, effect); // Create Vibration.Stats as close to the received request as possible, for tracking. HalVibration vib = new HalVibration(token, effect, new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason)); diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java index 10a2b9717555..05da9dfe7921 100644 --- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java +++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java @@ -15,6 +15,9 @@ */ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -23,6 +26,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.graphics.Point; @@ -78,7 +82,14 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, protected final WindowManagerService mService; protected final float mHighResSnapshotScale; - private final Rect mTmpRect = new Rect(); + + /** + * The transition change info of the target to capture screenshot. It is only non-null when + * capturing a snapshot with a given change info. It must be cleared after + * {@link #recordSnapshotInner} is done. + */ + protected Transition.ChangeInfo mCurrentChangeInfo; + /** * Flag indicating whether we are running on an Android TV device. */ @@ -137,41 +148,35 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, protected abstract boolean use16BitFormat(); /** - * This is different than {@link #recordSnapshotInner(TYPE, boolean)} because it doesn't store + * This is different than {@link #recordSnapshotInner(TYPE)} because it doesn't store * the snapshot to the cache and returns the TaskSnapshot immediately. * * This is only used for testing so the snapshot content can be verified. */ - // TODO(b/264551777): clean up the "snapshotHome" argument @VisibleForTesting - TaskSnapshot captureSnapshot(TYPE source, boolean snapshotHome) { + TaskSnapshot captureSnapshot(TYPE source) { final TaskSnapshot snapshot; - if (snapshotHome) { - snapshot = snapshot(source); - } else { - switch (getSnapshotMode(source)) { - case SNAPSHOT_MODE_NONE: - return null; - case SNAPSHOT_MODE_APP_THEME: - snapshot = drawAppThemeSnapshot(source); - break; - case SNAPSHOT_MODE_REAL: - snapshot = snapshot(source); - break; - default: - snapshot = null; - break; - } + switch (getSnapshotMode(source)) { + case SNAPSHOT_MODE_NONE: + return null; + case SNAPSHOT_MODE_APP_THEME: + snapshot = drawAppThemeSnapshot(source); + break; + case SNAPSHOT_MODE_REAL: + snapshot = snapshot(source); + break; + default: + snapshot = null; + break; } return snapshot; } - final TaskSnapshot recordSnapshotInner(TYPE source, boolean allowSnapshotHome) { + final TaskSnapshot recordSnapshotInner(TYPE source) { if (shouldDisableSnapshots()) { return null; } - final boolean snapshotHome = allowSnapshotHome && source.isActivityTypeHome(); - final TaskSnapshot snapshot = captureSnapshot(source, snapshotHome); + final TaskSnapshot snapshot = captureSnapshot(source); if (snapshot == null) { return null; } @@ -189,30 +194,32 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, @VisibleForTesting int getSnapshotMode(TYPE source) { - final ActivityRecord topChild = getTopActivity(source); - if (!source.isActivityTypeStandardOrUndefined() && !source.isActivityTypeAssistant()) { + final int type = source.getActivityType(); + if (type == ACTIVITY_TYPE_RECENTS || type == ACTIVITY_TYPE_DREAM) { return SNAPSHOT_MODE_NONE; - } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) { - return SNAPSHOT_MODE_APP_THEME; - } else { + } + if (type == ACTIVITY_TYPE_HOME) { return SNAPSHOT_MODE_REAL; } + final ActivityRecord topChild = getTopActivity(source); + if (topChild != null && topChild.shouldUseAppThemeSnapshot()) { + return SNAPSHOT_MODE_APP_THEME; + } + return SNAPSHOT_MODE_REAL; } @Nullable TaskSnapshot snapshot(TYPE source) { - return snapshot(source, PixelFormat.UNKNOWN); - } - - @Nullable - TaskSnapshot snapshot(TYPE source, int pixelFormat) { TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); - if (!prepareTaskSnapshot(source, pixelFormat, builder)) { + final Rect crop = prepareTaskSnapshot(source, builder); + if (crop == null) { // Failed some pre-req. Has been logged. return null; } - final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = - createSnapshot(source, builder); + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot"); + final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshot(source, + mHighResSnapshotScale, crop, builder); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); if (screenshotBuffer == null) { // Failed to acquire image. Has been logged. return null; @@ -225,37 +232,13 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, @Nullable ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source, - TaskSnapshot.Builder builder) { - Point taskSize = new Point(); - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot"); - final ScreenCapture.ScreenshotHardwareBuffer taskSnapshot = createSnapshot(source, - mHighResSnapshotScale, builder.getPixelFormat(), taskSize, builder); - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - builder.setTaskSize(taskSize); - return taskSnapshot; - } - - @Nullable - ScreenCapture.ScreenshotHardwareBuffer createSnapshot(@NonNull TYPE source, - float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) { + float scaleFraction, Rect crop, TaskSnapshot.Builder builder) { if (source.getSurfaceControl() == null) { if (DEBUG_SCREENSHOT) { Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + source); } return null; } - mTmpRect.setEmpty(); - if (source.mTransitionController.inFinishingTransition(source)) { - final Transition.ChangeInfo changeInfo = source.mTransitionController - .mFinishingTransition.mChanges.get(source); - if (changeInfo != null) { - mTmpRect.set(changeInfo.mAbsoluteBounds); - } - } - if (mTmpRect.isEmpty()) { - source.getBounds(mTmpRect); - } - mTmpRect.offsetTo(0, 0); SurfaceControl[] excludeLayers; final WindowState imeWindow = source.getDisplayContent().mInputMethodWindow; // Exclude IME window snapshot when IME isn't proper to attach to app. @@ -281,12 +264,8 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible()); final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = ScreenCapture.captureLayersExcluding( - source.getSurfaceControl(), mTmpRect, scaleFraction, - pixelFormat, excludeLayers); - if (outTaskSize != null) { - outTaskSize.x = mTmpRect.width(); - outTaskSize.y = mTmpRect.height(); - } + source.getSurfaceControl(), crop, scaleFraction, + builder.getPixelFormat(), excludeLayers); final HardwareBuffer buffer = screenshotBuffer == null ? null : screenshotBuffer.getHardwareBuffer(); if (isInvalidHardwareBuffer(buffer)) { @@ -305,17 +284,16 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, * information from the task and populates the builder. * * @param source the window to capture - * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to - * automatically select * @param builder the snapshot builder to populate * * @return true if the state of the task is ok to proceed */ @VisibleForTesting - boolean prepareTaskSnapshot(TYPE source, int pixelFormat, TaskSnapshot.Builder builder) { + @Nullable + Rect prepareTaskSnapshot(TYPE source, TaskSnapshot.Builder builder) { final Pair<ActivityRecord, WindowState> result = checkIfReadyToSnapshot(source); if (result == null) { - return false; + return null; } final ActivityRecord activity = result.first; final WindowState mainWindow = result.second; @@ -329,6 +307,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setLetterboxInsets(letterboxInsets); final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE; final boolean isShowWallpaper = mainWindow.hasWallpaper(); + int pixelFormat = builder.getPixelFormat(); if (pixelFormat == PixelFormat.UNKNOWN) { pixelFormat = use16BitFormat() && activity.fillsParent() && !(isWindowTranslucent && isShowWallpaper) @@ -340,11 +319,29 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setTopActivityComponent(activity.mActivityComponent); builder.setPixelFormat(pixelFormat); builder.setIsTranslucent(isTranslucent); - builder.setOrientation(activity.getTask().getConfiguration().orientation); - builder.setRotation(activity.getTask().getDisplayContent().getRotation()); builder.setWindowingMode(source.getWindowingMode()); builder.setAppearance(getAppearance(source)); - return true; + + final Configuration taskConfig = activity.getTask().getConfiguration(); + final int displayRotation = taskConfig.windowConfiguration.getDisplayRotation(); + final Rect outCrop = new Rect(); + final Transition.ChangeInfo changeInfo = mCurrentChangeInfo; + if (changeInfo != null && changeInfo.mRotation != displayRotation) { + // For example, the source is closing and display rotation changes at the same time. + // The snapshot should record the state in previous rotation. + outCrop.set(changeInfo.mAbsoluteBounds); + builder.setRotation(changeInfo.mRotation); + builder.setOrientation(changeInfo.mAbsoluteBounds.height() + >= changeInfo.mAbsoluteBounds.width() + ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE); + } else { + outCrop.set(taskConfig.windowConfiguration.getBounds()); + builder.setRotation(displayRotation); + builder.setOrientation(taskConfig.orientation); + } + outCrop.offsetTo(0, 0); + builder.setTaskSize(new Point(outCrop.right, outCrop.bottom)); + return outCrop; } /** diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index 148bf9bfdcd9..01b8bf794dae 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -280,7 +280,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity); } - final TaskSnapshot snapshot = recordSnapshotInner(activity, false /* allowSnapshotHome */); + final TaskSnapshot snapshot = recordSnapshotInner(activity); if (snapshot != null) { final int code = getSystemHashCode(activity); addUserSavedFile(code, activity.mUserId, snapshot); diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index c39b266a7701..4a5311b14397 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -577,7 +577,8 @@ public class ActivityStartController { .getRootTask(WINDOWING_MODE_UNDEFINED, activityType); if (rootTask == null) return false; final ActivityRecord r = rootTask.topRunningActivity(); - if (r == null || r.isVisibleRequested() || !r.attachedToProcess() + if (r == null || (r.isVisibleRequested() && rootTask.isTopRootTaskInDisplayArea()) + || !r.attachedToProcess() || !r.mActivityComponent.equals(intent.getComponent()) || !mService.isCallerRecents(r.getUid()) // Recents keeps invisible while device is locked. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a450b4d71a7e..0b673211a1c9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3858,11 +3858,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return null; } if (updateCache) { - return mWindowManager.mTaskSnapshotController.recordSnapshot(task, - true /* snapshotHome */); + return mWindowManager.mTaskSnapshotController.recordSnapshot(task); } else { - return mWindowManager.mTaskSnapshotController.captureSnapshot(task, - true /* snapshotHome */); + return mWindowManager.mTaskSnapshotController.captureSnapshot(task); } } } finally { @@ -6450,19 +6448,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (!restarting && hasVisibleActivities) { deferWindowLayout(); try { - final Task topTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); - if (topTask != null - && topTask.topRunningActivity(true /* focusableOnly */) == null) { - topTask.adjustFocusToNextFocusableTask("handleAppDied"); - } - if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) { - // If there was nothing to resume, and we are not already restarting - // this process, but there is a visible activity that is hosted by the - // process...then make sure all visible activities are running, taking - // care of restarting this process. - mRootWindowContainer.ensureActivitiesVisible(null, 0, - !PRESERVE_WINDOWS); - } + mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed( + "handleAppDied"); } finally { continueWindowLayout(); } @@ -6903,7 +6890,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) { synchronized (mGlobalLock) { - return mRootWindowContainer.finishTopCrashedActivities(crashedApp, reason); + deferWindowLayout(); + try { + final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities( + crashedApp, reason); + if (finishedTask != null) { + mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(reason); + return finishedTask.mTaskId; + } + return INVALID_TASK_ID; + } finally { + continueWindowLayout(); + } } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index c2885dab12f0..42376685498a 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -163,7 +163,8 @@ class BackNavigationController { if (window == null) { EmbeddedWindowController.EmbeddedWindow embeddedWindow = - wmService.mEmbeddedWindowController.getByFocusToken(focusedWindowToken); + wmService.mEmbeddedWindowController.getByInputTransferToken( + focusedWindowToken); if (embeddedWindow != null) { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window is embeddedWindow. Dispatch KEYCODE_BACK."); diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index 2439159164fd..73bcc8d94252 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -66,29 +66,29 @@ import java.util.Map; public final class CompatModePackages { /** - * {@link CompatModePackages#DOWNSCALED_INVERSE} is the gatekeeper of all per-app buffer inverse - * downscale changes. Enabling this change will allow the following scaling factors: - * {@link CompatModePackages#DOWNSCALE_90} - * {@link CompatModePackages#DOWNSCALE_85} - * {@link CompatModePackages#DOWNSCALE_80} - * {@link CompatModePackages#DOWNSCALE_75} - * {@link CompatModePackages#DOWNSCALE_70} - * {@link CompatModePackages#DOWNSCALE_65} - * {@link CompatModePackages#DOWNSCALE_60} - * {@link CompatModePackages#DOWNSCALE_55} - * {@link CompatModePackages#DOWNSCALE_50} - * {@link CompatModePackages#DOWNSCALE_45} - * {@link CompatModePackages#DOWNSCALE_40} - * {@link CompatModePackages#DOWNSCALE_35} - * {@link CompatModePackages#DOWNSCALE_30} + * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is the gatekeeper of all per-app buffer + * inverse downscale changes. Enabling this change will allow the following scaling factors: + * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> + * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> + * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> + * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> + * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> + * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> + * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> + * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> + * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> + * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> + * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> + * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> + * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> * - * If {@link CompatModePackages#DOWNSCALED_INVERSE} is enabled for an app package, then the app - * will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both 1/0.8 and - * 1/0.7 (* 100%) were enabled. + * If <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is enabled for an app package, then + * the app will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both + * 1/0.8 and 1/0.7 (* 100%) were enabled. * - * When both {@link CompatModePackages#DOWNSCALED_INVERSE} - * and {@link CompatModePackages#DOWNSCALED} are enabled, then - * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence. + * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> + * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then + * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. */ @ChangeId @Disabled @@ -96,29 +96,29 @@ public final class CompatModePackages { public static final long DOWNSCALED_INVERSE = 273564678L; // This is a Bug ID. /** - * {@link CompatModePackages#DOWNSCALED} is the gatekeeper of all per-app buffer downscaling + * <a href="#DOWNSCALED">DOWNSCALED</a> is the gatekeeper of all per-app buffer downscaling * changes. Enabling this change will allow the following scaling factors: - * {@link CompatModePackages#DOWNSCALE_90} - * {@link CompatModePackages#DOWNSCALE_85} - * {@link CompatModePackages#DOWNSCALE_80} - * {@link CompatModePackages#DOWNSCALE_75} - * {@link CompatModePackages#DOWNSCALE_70} - * {@link CompatModePackages#DOWNSCALE_65} - * {@link CompatModePackages#DOWNSCALE_60} - * {@link CompatModePackages#DOWNSCALE_55} - * {@link CompatModePackages#DOWNSCALE_50} - * {@link CompatModePackages#DOWNSCALE_45} - * {@link CompatModePackages#DOWNSCALE_40} - * {@link CompatModePackages#DOWNSCALE_35} - * {@link CompatModePackages#DOWNSCALE_30} + * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> + * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> + * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> + * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> + * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> + * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> + * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> + * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> + * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> + * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> + * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> + * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> + * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> * - * If {@link CompatModePackages#DOWNSCALED} is enabled for an app package, then the app will be + * If <a href="#DOWNSCALED">DOWNSCALED</a> is enabled for an app package, then the app will be * forcibly resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were * enabled. * - * When both {@link CompatModePackages#DOWNSCALED_INVERSE} - * and {@link CompatModePackages#DOWNSCALED} are enabled, then - * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence. + * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> + * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then + * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. */ @ChangeId @Disabled @@ -126,12 +126,13 @@ public final class CompatModePackages { public static final long DOWNSCALED = 168419799L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_90} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> for a package will force the app to assume it's * running on a display with 90% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 111.11% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 111.11% the vertical and horizontal resolution of + * the real display */ @ChangeId @Disabled @@ -139,12 +140,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_90 = 182811243L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_85} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> for a package will force the app to assume it's * running on a display with 85% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 117.65% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 117.65% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -152,12 +154,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_85 = 189969734L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_80} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> for a package will force the app to assume it's * running on a display with 80% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 125% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 125% the vertical and horizontal resolution of the real + * display */ @ChangeId @Disabled @@ -165,12 +168,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_80 = 176926753L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_75} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> for a package will force the app to assume it's * running on a display with 75% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 133.33% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 133.33% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -178,12 +182,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_75 = 189969779L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_70} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> for a package will force the app to assume it's * running on a display with 70% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 142.86% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 142.86% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -191,12 +196,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_70 = 176926829L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_65} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> for a package will force the app to assume it's * running on a display with 65% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 153.85% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 153.85% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -204,12 +210,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_65 = 189969744L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_60} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> for a package will force the app to assume it's * running on a display with 60% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 166.67% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 166.67% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -217,12 +224,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_60 = 176926771L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_55} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> for a package will force the app to assume it's * running on a display with 55% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 181.82% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 181.82% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -230,12 +238,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_55 = 189970036L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_50} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> for a package will force the app to assume it's * running on a display with 50% vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 200% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 200% the vertical and horizontal resolution of the real + * display */ @ChangeId @Disabled @@ -243,12 +252,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_50 = 176926741L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_45} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> for a package will force the app to assume it's * running on a display with 45% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 222.22% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 222.22% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -256,12 +266,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_45 = 189969782L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_40} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> for a package will force the app to assume it's * running on a display with 40% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 250% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 250% the vertical and horizontal resolution of the real + * display */ @ChangeId @Disabled @@ -269,12 +280,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_40 = 189970038L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_35} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> for a package will force the app to assume it's * running on a display with 35% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 285.71% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 285.71% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled @@ -282,12 +294,13 @@ public final class CompatModePackages { public static final long DOWNSCALE_35 = 189969749L; /** - * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id - * {@link CompatModePackages#DOWNSCALE_30} for a package will force the app to assume it's + * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id + * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> for a package will force the app to assume it's * running on a display with 30% the vertical and horizontal resolution of the real display. * - * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's - * running on a display with 333.33% the vertical and horizontal resolution of the real display + * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to + * assume it's running on a display with 333.33% the vertical and horizontal resolution of the + * real display */ @ChangeId @Disabled diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 0f1a1053716e..7af4aadb2f0e 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -48,7 +48,6 @@ import android.hardware.input.InputManagerGlobal; import android.os.Binder; import android.os.Build; import android.os.IBinder; -import android.os.InputConfig; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -186,6 +185,10 @@ class DragState { // Crop the input surface to the display size. mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); + // Make trusted overlay to not block any touches while D&D ongoing and allowing + // touches to pass through to windows underneath. This allows user to interact with the + // UI to navigate while dragging. + h.setTrustedOverlay(mTransaction, mInputSurface, true); mTransaction.show(mInputSurface) .setInputWindowInfo(mInputSurface, h) .setLayer(mInputSurface, Integer.MAX_VALUE) @@ -377,11 +380,6 @@ class DragState { mDragWindowHandle.ownerUid = MY_UID; mDragWindowHandle.scaleFactor = 1.0f; - // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing - // touches to pass through to windows underneath. This allows user to interact with the - // UI to navigate while dragging. - mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY; - // The drag window cannot receive new touches. mDragWindowHandle.touchableRegion.setEmpty(); diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index c9bae127b800..275396f459bd 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -45,8 +45,8 @@ class EmbeddedWindowController { private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM; /* maps input token to an embedded window */ private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>(); - private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken = - new ArrayMap<>(); + private ArrayMap<IBinder /*input transfer token */, EmbeddedWindow> + mWindowsByInputTransferToken = new ArrayMap<>(); private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken = new ArrayMap<>(); private final Object mGlobalLock; @@ -67,14 +67,14 @@ class EmbeddedWindowController { void add(IBinder inputToken, EmbeddedWindow window) { try { mWindows.put(inputToken, window); - final IBinder focusToken = window.getFocusGrantToken(); - mWindowsByFocusToken.put(focusToken, window); + final IBinder inputTransferToken = window.getInputTransferToken(); + mWindowsByInputTransferToken.put(inputTransferToken, window); mWindowsByWindowToken.put(window.getWindowToken(), window); updateProcessController(window); window.mClient.asBinder().linkToDeath(()-> { synchronized (mGlobalLock) { mWindows.remove(inputToken); - mWindowsByFocusToken.remove(focusToken); + mWindowsByInputTransferToken.remove(inputTransferToken); } }, 0); } catch (RemoteException e) { @@ -105,7 +105,7 @@ class EmbeddedWindowController { EmbeddedWindow ew = mWindows.valueAt(i); if (ew.mClient.asBinder() == client.asBinder()) { mWindows.removeAt(i).onRemoved(); - mWindowsByFocusToken.remove(ew.getFocusGrantToken()); + mWindowsByInputTransferToken.remove(ew.getInputTransferToken()); mWindowsByWindowToken.remove(ew.getWindowToken()); return; } @@ -117,7 +117,7 @@ class EmbeddedWindowController { EmbeddedWindow ew = mWindows.valueAt(i); if (ew.mHostWindowState == host) { mWindows.removeAt(i).onRemoved(); - mWindowsByFocusToken.remove(ew.getFocusGrantToken()); + mWindowsByInputTransferToken.remove(ew.getInputTransferToken()); mWindowsByWindowToken.remove(ew.getWindowToken()); } } @@ -127,8 +127,8 @@ class EmbeddedWindowController { return mWindows.get(inputToken); } - EmbeddedWindow getByFocusToken(IBinder focusGrantToken) { - return mWindowsByFocusToken.get(focusGrantToken); + EmbeddedWindow getByInputTransferToken(IBinder inputTransferToken) { + return mWindowsByInputTransferToken.get(inputTransferToken); } EmbeddedWindow getByWindowToken(IBinder windowToken) { @@ -153,7 +153,7 @@ class EmbeddedWindowController { * to request focus transfer to the embedded. This is not the input token since we don't * want to give clients access to each others input token. */ - private final IBinder mFocusGrantToken; + private final IBinder mInputTransferToken; private boolean mIsFocusable; @@ -171,7 +171,7 @@ class EmbeddedWindowController { */ EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken, WindowState hostWindowState, int ownerUid, int ownerPid, int windowType, - int displayId, IBinder focusGrantToken, String inputHandleName, + int displayId, IBinder inputTransferToken, String inputHandleName, boolean isFocusable) { mSession = session; mWmService = service; @@ -183,7 +183,7 @@ class EmbeddedWindowController { mOwnerPid = ownerPid; mWindowType = windowType; mDisplayId = displayId; - mFocusGrantToken = focusGrantToken; + mInputTransferToken = inputTransferToken; final String hostWindowName = (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString() : ""; @@ -260,8 +260,8 @@ class EmbeddedWindowController { return mOwnerUid; } - IBinder getFocusGrantToken() { - return mFocusGrantToken; + IBinder getInputTransferToken() { + return mInputTransferToken; } IBinder getInputChannelToken() { @@ -290,7 +290,7 @@ class EmbeddedWindowController { // Use null session since this is being granted by system server and doesn't // require the host session to be passed in mWmService.grantEmbeddedWindowFocus(null, mHostWindowState.mClient, - mFocusGrantToken, grantFocus); + mInputTransferToken, grantFocus); if (grantFocus) { // If granting focus to the embedded when tapped, we need to ensure the host // gains focus as well or the transfer won't take effect since it requires @@ -298,7 +298,7 @@ class EmbeddedWindowController { mHostWindowState.handleTapOutsideFocusInsideSelf(); } } else { - mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus); + mWmService.grantEmbeddedWindowFocus(mSession, mInputTransferToken, grantFocus); } } } diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 39622c1c5aaf..c21930dab5eb 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -74,7 +74,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mWindowHandle.ownerPid = WindowManagerService.MY_PID; mWindowHandle.ownerUid = WindowManagerService.MY_UID; mWindowHandle.scaleFactor = 1.0f; - mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY; + mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE; mInputSurface = mService.makeSurfaceBuilder( mService.mRoot.getDisplayContent(displayId).getSession()) @@ -129,12 +129,14 @@ class InputConsumerImpl implements IBinder.DeathRecipient { void show(SurfaceControl.Transaction t, WindowContainer w) { t.show(mInputSurface); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1); } void show(SurfaceControl.Transaction t, int layer) { t.show(mInputSurface); + mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, layer); } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 825d38b3eed7..af307ec3c2a9 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -675,6 +675,11 @@ final class InputMonitor { w.getKeyInterceptionInfo()); if (w.mWinAnimator.hasSurface()) { + // Update trusted overlay changes here because they are tied to input info. Input + // changes can be updated even if surfaces aren't. + inputWindowHandle.setTrustedOverlay(mInputTransaction, + w.mWinAnimator.mSurfaceController.mSurfaceControl, + w.isWindowTrustedOverlay()); populateInputWindowHandle(inputWindowHandle, w); setInputWindowInfoIfNeeded(mInputTransaction, w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); @@ -732,7 +737,7 @@ final class InputMonitor { new InputWindowHandle(null /* inputApplicationHandle */, displayId)); inputWindowHandle.setName(name); inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY); - inputWindowHandle.setTrustedOverlay(true); + inputWindowHandle.setTrustedOverlay(t, sc, true); populateOverlayInputInfo(inputWindowHandle); setInputWindowInfoIfNeeded(t, sc, inputWindowHandle); } diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java index 64b7a6064e45..90d81bd82087 100644 --- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -195,6 +195,11 @@ class InputWindowHandleWrapper { mChanged = true; } + void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc, + boolean trustedOverlay) { + mHandle.setTrustedOverlay(t, sc, trustedOverlay); + } + void setOwnerPid(int pid) { if (mHandle.ownerPid == pid) { return; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index def32a1bda87..82d4b90d06be 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -62,6 +62,7 @@ import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.IResultReceiver; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; import com.android.server.inputmethod.InputMethodManagerInternal; @@ -244,7 +245,8 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) { + public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, + IResultReceiver finishCb) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled); final long token = Binder.clearCallingIdentity(); @@ -257,6 +259,13 @@ public class RecentsAnimationController implements DeathRecipient { } finally { Binder.restoreCallingIdentity(token); } + if (finishCb != null) { + try { + finishCb.send(0, new Bundle()); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report animation finished", e); + } + } } @Override @@ -901,7 +910,8 @@ public class RecentsAnimationController implements DeathRecipient { for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final TaskAnimationAdapter adapter = mPendingAnimations.get(i); final Task task = adapter.mTask; - snapshotController.recordSnapshot(task, false /* allowSnapshotHome */); + if (task.isActivityTypeHome()) continue; + snapshotController.recordSnapshot(task); final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); if (snapshot != null) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 24612778ed4e..cf6a1feef5ee 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2189,8 +2189,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // 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); @@ -2320,19 +2319,36 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * * @param app The app that crashed. * @param reason Reason to perform this action. - * @return The task id that was finished in this root task, or INVALID_TASK_ID if none was - * finished. + * @return The finished task which was on top or visible, otherwise {@code null} if the crashed + * app doesn't have activity in visible task. */ - int finishTopCrashedActivities(WindowProcessController app, String reason) { + @Nullable + Task finishTopCrashedActivities(WindowProcessController app, String reason) { Task focusedRootTask = getTopDisplayFocusedRootTask(); final Task[] finishedTask = new Task[1]; forAllRootTasks(rootTask -> { + final boolean recordTopOrVisible = finishedTask[0] == null + && (focusedRootTask == rootTask || rootTask.isVisibleRequested()); final Task t = rootTask.finishTopCrashedActivityLocked(app, reason); - if (rootTask == focusedRootTask || finishedTask[0] == null) { + if (recordTopOrVisible) { finishedTask[0] = t; } }); - return finishedTask[0] != null ? finishedTask[0].mTaskId : INVALID_TASK_ID; + return finishedTask[0]; + } + + void ensureVisibilityOnVisibleActivityDiedOrCrashed(String reason) { + final Task topTask = getTopDisplayFocusedRootTask(); + if (topTask != null && topTask.topRunningActivity(true /* focusableOnly */) == null) { + // Move the next focusable task to front. + topTask.adjustFocusToNextFocusableTask(reason); + } + if (!resumeFocusedTasksTopActivities()) { + // It may be nothing to resume because there are pausing activities or all the top + // activities are resumed. Then it still needs to make sure all visible activities are + // running in case the tasks were reordered or there are non-top visible activities. + ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS); + } } boolean resumeFocusedTasksTopActivities() { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index bbe44c540c39..e6d48667ffb0 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 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, - int inputFeatures, IBinder windowToken, IBinder focusGrantToken, String inputHandleName, - InputChannel outInputChannel) { + int inputFeatures, IBinder windowToken, IBinder inputTransferToken, + String inputHandleName, InputChannel outInputChannel) { if (hostInputToken == null && !mCanAddInternalSystemWindow) { // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to // embedded windows without providing a host window input token @@ -896,7 +896,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { try { mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0, - type, inputFeatures, windowToken, focusGrantToken, inputHandleName, + type, inputFeatures, windowToken, inputTransferToken, inputHandleName, outInputChannel); } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index 37f9730d5443..2e7ff7a6b9b8 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -83,9 +83,10 @@ class SnapshotController { Transition.ChangeInfo info = changeInfos.get(i); // Intentionally skip record snapshot for changes originated from PiP. if (info.mWindowingMode == WINDOWING_MODE_PINNED) continue; - if (info.mContainer.asTask() != null && !info.mContainer.isVisibleRequested()) { - mTaskSnapshotController.recordSnapshot(info.mContainer.asTask(), - false /* allowSnapshotHome */); + if (info.mContainer.isActivityTypeHome()) continue; + final Task task = info.mContainer.asTask(); + if (task != null && !task.isVisibleRequested()) { + mTaskSnapshotController.recordSnapshot(task, info); } // Won't need to capture activity snapshot in close transition. if (isTransitionClose) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 4eb42908c6d4..2b12e7497233 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -118,6 +118,7 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot mTmpTasks.clear(); for (int i = closingApps.size() - 1; i >= 0; i--) { final ActivityRecord activity = closingApps.valueAt(i); + if (activity.isActivityTypeHome()) continue; final Task task = activity.getTask(); if (task == null) continue; @@ -144,23 +145,32 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot } void snapshotTasks(ArraySet<Task> tasks) { - snapshotTasks(tasks, false /* allowSnapshotHome */); + for (int i = tasks.size() - 1; i >= 0; i--) { + recordSnapshot(tasks.valueAt(i)); + } } - TaskSnapshot recordSnapshot(Task task, boolean allowSnapshotHome) { - final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome(); - final TaskSnapshot snapshot = recordSnapshotInner(task, allowSnapshotHome); - if (!snapshotHome && snapshot != null) { - mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot); - task.onSnapshotChanged(snapshot); + /** + * The attributes of task snapshot are based on task configuration. But sometimes the + * configuration may have been changed during a transition, so supply the ChangeInfo that + * stored the previous appearance of the closing task. + */ + void recordSnapshot(Task task, Transition.ChangeInfo changeInfo) { + mCurrentChangeInfo = changeInfo; + try { + recordSnapshot(task); + } finally { + mCurrentChangeInfo = null; } - return snapshot; } - private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) { - for (int i = tasks.size() - 1; i >= 0; i--) { - recordSnapshot(tasks.valueAt(i), allowSnapshotHome); + TaskSnapshot recordSnapshot(Task task) { + final TaskSnapshot snapshot = recordSnapshotInner(task); + if (snapshot != null && !task.isActivityTypeHome()) { + mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot); + task.onSnapshotChanged(snapshot); } + return snapshot; } /** @@ -320,19 +330,22 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot if (displayContent == null) { return; } + // Allow taking snapshot of home when turning screen off to reduce the delay of waking from + // secure lock to home. + final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY + && mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId); mTmpTasks.clear(); - displayContent.forAllTasks(task -> { + displayContent.forAllLeafTasks(task -> { + if (!allowSnapshotHome && task.isActivityTypeHome()) { + return; + } // Since RecentsAnimation will handle task snapshot while switching apps with the best // capture timing (e.g. IME window capture), No need additional task capture while task // is controlled by RecentsAnimation. if (task.isVisible() && !isAnimatingByRecents(task)) { mTmpTasks.add(task); } - }); - // Allow taking snapshot of home when turning screen off to reduce the delay of waking from - // secure lock to home. - final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY - && mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId); - snapshotTasks(mTmpTasks, allowSnapshotHome); + }, true /* traverseTopToBottom */); + snapshotTasks(mTmpTasks); } } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 774be9e33a61..882104a297ef 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -372,8 +372,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { parent.forAllTasks(t -> { // Skip transient-launch task if (t == transientRootTask) return false; - if (t.isVisibleRequested() && !t.isAlwaysOnTop() - && !t.getWindowConfiguration().tasksAreFloating()) { + if (t.isVisibleRequested() && !t.isAlwaysOnTop()) { if (t.isRootTask()) { mTransientHideTasks.add(t); } @@ -1197,7 +1196,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Commit activity becoming invisible: %s", ar); final SnapshotController snapController = mController.mSnapshotController; - if (mTransientLaunches != null && !task.isVisibleRequested()) { + if (mTransientLaunches != null && !task.isVisibleRequested() + && !task.isActivityTypeHome()) { final long startTimeNs = mLogger.mSendTimeNs; final long lastSnapshotTimeNs = snapController.mTaskSnapshotController .getSnapshotCaptureTime(task.mTaskId); @@ -1205,8 +1205,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // transition only if a snapshot was not already captured by request // during the transition if (lastSnapshotTimeNs < startTimeNs) { - snapController.mTaskSnapshotController - .recordSnapshot(task, false /* allowSnapshotHome */); + snapController.mTaskSnapshotController.recordSnapshot(task); } else { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Skipping post-transition snapshot for task %d", diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 82452ccc3d06..074b4044fdaa 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 @@ public class WindowManagerService extends IWindowManager.Stub outInsetsState.set(win.getCompatInsetsState(), true /* copySources */); } - // TODO (b/298562855): Remove this after identifying the reason why the frame is empty. - if (win.mAttrs.providedInsets != null && win.getFrame().isEmpty()) { - Slog.w(TAG, "Empty frame of " + win - + " configChanged=" + configChanged - + " frame=" + win.getFrame().toShortString() - + " attrs=" + attrs); - } - ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b", win, focusMayChange); @@ -8803,7 +8795,7 @@ public class WindowManagerService extends IWindowManager.Stub void grantInputChannel(Session session, int callingUid, int callingPid, int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken, - IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) { + IBinder inputTransferToken, String inputHandleName, InputChannel outInputChannel) { final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type); final InputApplicationHandle applicationHandle; final String name; @@ -8812,7 +8804,7 @@ public class WindowManagerService extends IWindowManager.Stub EmbeddedWindowController.EmbeddedWindow win = new EmbeddedWindowController.EmbeddedWindow(session, this, window, mInputToWindowMap.get(hostInputToken), callingUid, callingPid, - sanitizedType, displayId, focusGrantToken, inputHandleName, + sanitizedType, displayId, inputTransferToken, inputHandleName, (flags & FLAG_NOT_FOCUSABLE) == 0); win.openInputChannel(outInputChannel); mEmbeddedWindowController.add(outInputChannel.getToken(), win); @@ -8884,11 +8876,6 @@ public class WindowManagerService extends IWindowManager.Stub h.inputConfig |= InputConfig.NOT_FOCUSABLE; } - // Check private trusted overlay flag to set trustedOverlay field of input window handle. - if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { - h.inputConfig |= InputConfig.TRUSTED_OVERLAY; - } - h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; h.ownerUid = callingUid; h.ownerPid = callingPid; @@ -8908,6 +8895,8 @@ public class WindowManagerService extends IWindowManager.Stub } final SurfaceControl.Transaction t = mTransactionFactory.get(); + // Check private trusted overlay flag to set trustedOverlay field of input window handle. + h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0); t.setInputWindowInfo(surface, h); t.apply(); t.close(); @@ -9139,10 +9128,10 @@ public class WindowManagerService extends IWindowManager.Stub return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId); } - void grantEmbeddedWindowFocus(Session session, IBinder focusToken, boolean grantFocus) { + void grantEmbeddedWindowFocus(Session session, IBinder inputTransferToken, boolean grantFocus) { synchronized (mGlobalLock) { final EmbeddedWindowController.EmbeddedWindow embeddedWindow = - mEmbeddedWindowController.getByFocusToken(focusToken); + mEmbeddedWindowController.getByInputTransferToken(inputTransferToken); if (embeddedWindow == null) { Slog.e(TAG, "Embedded window not found"); return; @@ -9187,8 +9176,8 @@ public class WindowManagerService extends IWindowManager.Stub } } - void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetFocusToken, - boolean grantFocus) { + void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, + IBinder inputTransferToken, boolean grantFocus) { synchronized (mGlobalLock) { final WindowState hostWindow = windowForClientLocked(session, callingWindow, false /* throwOnError*/); @@ -9201,7 +9190,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } final EmbeddedWindowController.EmbeddedWindow embeddedWindow = - mEmbeddedWindowController.getByFocusToken(targetFocusToken); + mEmbeddedWindowController.getByInputTransferToken(inputTransferToken); if (embeddedWindow == null) { Slog.e(TAG, "Embedded window not found"); return; @@ -9430,7 +9419,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalArgumentException( "Failed to find matching task for taskId=" + taskId); } - taskSnapshot = mTaskSnapshotController.captureSnapshot(task, false); + taskSnapshot = mTaskSnapshotController.captureSnapshot(task); } } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ebef606a8d60..4beec2bc79e6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -178,6 +178,7 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY; import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; +import static com.android.window.flags.Flags.surfaceTrustedOverlay; import android.annotation.CallSuper; import android.annotation.NonNull; @@ -1110,7 +1111,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mInputWindowHandle.setName(getName()); mInputWindowHandle.setPackageName(mAttrs.packageName); mInputWindowHandle.setLayoutParamsType(mAttrs.type); - mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s)); + if (!surfaceTrustedOverlay()) { + mInputWindowHandle.setTrustedOverlay(isWindowTrustedOverlay()); + } if (DEBUG) { Slog.v(TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); @@ -1185,12 +1188,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - boolean shouldWindowHandleBeTrusted(Session s) { + public boolean isWindowTrustedOverlay() { return InputMonitor.isTrustedOverlay(mAttrs.type) || ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0 - && s.mCanAddInternalSystemWindow) + && mSession.mCanAddInternalSystemWindow) || ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0 - && s.mCanCreateSystemApplicationOverlay); + && mSession.mCanCreateSystemApplicationOverlay); } int getTouchOcclusionMode() { @@ -5187,6 +5190,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateFrameRateSelectionPriorityIfNeeded(); updateScaleIfNeeded(); mWinAnimator.prepareSurfaceLocked(getSyncTransaction()); + if (surfaceTrustedOverlay()) { + getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay()); + } } super.prepareSurfaces(); } @@ -5939,7 +5945,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean isTrustedOverlay() { - return mInputWindowHandle.isTrustedOverlay(); + if (surfaceTrustedOverlay()) { + WindowState parentWindow = getParentWindow(); + return isWindowTrustedOverlay() || (parentWindow != null + && parentWindow.isWindowTrustedOverlay()); + } else { + return mInputWindowHandle.isTrustedOverlay(); + } } public boolean receiveFocusFromTapOutside() { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index ec5378f01ce3..bc70658a06c4 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -78,6 +78,7 @@ cc_library_static { "onload.cpp", ":lib_cachedAppOptimizer_native", ":lib_gameManagerService_native", + ":lib_oomConnection_native", ], include_dirs: [ @@ -122,6 +123,7 @@ cc_defaults { "libhardware_legacy", "libhidlbase", "libmeminfo", + "libmemevents", "libmemtrackproxy", "libmtp", "libnativehelper", @@ -239,3 +241,8 @@ filegroup { "com_android_server_app_GameManagerService.cpp", ], } + +filegroup { + name: "lib_oomConnection_native", + srcs: ["com_android_server_am_OomConnection.cpp",], +} diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp new file mode 100644 index 000000000000..e892d23460c9 --- /dev/null +++ b/services/core/jni/com_android_server_am_OomConnection.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OomConnection" + +#include <core_jni_helpers.h> +#include <jni.h> +#include <memevents/memevents.h> + +namespace android { + +// Used to cache the results of the JNI name lookup +static struct { + jclass clazz; + jmethodID ctor; +} sOomKillRecordInfo; + +static memevents::MemEventListener memevent_listener; + +/** + * Initialize listening and waiting for new out-of-memory (OOM) events to occur. + * Once a OOM event is detected, we then fetch the list of OOM kills, and return + * a corresponding java array with the information gathered. + * + * In the case that we encounter an error, we make sure to close the epfd, and + * the OOM file descriptor, by calling `deregisterAllEvents()`. + * + * @return list of `android.os.OomKillRecord` + * @throws java.lang.RuntimeException + */ +static jobjectArray android_server_am_OomConnection_waitOom(JNIEnv* env, jobject) { + const memevents::MemEvent oom_event = memevents::MemEvent::OOM_KILL; + if (!memevent_listener.registerEvent(oom_event)) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "listener failed to register to OOM events"); + return nullptr; + } + + memevents::MemEvent event_received; + do { + event_received = memevent_listener.listen(); + if (event_received == memevents::MemEvent::ERROR) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "listener received error event"); + return nullptr; + } + } while (event_received != oom_event); + + std::vector<memevents::OomKill> oom_events; + if (!memevent_listener.getOomEvents(oom_events)) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed to get OOM events"); + return nullptr; + } + + jobjectArray java_oom_array = + env->NewObjectArray(oom_events.size(), sOomKillRecordInfo.clazz, nullptr); + if (java_oom_array == NULL) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed to create OomKillRecord array"); + return nullptr; + } + + for (int i = 0; i < oom_events.size(); i++) { + const memevents::OomKill oom_event = oom_events[i]; + jstring process_name = env->NewStringUTF(oom_event.process_name); + if (process_name == NULL) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed creating java string for process name"); + } + jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor, + oom_event.timestamp_ms, oom_event.pid, oom_event.uid, + process_name, oom_event.oom_score_adj); + if (java_oom_kill == NULL) { + memevent_listener.deregisterAllEvents(); + jniThrowRuntimeException(env, "Failed to create OomKillRecord object"); + return java_oom_array; + } + env->SetObjectArrayElement(java_oom_array, i, java_oom_kill); + } + return java_oom_array; +} + +static const JNINativeMethod sOomConnectionMethods[] = { + /* name, signature, funcPtr */ + {"waitOom", "()[Landroid/os/OomKillRecord;", + (void*)android_server_am_OomConnection_waitOom}, +}; + +int register_android_server_am_OomConnection(JNIEnv* env) { + sOomKillRecordInfo.clazz = FindClassOrDie(env, "android/os/OomKillRecord"); + sOomKillRecordInfo.clazz = MakeGlobalRefOrDie(env, sOomKillRecordInfo.clazz); + + sOomKillRecordInfo.ctor = + GetMethodIDOrDie(env, sOomKillRecordInfo.clazz, "<init>", "(JIILjava/lang/String;S)V"); + + return RegisterMethodsOrDie(env, "com/android/server/am/OomConnection", sOomConnectionMethods, + NELEM(sOomConnectionMethods)); +} +} // namespace android
\ No newline at end of file diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index f6f673746609..df4489528be5 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -52,6 +52,7 @@ int register_android_server_Watchdog(JNIEnv* env); int register_android_server_HardwarePropertiesManagerService(JNIEnv* env); int register_android_server_SyntheticPasswordManager(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); +int register_android_server_am_OomConnection(JNIEnv* env); int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env); @@ -112,6 +113,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_storage_AppFuse(env); register_android_server_SyntheticPasswordManager(env); register_android_hardware_display_DisplayViewport(env); + register_android_server_am_OomConnection(env); register_android_server_am_CachedAppOptimizer(env); register_android_server_am_LowMemDetector(env); register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env); diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index d0a9c458a20f..debd891400b6 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -46,6 +46,8 @@ <xs:annotation name="nonnull"/> <xs:annotation name="final"/> </xs:element> + <xs:element type="powerThrottlingConfig" name="powerThrottlingConfig" minOccurs="0" + maxOccurs="1"/> <xs:element type="luxThrottling" name="luxThrottling" minOccurs="0" maxOccurs="1"/> <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" @@ -350,6 +352,43 @@ </xs:sequence> </xs:complexType> + <xs:complexType name="powerThrottlingMap"> + <xs:sequence> + <xs:element name="powerThrottlingPoint" type="powerThrottlingPoint" maxOccurs="unbounded" minOccurs="1"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + <xs:attribute name="id" type="xs:string"/> + </xs:complexType> + + <xs:complexType name="powerThrottlingPoint"> + <xs:sequence> + <xs:element type="thermalStatus" name="thermalStatus"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="nonNegativeDecimal" name="powerQuotaMilliWatts"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="powerThrottlingConfig"> + <xs:element type="nonNegativeDecimal" name="brightnessLowestCapAllowed"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="pollingWindowMillis" type="xs:nonNegativeInteger"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="powerThrottlingMap" name="powerThrottlingMap" maxOccurs="unbounded"> + <xs:annotation name="final"/> + </xs:element> + </xs:complexType> + <xs:complexType name="nitsMap"> <xs:sequence> <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2"> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 949b1f2cb74b..2d27f0c79660 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -106,6 +106,7 @@ package com.android.server.display.config { method public final com.android.server.display.config.SensorDetails getLightSensor(); method public com.android.server.display.config.LuxThrottling getLuxThrottling(); method @Nullable public final String getName(); + method public com.android.server.display.config.PowerThrottlingConfig getPowerThrottlingConfig(); method public final com.android.server.display.config.SensorDetails getProxSensor(); method public com.android.server.display.config.DisplayQuirks getQuirks(); method public com.android.server.display.config.RefreshRateConfigs getRefreshRate(); @@ -138,6 +139,7 @@ package com.android.server.display.config { method public final void setLightSensor(com.android.server.display.config.SensorDetails); method public void setLuxThrottling(com.android.server.display.config.LuxThrottling); method public final void setName(@Nullable String); + method public void setPowerThrottlingConfig(com.android.server.display.config.PowerThrottlingConfig); method public final void setProxSensor(com.android.server.display.config.SensorDetails); method public void setQuirks(com.android.server.display.config.DisplayQuirks); method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs); @@ -246,6 +248,30 @@ package com.android.server.display.config { method public final void setValue(@NonNull java.math.BigDecimal); } + public class PowerThrottlingConfig { + ctor public PowerThrottlingConfig(); + method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed(); + method @NonNull public final java.math.BigInteger getPollingWindowMillis(); + method public final java.util.List<com.android.server.display.config.PowerThrottlingMap> getPowerThrottlingMap(); + method public final void setBrightnessLowestCapAllowed(@NonNull java.math.BigDecimal); + method public final void setPollingWindowMillis(@NonNull java.math.BigInteger); + } + + public class PowerThrottlingMap { + ctor public PowerThrottlingMap(); + method public String getId(); + method @NonNull public final java.util.List<com.android.server.display.config.PowerThrottlingPoint> getPowerThrottlingPoint(); + method public void setId(String); + } + + public class PowerThrottlingPoint { + ctor public PowerThrottlingPoint(); + method @NonNull public final java.math.BigDecimal getPowerQuotaMilliWatts(); + method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatus(); + method public final void setPowerQuotaMilliWatts(@NonNull java.math.BigDecimal); + method public final void setThermalStatus(@NonNull com.android.server.display.config.ThermalStatus); + } + public enum PredefinedBrightnessLimitNames { method public String getRawName(); enum_constant public static final com.android.server.display.config.PredefinedBrightnessLimitNames _default; diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd index 57b5d00f75a0..4e954653bcbb 100644 --- a/services/core/xsd/display-layout-config/display-layout-config.xsd +++ b/services/core/xsd/display-layout-config/display-layout-config.xsd @@ -52,6 +52,7 @@ <xs:element name="address" type="xs:nonNegativeInteger"/> <xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" /> + <xs:element name="powerThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="refreshRateThermalThrottlingMapId" type="xs:string" minOccurs="0" /> <xs:element name="leadDisplayAddress" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1" /> </xs:sequence> diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt index 2d4f7a428db1..195cae5aee14 100644 --- a/services/core/xsd/display-layout-config/schema/current.txt +++ b/services/core/xsd/display-layout-config/schema/current.txt @@ -8,6 +8,7 @@ package com.android.server.display.config.layout { method public String getDisplayGroup(); method public java.math.BigInteger getLeadDisplayAddress(); method public String getPosition(); + method public String getPowerThrottlingMapId(); method public String getRefreshRateThermalThrottlingMapId(); method public String getRefreshRateZoneId(); method public boolean isDefaultDisplay(); @@ -19,6 +20,7 @@ package com.android.server.display.config.layout { method public void setEnabled(boolean); method public void setLeadDisplayAddress(java.math.BigInteger); method public void setPosition(String); + method public void setPowerThrottlingMapId(String); method public void setRefreshRateThermalThrottlingMapId(String); method public void setRefreshRateZoneId(String); } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java index b90f08e420e7..3c190bf7ad11 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java @@ -32,6 +32,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ResultReceiver; +import android.os.UserHandle; import android.service.credentials.CredentialProviderInfoFactory; import android.util.Slog; @@ -171,7 +172,9 @@ public class CredentialManagerUi { .setAction(UUID.randomUUID().toString()); //TODO: Create unique pending intent using request code and cancel any pre-existing pending // intents - return PendingIntent.getActivity( - mContext, /*requestCode=*/0, intent, PendingIntent.FLAG_IMMUTABLE); + return PendingIntent.getActivityAsUser( + mContext, /*requestCode=*/0, intent, + PendingIntent.FLAG_IMMUTABLE, /*options=*/null, + UserHandle.of(mUserId)); } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 3eb6718c0a95..7bd1cc4ca6c8 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -31,6 +31,7 @@ import android.credentials.ui.Entry; import android.credentials.ui.GetCredentialProviderData; import android.credentials.ui.ProviderPendingIntentResponse; import android.os.ICancellationSignal; +import android.service.autofill.Flags; import android.service.credentials.Action; import android.service.credentials.BeginGetCredentialOption; import android.service.credentials.BeginGetCredentialRequest; @@ -42,6 +43,7 @@ import android.service.credentials.GetCredentialRequest; import android.service.credentials.RemoteEntry; import android.util.Pair; import android.util.Slog; +import android.view.autofill.AutofillId; import java.util.ArrayList; import java.util.HashMap; @@ -379,13 +381,23 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential // but does not resolve to a valid option. For now, not skipping it because // it may be possible that the provider adds their own extras and expects to receive // those and complete the flow. - if (mBeginGetOptionToCredentialOptionMap.get(id) == null) { + Intent intent = new Intent(); + CredentialOption credentialOption = mBeginGetOptionToCredentialOptionMap.get(id); + if (credentialOption == null) { Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option"); - return new Intent(); + return intent; } - return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST, + AutofillId autofillId = credentialOption + .getCandidateQueryData() + .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class); + if (autofillId != null && Flags.autofillCredmanIntegration()) { + intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId); + } + return intent.putExtra( + CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST, new GetCredentialRequest( - mCallingAppInfo, List.of(mBeginGetOptionToCredentialOptionMap.get(id)))); + mCallingAppInfo, + List.of(credentialOption))); } private Intent setUpFillInIntentWithQueryRequest() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6aa135a3eaa9..43e47d7a45fc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -241,6 +241,7 @@ import static android.provider.Telephony.Carriers.ENFORCE_KEY; import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI; import static android.provider.Telephony.Carriers.INVALID_APN_ID; import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION; + import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -15781,57 +15782,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else { long ident = mInjector.binderClearCallingIdentity(); try { - // TODO(b/277908283): check in the policy engine instead of calling user manager. - List<UserManager.EnforcingUser> sources = mUserManager - .getUserRestrictionSources(restriction, UserHandle.of(userId)); - if (sources == null) { - // The restriction is not enforced. - return null; - } - int sizeBefore = sources.size(); - if (sizeBefore > 1) { - Slogf.d(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): " - + "%d sources found, excluding those set by UserManager", - userId, restriction, sizeBefore); - sources = getDevicePolicySources(sources); - } - if (sources.isEmpty()) { - // The restriction is not enforced (or is just enforced by the system) + if (getEnforcingAdminsForRestrictionInternal(userId, restriction).size() == 0) { return null; } - if (sources.size() > 1) { - // In this case, we'll show an admin support dialog that does not - // specify the admin. - // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return - // the admin for the calling user. - Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple " - + "sources for restriction %s on user %d", - userId, restriction, restriction, userId); + ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId); + if (admin != null) { result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, userId); + result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier()); + result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, + admin.info.getComponent()); return result; } - final UserManager.EnforcingUser enforcingUser = sources.get(0); - final int sourceType = enforcingUser.getUserRestrictionSource(); - if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER - || sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) { - ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId); - if (admin != null) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier()); - result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - admin.info.getComponent()); - return result; - } - } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) { - /* - * In this case, the user restriction is enforced by the system. - * So we won't show an admin support intent, even if it is also - * enforced by a profile/device owner. - */ - return null; - } + return null; } finally { mInjector.binderRestoreCallingIdentity(ident); } diff --git a/services/foldables/devicestateprovider/tests/Android.bp b/services/foldables/devicestateprovider/tests/Android.bp index a8db05e99179..84a6df38e0a0 100644 --- a/services/foldables/devicestateprovider/tests/Android.bp +++ b/services/foldables/devicestateprovider/tests/Android.bp @@ -20,11 +20,11 @@ android_test { "foldable-device-state-provider", "androidx.test.rules", "junit", - "truth-prebuilt", + "truth", "mockito-target-extended-minus-junit4", "androidx.test.uiautomator_uiautomator", "androidx.test.ext.junit", "testables", ], - test_suites: ["device-tests"] + test_suites: ["device-tests"], } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 57fa12d20de5..49ad84a8e6d9 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -264,6 +264,8 @@ public final class SystemServer implements Dumpable { "com.android.server.backup.BackupManagerService$Lifecycle"; private static final String APPWIDGET_SERVICE_CLASS = "com.android.server.appwidget.AppWidgetService"; + private static final String ARC_SYSTEM_HEALTH_SERVICE = + "com.android.server.arc.health.ArcSystemHealthService"; private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS = "com.android.server.voiceinteraction.VoiceInteractionManagerService"; private static final String APP_HIBERNATION_SERVICE_CLASS = @@ -1287,6 +1289,12 @@ public final class SystemServer implements Dumpable { } } + if (Build.IS_ARC) { + t.traceBegin("StartArcSystemHealthService"); + mSystemServiceManager.startService(ARC_SYSTEM_HEALTH_SERVICE); + t.traceEnd(); + } + t.traceBegin("StartUserManagerService"); mSystemServiceManager.startService(UserManagerService.LifeCycle.class); t.traceEnd(); diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp index e04dd688f2a2..8b9efb312efe 100644 --- a/services/robotests/backup/Android.bp +++ b/services/robotests/backup/Android.bp @@ -59,7 +59,7 @@ android_robolectric_test { "mockito-robolectric-prebuilt", "platform-test-annotations", "testng", - "truth-prebuilt", + "truth", ], instrumentation_for: "BackupFrameworksServicesLib", diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp index 36446f64bdce..ffe6dc5d1c63 100644 --- a/services/tests/InputMethodSystemServerTests/Android.bp +++ b/services/tests/InputMethodSystemServerTests/Android.bp @@ -44,7 +44,7 @@ android_test { "service-permission.stubs.system_server", "servicestests-core-utils", "servicestests-utils-mockito-extended", - "truth-prebuilt", + "truth", ], libs: [ @@ -92,7 +92,7 @@ android_test { "service-permission.stubs.system_server", "servicestests-core-utils", "servicestests-utils-mockito-extended", - "truth-prebuilt", + "truth", "SimpleImeTestingLib", "SimpleImeImsLib", ], diff --git a/services/tests/PackageManager/packageinstaller/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp index 35d754b4adc5..e8fce8e72601 100644 --- a/services/tests/PackageManager/packageinstaller/Android.bp +++ b/services/tests/PackageManager/packageinstaller/Android.bp @@ -32,7 +32,7 @@ android_test { "androidx.test.runner", "junit", "kotlin-test", - "truth-prebuilt", + "truth", ], platform_apis: true, test_suites: ["device-tests"], diff --git a/services/tests/PackageManagerComponentOverrideTests/Android.bp b/services/tests/PackageManagerComponentOverrideTests/Android.bp index bc369701b2d4..00850a5e5be0 100644 --- a/services/tests/PackageManagerComponentOverrideTests/Android.bp +++ b/services/tests/PackageManagerComponentOverrideTests/Android.bp @@ -38,7 +38,7 @@ android_test { "service-permission.stubs.system_server", "servicestests-utils-mockito-extended", "testng", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows - "truth-prebuilt", + "truth", ], jni_libs: [ diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp index 9c4e6fd66ceb..ad7af44d4089 100644 --- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp +++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp @@ -29,7 +29,7 @@ android_test { static_libs: [ "compatibility-device-util-axt", "androidx.test.runner", - "truth-prebuilt", + "truth", "Harrier", ], platform_apis: true, diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index ce28682c0a25..6eacef767042 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -30,7 +30,7 @@ java_test_host { libs: [ "tradefed", "junit", - "truth-prebuilt", + "truth", ], static_libs: [ "ApexInstallHelper", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp index 462c5801e952..cea9c599317d 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp @@ -38,6 +38,6 @@ android_test_helper_app { "junit-params", "androidx.test.ext.junit", "androidx.test.rules", - "truth-prebuilt", + "truth", ], } diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp index 57184748d074..ed5f2b51b9ed 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp @@ -28,6 +28,6 @@ android_test_helper_app { "androidx.test.runner", "junit", "kotlin-test", - "truth-prebuilt", + "truth", ], } diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp index a1d846e0f426..3aca1cafbf75 100644 --- a/services/tests/PackageManagerServiceTests/server/Android.bp +++ b/services/tests/PackageManagerServiceTests/server/Android.bp @@ -41,7 +41,7 @@ android_test { "mockito-target-minus-junit4", "platform-test-annotations", "ShortcutManagerTestUtils", - "truth-prebuilt", + "truth", "testables", "platformprotosnano", "framework-protos", @@ -51,7 +51,7 @@ android_test { "servicestests-utils", "service-permission.impl", "testng", - "truth-prebuilt", + "truth", "junit", "junit-params", "platform-compat-test-rules", diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp index 9b3b8c35736c..8505983894a8 100644 --- a/services/tests/PackageManagerServiceTests/unit/Android.bp +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -37,7 +37,7 @@ android_test { "services.core", "servicestests-utils", "servicestests-core-utils", - "truth-prebuilt", + "truth", ], jni_libs: [ "libdexmakerjvmtiagent", diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp index fc2c0857146b..19c913620760 100644 --- a/services/tests/RemoteProvisioningServiceTests/Android.bp +++ b/services/tests/RemoteProvisioningServiceTests/Android.bp @@ -30,8 +30,8 @@ android_test { "mockito-target", "service-rkp.impl", "services.core", - "truth-prebuilt", - "truth-java8-extension-jar", + "truth", + "truth-java8-extension", ], test_suites: [ "device-tests", diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp index e724e804f4e7..9dacfeabf1ef 100644 --- a/services/tests/apexsystemservices/Android.bp +++ b/services/tests/apexsystemservices/Android.bp @@ -34,7 +34,7 @@ java_test_host { "compatibility-host-util", "cts-install-lib-host", "frameworks-base-hostutils", - "truth-prebuilt", + "truth", "modules-utils-build-testing", ], test_suites: [ diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index 6ef150c80037..c37d21ae1cc0 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -179,6 +179,89 @@ public final class DisplayDeviceConfigTest { } @Test + public void testPowerThrottlingConfigFromDisplayConfig() throws IOException { + setupDisplayDeviceConfigFromDisplayConfigFile(); + + DisplayDeviceConfig.PowerThrottlingConfigData powerThrottlingConfigData = + mDisplayDeviceConfig.getPowerThrottlingConfigData(); + assertNotNull(powerThrottlingConfigData); + assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA); + assertEquals(10, powerThrottlingConfigData.pollingWindowMillis); + } + + @Test + public void testPowerThrottlingDataFromDisplayConfig() throws IOException { + setupDisplayDeviceConfigFromDisplayConfigFile(); + + List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel> + defaultThrottlingLevels = new ArrayList<>(); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f + )); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f + )); + + DisplayDeviceConfig.PowerThrottlingData defaultThrottlingData = + new DisplayDeviceConfig.PowerThrottlingData(defaultThrottlingLevels); + + List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel> + concurrentThrottlingLevels = new ArrayList<>(); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f + )); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f + )); + DisplayDeviceConfig.PowerThrottlingData concurrentThrottlingData = + new DisplayDeviceConfig.PowerThrottlingData(concurrentThrottlingLevels); + + HashMap<String, DisplayDeviceConfig.PowerThrottlingData> throttlingDataMap = + new HashMap<>(2); + throttlingDataMap.put("default", defaultThrottlingData); + throttlingDataMap.put("concurrent", concurrentThrottlingData); + + assertEquals(throttlingDataMap, + mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId()); + } + + @Test public void testConfigValuesFromConfigResource() { setupDisplayDeviceConfigFromConfigResourceFile(); verifyConfigValuesFromConfigResource(); @@ -845,6 +928,64 @@ public final class DisplayDeviceConfigTest { + "</screenBrightnessRampSlowIncreaseIdle>\n"; } + private String getPowerThrottlingConfig() { + return "<powerThrottlingConfig >\n" + + "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n" + + "<pollingWindowMillis>10</pollingWindowMillis>\n" + + "<powerThrottlingMap>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>light</thermalStatus>\n" + + "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>moderate</thermalStatus>\n" + + "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>severe</thermalStatus>\n" + + "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>critical</thermalStatus>\n" + + "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>emergency</thermalStatus>\n" + + "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>shutdown</thermalStatus>\n" + + "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "</powerThrottlingMap>\n" + + "<powerThrottlingMap id=\"concurrent\">\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>light</thermalStatus>\n" + + "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>moderate</thermalStatus>\n" + + "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>severe</thermalStatus>\n" + + "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>critical</thermalStatus>\n" + + "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>emergency</thermalStatus>\n" + + "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "<powerThrottlingPoint>\n" + + "<thermalStatus>shutdown</thermalStatus>\n" + + "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n" + + "</powerThrottlingPoint>\n" + + "</powerThrottlingMap>\n" + + "</powerThrottlingConfig>\n"; + } private String getScreenBrightnessRampCapsIdle() { return "<screenBrightnessRampIncreaseMaxIdleMillis>" + "4000" @@ -915,6 +1056,7 @@ public final class DisplayDeviceConfigTest { + "</displayBrightnessPoint>\n" + "</displayBrightnessMapping>\n" + "</autoBrightness>\n" + + getPowerThrottlingConfig() + "<highBrightnessMode enabled=\"true\">\n" + "<transitionPoint>" + BRIGHTNESS[1] + "</transitionPoint>\n" + "<minimumLux>10000</minimumLux>\n" diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java index c63fac9832e9..ee187baf524e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java @@ -22,6 +22,9 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -97,6 +100,37 @@ public class HdrClamperTest { } @Test + public void testRegisterHdrListener() { + verify(mMockHdrInfoListener).register(mMockBinder); + } + + @Test + public void testRegisterOtherHdrListenerWhenCalledWithOtherToken() { + IBinder otherBinder = mock(IBinder.class); + mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, otherBinder); + + verify(mMockHdrInfoListener).unregister(mMockBinder); + verify(mMockHdrInfoListener).register(otherBinder); + } + + @Test + public void testRegisterHdrListenerOnceWhenCalledWithSameToken() { + mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder); + + verify(mMockHdrInfoListener, never()).unregister(mMockBinder); + verify(mMockHdrInfoListener, times(1)).register(mMockBinder); + } + + @Test + public void testRegisterNotCalledIfHbmConfigIsMissing() { + IBinder otherBinder = mock(IBinder.class); + mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, -1, otherBinder); + + verify(mMockHdrInfoListener).unregister(mMockBinder); + verify(mMockHdrInfoListener, never()).register(otherBinder); + } + + @Test public void testClamper_AmbientLuxChangesAboveLimit() { mHdrClamper.onAmbientLuxChange(500); diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp index 7c237ac6befb..086e84b86aca 100644 --- a/services/tests/inprocesstests/Android.bp +++ b/services/tests/inprocesstests/Android.bp @@ -14,7 +14,7 @@ android_test { "androidx.test.core", "androidx.test.rules", "services.core", - "truth-prebuilt", + "truth", "platform-test-annotations", ], test_suites: ["general-tests"], diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 101498aef183..063af573e1f3 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -46,6 +46,7 @@ android_test { "androidx.test.espresso.core", "androidx.test.espresso.contrib", "androidx.test.ext.truth", + "flag-junit", "frameworks-base-testutils", "hamcrest-library", "kotlin-test", @@ -67,7 +68,7 @@ android_test { "servicestests-core-utils", "servicestests-utils-mockito-extended", "testables", - "truth-prebuilt", + "truth", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows "testng", "compatibility-device-util-axt", diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp index f1dc1fa1a108..1eb9888489cb 100644 --- a/services/tests/mockingservicestests/jni/Android.bp +++ b/services/tests/mockingservicestests/jni/Android.bp @@ -22,6 +22,7 @@ cc_library_shared { srcs: [ ":lib_cachedAppOptimizer_native", ":lib_gameManagerService_native", + ":lib_oomConnection_native", "onload.cpp", ], @@ -42,6 +43,7 @@ cc_library_shared { "libgui", "libhidlbase", "liblog", + "libmemevents", "libmeminfo", "libnativehelper", "libprocessgroup", diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp index 23ccb22321b2..fb910513adda 100644 --- a/services/tests/mockingservicestests/jni/onload.cpp +++ b/services/tests/mockingservicestests/jni/onload.cpp @@ -26,6 +26,7 @@ namespace android { int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_app_GameManagerService(JNIEnv* env); +int register_android_server_am_OomConnection(JNIEnv* env); }; using namespace android; @@ -42,6 +43,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) ALOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_am_CachedAppOptimizer(env); register_android_server_app_GameManagerService(env); + register_android_server_am_OomConnection(env); return JNI_VERSION_1_4; } diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 5b1508b8393b..be29163e7677 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -1290,7 +1290,8 @@ public class DeviceIdleControllerTest { } @Test - public void testLightStepIdleStateIdlingTimeIncreases() { + public void testLightStepIdleStateIdlingTimeIncreasesExponentially() { + mConstants.LIGHT_IDLE_INCREASE_LINEARLY = false; final long maintenanceTimeMs = 60_000L; mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs; mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs; @@ -1335,13 +1336,88 @@ public class DeviceIdleControllerTest { eq(mInjector.nowElapsed + idlingTimeMs), anyLong(), anyString(), any(), any(Handler.class)); - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < 10; ++i) { // IDLE->MAINTENANCE alarm mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); alarmListener.onAlarm(); verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE); long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs; idlingTimeMs *= mConstants.LIGHT_IDLE_FACTOR; + idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT); + // Set MAINTENANCE->IDLE + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(maintenanceExpiryTime), + anyLong(), anyString(), any(), any(Handler.class)); + + // MAINTENANCE->IDLE alarm + mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); + alarmListener.onAlarm(); + verifyLightStateConditions(LIGHT_STATE_IDLE); + // Set IDLE->MAINTENANCE again + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(mInjector.nowElapsed + idlingTimeMs), + anyLong(), anyString(), any(), any(Handler.class)); + } + } + + @Test + public void testLightStepIdleStateIdlingTimeIncreasesLinearly() { + mConstants.LIGHT_IDLE_INCREASE_LINEARLY = true; + final long maintenanceTimeMs = 60_000L; + mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs; + mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs; + mConstants.LIGHT_IDLE_TIMEOUT = 5 * 60_000L; + mConstants.LIGHT_MAX_IDLE_TIMEOUT = 30 * 60_000L; + mConstants.LIGHT_IDLE_FACTOR = 2f; + mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = 2 * 60_000L; + + setNetworkConnected(true); + mDeviceIdleController.setJobsActive(false); + mDeviceIdleController.setAlarmsActive(false); + mDeviceIdleController.setActiveIdleOpsForTest(0); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = ArgumentCaptor + .forClass(AlarmManager.OnAlarmListener.class); + doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(), + eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any()); + + // Set state to INACTIVE. + mDeviceIdleController.becomeActiveLocked("testing", 0); + setChargingOn(false); + setScreenOn(false); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + long idlingTimeMs = mConstants.LIGHT_IDLE_TIMEOUT; + final long idleAfterInactiveExpiryTime = + mInjector.nowElapsed + mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT; + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(idleAfterInactiveExpiryTime), + anyLong(), anyString(), any(), any(Handler.class)); + + final AlarmManager.OnAlarmListener alarmListener = + alarmListenerCaptor.getAllValues().get(0); + + // INACTIVE -> IDLE alarm + mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); + alarmListener.onAlarm(); + verifyLightStateConditions(LIGHT_STATE_IDLE); + alarmManagerInOrder.verify(mAlarmManager).setWindow( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(mInjector.nowElapsed + idlingTimeMs), + anyLong(), anyString(), any(), any(Handler.class)); + + for (int i = 0; i < 10; ++i) { + // IDLE->MAINTENANCE alarm + mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting(); + alarmListener.onAlarm(); + verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE); + long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs; + idlingTimeMs += mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS; + idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT); // Set MAINTENANCE->IDLE alarmManagerInOrder.verify(mAlarmManager).setWindow( eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java index 64cc3979f181..9ba4f5b4fe30 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java @@ -218,4 +218,9 @@ public class ActivityManagerInternalTest { assertEquals(errMsg, Thread.State.TERMINATED, getState()); } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 2bc66ace454b..40b5458b06b9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -1210,4 +1210,9 @@ public class ActivityManagerServiceTest { return returnValueForstartUserOnSecondaryDisplay; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java index 1c0989c4fb65..9391d5bfeed4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java @@ -338,4 +338,8 @@ public class AppChildProcessTest { } } + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index d56229c9681f..e15942bb8f9a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -1126,4 +1126,9 @@ public class ApplicationExitInfoTest { }; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java index 0abf46b8ee55..596a3f3d0400 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java @@ -284,4 +284,9 @@ public class AsyncProcessStartTest { return app; } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java index 434d20035369..dfb8fda56edf 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java @@ -803,4 +803,9 @@ public class CacheOomRankerTest { return mHandler; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java index fd1b06830a89..7ec27be0bfc3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java @@ -201,4 +201,9 @@ public final class ServiceTimeoutTest { return mActiveServices; } } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 2b56ea8bba33..bded9b40e591 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -34,6 +34,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER; import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; @@ -66,6 +67,7 @@ import android.net.NetworkPolicyManager; import android.os.Build; import android.os.Looper; import android.os.SystemClock; +import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.CellSignalStrength; import android.telephony.SignalStrength; import android.telephony.TelephonyCallback; @@ -79,6 +81,7 @@ import com.android.server.job.JobSchedulerService.Constants; import com.android.server.net.NetworkPolicyManagerInternal; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -107,6 +110,9 @@ public class ConnectivityControllerTest { @Mock private PackageManager mPackageManager; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private Constants mConstants; private FlexibilityController mFlexibilityController; @@ -898,7 +904,8 @@ public class ConnectivityControllerTest { assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); } - // Metered network is only when prefetching, late, and in opportunistic quota + // Metered network is only when prefetching, charging*, late, and in opportunistic quota + // *Charging only when the flag is enabled { final Network net = mock(Network.class); final NetworkCapabilities caps = createCapabilitiesBuilder() @@ -910,10 +917,27 @@ public class ConnectivityControllerTest { assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants)); assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); + mSetFlagsRule.disableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER); + when(mService.isBatteryCharging()).thenReturn(false); + + when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota( + any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS))) + .thenReturn(9876543210L); + assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); + // Only relax restrictions when we at least know the estimated download bytes. + assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); + assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); + mSetFlagsRule.enableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER); when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota( any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS))) .thenReturn(9876543210L); + assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants)); + // Only relax restrictions when we at least know the estimated download bytes. + assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); + assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); + + when(mService.isBatteryCharging()).thenReturn(true); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); // Only relax restrictions when we at least know the estimated download bytes. assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java index 6304270f9a76..305569edd2fa 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -308,7 +308,6 @@ public final class UserManagerServiceTest { addDefaultProfileAndParent(); mUms.setBootUser(PROFILE_USER_ID); - // Boot user not switchable so return most recently in foreground. assertWithMessage("getBootUser") .that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID); @@ -523,6 +522,24 @@ public final class UserManagerServiceTest { .isFalse(); } + @Test + public void testCreateUserWithLongName_TruncatesName() { + UserInfo user = mUms.createUserWithThrow(generateLongString(), USER_TYPE_FULL_SECONDARY, 0); + assertThat(user.name.length()).isEqualTo(500); + UserInfo user1 = mUms.createUserWithThrow("Test", USER_TYPE_FULL_SECONDARY, 0); + assertThat(user1.name.length()).isEqualTo(4); + } + + private String generateLongString() { + String partialString = "Test Name Test Name Test Name Test Name Test Name Test Name Test " + + "Name Test Name Test Name Test Name "; //String of length 100 + StringBuilder resultString = new StringBuilder(); + for (int i = 0; i < 660; i++) { + resultString.append(partialString); + } + return resultString.toString(); + } + private void removeNonSystemUsers() { for (UserInfo user : mUms.getUsers(true)) { if (!user.getUserHandle().isSystem()) { diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index 8ab45070a017..18a4f0068909 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -16,7 +16,7 @@ android_test { "coretests-aidl", "platformprotosnano", "junit", - "truth-prebuilt", + "truth", "androidx.test.runner", "androidx.test.ext.junit", "androidx.test.ext.truth", diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 20d8a5d1efeb..2ece8c74420c 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -2,6 +2,13 @@ // Build FrameworksServicesTests package //######################################################################## +java_defaults { + name: "FrameworksServicesTests-jni-defaults", + jni_libs: [ + "libservicestestjni", + ], +} + package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import @@ -13,6 +20,9 @@ package { android_test { name: "FrameworksServicesTests", + defaults: [ + "FrameworksServicesTests-jni-defaults", + ], // Include all test java files. srcs: [ @@ -51,7 +61,7 @@ android_test { "mockito-target-minus-junit4", "platform-test-annotations", "ShortcutManagerTestUtils", - "truth-prebuilt", + "truth", "testables", "androidx.test.uiautomator_uiautomator", "platformprotosnano", @@ -62,7 +72,7 @@ android_test { // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", - "truth-prebuilt", + "truth", "junit", "junit-params", "ActivityContext", diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp new file mode 100644 index 000000000000..174beb81d3eb --- /dev/null +++ b/services/tests/servicestests/jni/Android.bp @@ -0,0 +1,58 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_library_shared { + name: "libservicestestjni", + + defaults: ["android.hardware.graphics.common-ndk_shared"], + + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], + + srcs: [ + ":lib_cachedAppOptimizer_native", + ":lib_gameManagerService_native", + ":lib_oomConnection_native", + "onload.cpp", + ], + + include_dirs: [ + "frameworks/base/libs", + "frameworks/native/services", + "frameworks/native/libs/math/include", + "frameworks/native/libs/ui/include", + "system/memory/libmeminfo/include", + ], + + shared_libs: [ + "libandroid", + "libandroid_runtime", + "libbase", + "libbinder", + "libgralloctypes", + "libgui", + "libhidlbase", + "liblog", + "libmeminfo", + "libmemevents", + "libnativehelper", + "libprocessgroup", + "libutils", + "libcutils", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", + "android.hardware.graphics.common@1.2", + "android.hardware.graphics.mapper@4.0", + "android.hidl.token@1.0-utils", + ], +}
\ No newline at end of file diff --git a/services/tests/servicestests/jni/onload.cpp b/services/tests/servicestests/jni/onload.cpp new file mode 100644 index 000000000000..f160b3d97367 --- /dev/null +++ b/services/tests/servicestests/jni/onload.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * this is a mini native libaray for cached app optimizer tests to run properly. It + * loads all the native methods necessary. + */ +#include <nativehelper/JNIHelp.h> +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +namespace android { +int register_android_server_am_CachedAppOptimizer(JNIEnv* env); +int register_android_server_app_GameManagerService(JNIEnv* env); +int register_android_server_am_OomConnection(JNIEnv* env); +}; + +using namespace android; + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) +{ + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + ALOGE("GetEnv failed!"); + return result; + } + ALOG_ASSERT(env, "Could not retrieve the env!"); + register_android_server_am_CachedAppOptimizer(env); + register_android_server_app_GameManagerService(env); + register_android_server_am_OomConnection(env); + return JNI_VERSION_1_4; +} diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java index acdfee9af557..c0051c6c9e17 100644 --- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java @@ -172,4 +172,9 @@ public class AnrHelperTest { anyString(), any(), any(), any(), anyBoolean(), any(), eq(mAuxExecutorService), anyBoolean(), anyBoolean(), any()); } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("servicestestjni"); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java index 9fdbdda38c75..70527ce2ad32 100644 --- a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java @@ -228,6 +228,7 @@ public class AnrTimerTest { TestHandler mTestHandler; TestInjector(int skip, boolean immediate) { + super(mHandler); mTracker = new TestTracker(skip); mImmediate = immediate; } @@ -249,9 +250,16 @@ public class AnrTimerTest { return mTestHandler; } + @Override AnrTimer.CpuTracker getTracker() { return mTracker; } + + /** For test purposes, always enable the feature. */ + @Override + boolean getFeatureEnabled() { + return true; + } } // Tests @@ -261,7 +269,6 @@ public class AnrTimerTest { // 4. Start a couple of timers. Verify max active timers. Discard one and verify the active // count drops by 1. Accept one and verify the active count drops by 1. - @Test public void testSimpleTimeout() throws Exception { // Create an immediate TestHandler. diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 1d37f9da8d7a..d1f4961ab7e5 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -39,7 +39,7 @@ android_test { "hamcrest-library", "servicestests-utils", "testables", - "truth-prebuilt", + "truth", // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java index 312057ee922d..348d1bfd44df 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java @@ -44,6 +44,12 @@ import org.mockito.Captor; import org.mockito.Mock; import java.lang.reflect.Field; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.time.ZoneId; @RunWith(AndroidTestingRunner.class) public class NotificationBitmapJobServiceTest extends UiServiceTestCase { @@ -103,17 +109,39 @@ public class NotificationBitmapJobServiceTest extends UiServiceTestCase { @Test public void testGetTimeUntilRemoval_beforeToday2am_returnTimeUntilToday2am() { - final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 1, - /* today2AM= */ 2, /* tomorrow2AM= */ 26); + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime now = Instant.now().atZone(zoneId); + LocalDate today = now.toLocalDate(); - assertThat(timeUntilRemoval).isEqualTo(1); + LocalTime oneAM = LocalTime.of(/* hour= */ 1, /* minute= */ 0); + LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0); + + ZonedDateTime today1AM = ZonedDateTime.of(today, oneAM, zoneId); + ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId); + ZonedDateTime tomorrow2AM = today2AM.plusDays(1); + + final long msUntilRemoval = mJobService.getTimeUntilRemoval( + /* now= */ today1AM, today2AM, tomorrow2AM); + + assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(1).toMillis()); } @Test public void testGetTimeUntilRemoval_afterToday2am_returnTimeUntilTomorrow2am() { - final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 3, - /* today2AM= */ 2, /* tomorrow2AM= */ 26); + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime now = Instant.now().atZone(zoneId); + LocalDate today = now.toLocalDate(); + + LocalTime threeAM = LocalTime.of(/* hour= */ 3, /* minute= */ 0); + LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0); + + ZonedDateTime today3AM = ZonedDateTime.of(today, threeAM, zoneId); + ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId); + ZonedDateTime tomorrow2AM = today2AM.plusDays(1); + + final long msUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ today3AM, + today2AM, tomorrow2AM); - assertThat(timeUntilRemoval).isEqualTo(23); + assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(23).toMillis()); } }
\ No newline at end of file diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java index d758e71c62a2..3499a12f5954 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java @@ -71,10 +71,10 @@ public class NotificationHistoryJobServiceTest extends UiServiceTestCase { @Before public void setUp() throws Exception { mJobService = new NotificationHistoryJobService(); + mJobService.attachBaseContext(mContext); + mJobService.onCreate(); + mJobService.onBind(/* intent= */ null); // Create JobServiceEngine within JobService. - final Field field = JobService.class.getDeclaredField("mEngine"); - field.setAccessible(true); - field.set(mJobService, mock(JobServiceEngine.class)); mContext.addMockSystemService(JobScheduler.class, mMockJobScheduler); // add NotificationManagerInternal to LocalServices diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 7a55143ba13f..c05f81497e57 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -462,7 +462,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) { - assertEquals(a.getRankingMap(), b.getRankingMap()); + detailedAssertEquals(a.getRankingMap(), b.getRankingMap()); } private void detailedAssertEquals(String comment, Ranking a, Ranking b) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index c98d2359b2f9..91129a14ecab 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -91,7 +91,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER; -import static com.android.server.notification.NotificationManagerService.BITMAP_EXPIRATION_TIME_MS; +import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION; import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; @@ -11381,7 +11381,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { long timePostedMs = System.currentTimeMillis(); if (isExpired) { - timePostedMs -= BITMAP_EXPIRATION_TIME_MS; + timePostedMs -= BITMAP_DURATION.toMillis(); } StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, notification, UserHandle.getUserHandleForUid(mUid), null, timePostedMs); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java index a91bd2b55f76..000355598281 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java @@ -16,6 +16,8 @@ package com.android.server.vibrator; +import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY; +import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF; import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK; import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK; @@ -24,8 +26,13 @@ import static android.view.HapticFeedbackConstants.CLOCK_TICK; import static android.view.HapticFeedbackConstants.CONTEXT_CLICK; import static android.view.HapticFeedbackConstants.SAFE_MODE_ENABLED; import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE; +import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS; +import static android.view.HapticFeedbackConstants.SCROLL_LIMIT; +import static android.view.HapticFeedbackConstants.SCROLL_TICK; + import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.when; @@ -37,6 +44,7 @@ import android.os.VibrationEffect; import android.os.VibratorInfo; import android.util.AtomicFile; import android.util.SparseArray; +import android.view.flags.FeatureFlags; import androidx.test.InstrumentationRegistry; @@ -59,23 +67,25 @@ public class HapticFeedbackVibrationProviderTest { private static final VibrationEffect PRIMITIVE_CLICK_EFFECT = VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK, 0.3497f).compose(); + private static final int[] SCROLL_FEEDBACK_CONSTANTS = + new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK}; private Context mContext = InstrumentationRegistry.getContext(); private VibratorInfo mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO; @Mock private Resources mResourcesMock; + @Mock private FeatureFlags mViewFeatureFlags; @Test public void testNonExistentCustomization_useDefault() throws Exception { // No customization file is set. - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) .isEqualTo(VibrationEffect.get(EFFECT_TICK)); // The customization file specifies no customization. setupCustomizationFile("<haptic-feedback-constants></haptic-feedback-constants>"); - hapticProvider = new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + hapticProvider = createProviderWithDefaultCustomizations(); assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) .isEqualTo(VibrationEffect.get(EFFECT_TICK)); @@ -84,8 +94,7 @@ public class HapticFeedbackVibrationProviderTest { @Test public void testExceptionParsingCustomizations_useDefault() throws Exception { setupCustomizationFile("<bad-xml></bad-xml>"); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) .isEqualTo(VibrationEffect.get(EFFECT_TICK)); @@ -97,8 +106,7 @@ public class HapticFeedbackVibrationProviderTest { SparseArray<VibrationEffect> customizations = new SparseArray<>(); customizations.put(CONTEXT_CLICK, PRIMITIVE_CLICK_EFFECT); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); // The override for `CONTEXT_CLICK` is used. assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) @@ -118,8 +126,7 @@ public class HapticFeedbackVibrationProviderTest { + "</haptic-feedback-constants>"; setupCustomizationFile(xml); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); // The override for `CONTEXT_CLICK` is not used because the vibration is not supported. assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK)) @@ -137,15 +144,12 @@ public class HapticFeedbackVibrationProviderTest { customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT); // Test with a customization available for `TEXT_HANDLE_MOVE`. - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull(); // Test with no customization available for `TEXT_HANDLE_MOVE`. - hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull(); } @@ -158,16 +162,13 @@ public class HapticFeedbackVibrationProviderTest { customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT); // Test with a customization available for `TEXT_HANDLE_MOVE`. - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)) .isEqualTo(PRIMITIVE_CLICK_EFFECT); // Test with no customization available for `TEXT_HANDLE_MOVE`. - hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)) .isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK)); @@ -181,15 +182,13 @@ public class HapticFeedbackVibrationProviderTest { SparseArray<VibrationEffect> customizations = new SparseArray<>(); customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)) .isEqualTo(PRIMITIVE_CLICK_EFFECT); mockSafeModeEnabledVibration(null); - hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations); + hapticProvider = createProvider(customizations); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)) .isEqualTo(PRIMITIVE_CLICK_EFFECT); @@ -199,9 +198,7 @@ public class HapticFeedbackVibrationProviderTest { public void testNoValidCustomizationPresentForSafeModeEnabled_resourceBasedVibrationUsed() throws Exception { mockSafeModeEnabledVibration(10, 20, 30, 40); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)) .isEqualTo(VibrationEffect.createWaveform(new long[] {10, 20, 30, 40}, -1)); @@ -211,35 +208,65 @@ public class HapticFeedbackVibrationProviderTest { public void testNoValidCustomizationAndResourcePresentForSafeModeEnabled_noVibrationUsed() throws Exception { mockSafeModeEnabledVibration(null); - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider( - mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null); + HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null); assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)).isNull(); } @Test public void testVibrationAttribute_forNotBypassingIntensitySettings() { - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false); - assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .isEqualTo(0); + assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isFalse(); } @Test public void testVibrationAttribute_forByassingIntensitySettings() { - HapticFeedbackVibrationProvider hapticProvider = - new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true); - assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .isNotEqualTo(0); + assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isTrue(); + } + + @Test + public void testVibrationAttribute_scrollFeedback_scrollApiFlagOn_bypassInterruptPolicy() { + when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); + + for (int effectId : SCROLL_FEEDBACK_CONSTANTS) { + VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( + effectId, /* bypassVibrationIntensitySetting= */ false); + assertWithMessage("Expected FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId) + .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isTrue(); + } + } + + @Test + public void testVibrationAttribute_scrollFeedback_scrollApiFlagOff_noBypassInterruptPolicy() { + when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(false); + HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations(); + + for (int effectId : SCROLL_FEEDBACK_CONSTANTS) { + VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback( + effectId, /* bypassVibrationIntensitySetting= */ false); + assertWithMessage("Expected no FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId) + .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isFalse(); + } + } + + private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() { + return createProvider(/* customizations= */ null); + } + + private HapticFeedbackVibrationProvider createProvider( + SparseArray<VibrationEffect> customizations) { + return new HapticFeedbackVibrationProvider( + mResourcesMock, mVibratorInfo, customizations, mViewFeatureFlags); } private void mockVibratorPrimitiveSupport(int... supportedPrimitives) { diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 0eec9cd93f3b..40e0e84dca59 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -46,6 +47,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.res.Resources; import android.hardware.input.IInputManager; @@ -87,6 +89,7 @@ import android.util.SparseBooleanArray; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.InputDevice; +import android.view.flags.FeatureFlags; import androidx.test.InstrumentationRegistry; import androidx.test.filters.FlakyTest; @@ -172,6 +175,8 @@ public class VibratorManagerServiceTest { private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock; @Mock private AudioManager mAudioManagerMock; + @Mock + private FeatureFlags mViewFeatureFlags; private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); @@ -321,7 +326,8 @@ public class VibratorManagerServiceTest { HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider( Resources resources, VibratorInfo vibratorInfo) { return new HapticFeedbackVibrationProvider( - resources, vibratorInfo, mHapticFeedbackVibrationMap); + resources, vibratorInfo, mHapticFeedbackVibrationMap, + mViewFeatureFlags); } }); return mService; @@ -649,6 +655,42 @@ public class VibratorManagerServiceTest { } @Test + public void vibrate_withoutBypassFlagsPermissions_bypassFlagsNotApplied() throws Exception { + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(false); + } + + @Test + public void vibrate_withSecureSettingsPermission_bypassFlagsApplied() throws Exception { + grantPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(true); + } + + @Test + public void vibrate_withModifyPhoneStatePermission_bypassFlagsApplied() throws Exception { + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(true); + } + + @Test + public void vibrate_withModifyAudioRoutingPermission_bypassFlagsApplied() throws Exception { + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + grantPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + assertCanVibrateWithBypassFlags(true); + } + + @Test public void vibrate_withRingtone_usesRingerModeSettings() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); @@ -1166,7 +1208,7 @@ public class VibratorManagerServiceTest { } @Test - public void vibrate_withTriggerCallback_finishesVibration() throws Exception { + public void vibrate_withriggerCallback_finishesVibration() throws Exception { mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE); mockVibrators(1, 2); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); @@ -1303,10 +1345,18 @@ public class VibratorManagerServiceTest { } @Test - public void performHapticFeedback_doesNotRequirePermission() throws Exception { + public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception { + // Deny permissions that would have been required for regular vibrations, and check that + // the vibration proceed as expected to verify that haptic feedback does not need these + // permissions. denyPermission(android.Manifest.permission.VIBRATE); + denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); + denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); + denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + // Flag override to enable the scroll feedack constants to bypass interruption policies. + when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true); mHapticFeedbackVibrationMap.put( - HapticFeedbackConstants.KEYBOARD_TAP, + HapticFeedbackConstants.SCROLL_TICK, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); @@ -1315,13 +1365,16 @@ public class VibratorManagerServiceTest { HalVibration vibration = performHapticFeedbackAndWaitUntilFinished( - service, HapticFeedbackConstants.KEYBOARD_TAP, /* always= */ true); + service, HapticFeedbackConstants.SCROLL_TICK, /* always= */ true); List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments(); assertEquals(1, playedSegments.size()); PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0); assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId()); - assertEquals(VibrationAttributes.USAGE_TOUCH, vibration.callerInfo.attrs.getUsage()); + VibrationAttributes attrs = vibration.callerInfo.attrs; + assertEquals(VibrationAttributes.USAGE_HARDWARE_FEEDBACK, attrs.getUsage()); + assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)); + assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)); } @Test @@ -2261,6 +2314,31 @@ public class VibratorManagerServiceTest { assertNull(metrics.halUnsupportedEffectsUsed); } + private void assertCanVibrateWithBypassFlags(boolean expectedCanApplyBypassFlags) + throws Exception { + mockVibrators(1); + mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + VibratorManagerService service = createSystemReadyService(); + + HalVibration vibration = vibrateAndWaitUntilFinished( + service, + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), + new VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_TOUCH) + .setFlags( + VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF + | VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) + .build()); + + VibrationAttributes attrs = vibration.callerInfo.attrs; + assertEquals( + expectedCanApplyBypassFlags, + attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)); + assertEquals( + expectedCanApplyBypassFlags, + attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)); + } + private VibrationEffectSegment expectedPrebaked(int effectId) { return expectedPrebaked(effectId, VibrationEffect.EFFECT_STRENGTH_MEDIUM); } @@ -2327,12 +2405,13 @@ public class VibratorManagerServiceTest { return vib; } - private void vibrateAndWaitUntilFinished(VibratorManagerService service, VibrationEffect effect, - VibrationAttributes attrs) throws InterruptedException { - vibrateAndWaitUntilFinished(service, CombinedVibration.createParallel(effect), attrs); + private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service, + VibrationEffect effect, VibrationAttributes attrs) throws InterruptedException { + return vibrateAndWaitUntilFinished( + service, CombinedVibration.createParallel(effect), attrs); } - private void vibrateAndWaitUntilFinished(VibratorManagerService service, + private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service, CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException { HalVibration vib = service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME, @@ -2340,6 +2419,8 @@ public class VibratorManagerServiceTest { if (vib != null) { vib.waitForEnd(); } + + return vib; } private void vibrate(VibratorManagerService service, VibrationEffect effect, @@ -2368,7 +2449,15 @@ public class VibratorManagerServiceTest { return predicateResult; } + private void grantPermission(String permission) { + when(mContextSpy.checkCallingOrSelfPermission(permission)) + .thenReturn(PackageManager.PERMISSION_GRANTED); + doNothing().when(mContextSpy).enforceCallingOrSelfPermission(eq(permission), anyString()); + } + private void denyPermission(String permission) { + when(mContextSpy.checkCallingOrSelfPermission(permission)) + .thenReturn(PackageManager.PERMISSION_DENIED); doThrow(new SecurityException()).when(mContextSpy) .enforceCallingOrSelfPermission(eq(permission), anyString()); } diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp index e704ebf32270..744cb63f72b3 100644 --- a/services/tests/voiceinteractiontests/Android.bp +++ b/services/tests/voiceinteractiontests/Android.bp @@ -43,7 +43,7 @@ android_test { "services.soundtrigger", "servicestests-core-utils", "servicestests-utils-mockito-extended", - "truth-prebuilt", + "truth", ], libs: [ diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index c2812a14a3eb..af39b2f027ee 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -57,12 +57,14 @@ android_test { "platform-test-annotations", "servicestests-utils", "testng", - "truth-prebuilt", + "truth", "testables", "hamcrest-library", "platform-compat-test-rules", "CtsSurfaceValidatorLib", "service-sdksandbox.impl", + "com.android.window.flags.window-aconfig-java", + "flag-junit", ], libs: [ diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 42e3383987d6..762e23c8e288 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -47,6 +47,8 @@ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/> + <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/> + <uses-permission android:name="android.permission.MONITOR_INPUT"/> <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" @@ -104,6 +106,11 @@ android:showWhenLocked="true" android:turnScreenOn="true" /> + <activity android:name="android.app.Activity" + android:exported="true" + android:showWhenLocked="true" + android:turnScreenOn="true" /> + <activity android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity" android:exported="true"> diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java index 0a7bb00ce1c2..71098aa5e883 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java @@ -20,6 +20,7 @@ import static com.android.server.policy.PhoneWindowManager.DOUBLE_TAP_HOME_RECEN import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL; +import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL; import android.platform.test.annotations.Presubmit; import android.view.KeyEvent; @@ -284,6 +285,16 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_H, META_ON}}; } + @Keep + private static Object[][] shortPressOnSettingsTestArguments() { + // testName, testKeys, shortPressOnSettingsBehavior, expectedLogEvent, expectedKey, + // expectedModifierState + return new Object[][]{ + {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS}, + SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL, + KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_SETTINGS, 0}}; + } + @Before public void setUp() { setUpPhoneWindowManager(/*supportSettingsUpdate*/ true); @@ -294,6 +305,7 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { mPhoneWindowManager.overrideEnableBugReportTrigger(true); mPhoneWindowManager.overrideStatusBarManagerInternal(); mPhoneWindowManager.overrideStartActivity(); + mPhoneWindowManager.overrideSendBroadcast(); mPhoneWindowManager.overrideUserSetupComplete(); mPhoneWindowManager.setupAssistForLaunch(); mPhoneWindowManager.overrideTogglePanel(); @@ -330,4 +342,15 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent, expectedKey, expectedModifierState, "Failed while executing " + testName); } + + @Test + @Parameters(method = "shortPressOnSettingsTestArguments") + public void testShortPressOnSettings(String testName, int[] testKeys, + int shortPressOnSettingsBehavior, KeyboardLogEvent expectedLogEvent, int expectedKey, + int expectedModifierState) { + mPhoneWindowManager.overrideShortPressOnSettingsBehavior(shortPressOnSettingsBehavior); + sendKeyCombination(testKeys, 0 /* duration */); + mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent, + expectedKey, expectedModifierState, "Failed while executing " + testName); + } } diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index ef28ffa7da8f..2244dbe8af98 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -375,6 +375,10 @@ class TestPhoneWindowManager { mPhoneWindowManager.mDoubleTapOnHomeBehavior = behavior; } + void overrideShortPressOnSettingsBehavior(int behavior) { + mPhoneWindowManager.mShortPressOnSettingsBehavior = behavior; + } + void overrideCanStartDreaming(boolean canDream) { doReturn(canDream).when(mDreamManagerInternal).canStartDreaming(anyBoolean()); } @@ -484,6 +488,10 @@ class TestPhoneWindowManager { doNothing().when(mContext).startActivityAsUser(any(), any(), any()); } + void overrideSendBroadcast() { + doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + } + void overrideUserSetupComplete() { doReturn(true).when(mPhoneWindowManager).isUserSetupComplete(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 3bc6450ae591..c241033c69d3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -549,10 +549,12 @@ public class RootWindowContainerTests extends WindowTestsBase { // Let's pretend that the app has crashed. firstActivity.app.setThread(null); - mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); + final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities( + firstActivity.app, "test"); // Verify that the root task was removed. assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount()); + assertEquals(rootTask, finishedTask); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java index c1d51474b1cd..8119fd486a87 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java @@ -138,11 +138,11 @@ public class SurfaceControlViewHostTests { IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken()); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window, - mScvh1.getFocusGrantToken(), true); + mScvh1.getInputTransferToken(), true); assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true)); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window, - mScvh2.getFocusGrantToken(), true); + mScvh2.getInputTransferToken(), true); assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index 91256ee8a1c4..4a335944228a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -216,7 +216,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { try { final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); mWm.mTaskSnapshotController.createSnapshot(mAppWindow.mActivityRecord.getTask(), - 1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */, builder); + 1f /* scaleFraction */, new Rect() /* crop */, builder); } catch (NullPointerException e) { fail("There should be no exception when calling createTaskSnapshot"); } @@ -238,7 +238,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { spyOn(builder); mWm.mTaskSnapshotController.createSnapshot( mAppWindow.mActivityRecord.getTask(), 1f /* scaleFraction */, - PixelFormat.UNKNOWN, null /* outTaskSize */, builder); + new Rect() /* crop */, builder); // Verify the builder should includes IME surface. verify(builder).setHasImeSurface(eq(true)); builder.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)); @@ -261,7 +261,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot( - mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); + mAppWindow.mActivityRecord.getTask(), builder) != null; assertTrue(success); // The pixel format should be selected automatically. @@ -270,7 +270,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { // Snapshot should not be taken while the rotation of activity and task are different. doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform(); success = mWm.mTaskSnapshotController.prepareTaskSnapshot( - mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); + mAppWindow.mActivityRecord.getTask(), builder) != null; assertFalse(success); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 241f7d8937f2..474720f68731 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1442,7 +1442,7 @@ public class TransitionTests extends WindowTestsBase { // normally. mWm.mSyncEngine.abort(openTransition.getSyncId()); - verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2), eq(false)); + verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2)); controller.finishTransition(openTransition); @@ -1482,7 +1482,7 @@ public class TransitionTests extends WindowTestsBase { // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be // called until finish). - verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1), eq(false)); + verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1)); enteringAnimReports.clear(); doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), @@ -1515,7 +1515,7 @@ public class TransitionTests extends WindowTestsBase { assertFalse(activity1.isVisible()); assertFalse(activity1.app.hasActivityInVisibleTask()); - verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1), eq(false)); + verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1)); assertTrue(enteringAnimReports.contains(activity2)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java new file mode 100644 index 000000000000..ac498397eb39 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java @@ -0,0 +1,217 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.app.Instrumentation; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.server.wm.BuildUtils; +import android.server.wm.CtsWindowInfoUtils; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.window.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@Presubmit +public class TrustedOverlayTests { + private static final String TAG = "TrustedOverlayTests"; + private static final long TIMEOUT_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Rule + public TestName mName = new TestName(); + + @Rule + public final ActivityScenarioRule<Activity> mActivityRule = new ActivityScenarioRule<>( + Activity.class); + + private Instrumentation mInstrumentation; + private Activity mActivity; + + @Before + public void setup() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mActivityRule.getScenario().onActivity(activity -> { + mActivity = activity; + }); + } + + @RequiresFlagsDisabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY) + @Test + public void setTrustedOverlayInputWindow() throws InterruptedException { + testTrustedOverlayChildHelper(false); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY) + public void setTrustedOverlayChildLayer() throws InterruptedException { + testTrustedOverlayChildHelper(true); + } + + /** + * b/300659960 where setting spy window and trusted overlay were not happening in the same + * transaction causing the system to crash. This ensures there are no synchronization issues + * setting both spy window and trusted overlay. + */ + @Test + public void setSpyWindowDoesntCrash() throws InterruptedException { + IBinder[] tokens = new IBinder[1]; + CountDownLatch hostTokenReady = new CountDownLatch(1); + mInstrumentation.runOnMainSync(() -> { + WindowManager.LayoutParams params = mActivity.getWindow().getAttributes(); + params.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + params.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; + mActivity.getWindow().setAttributes(params); + + View rootView = mActivity.getWindow().getDecorView(); + if (rootView.isAttachedToWindow()) { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } else { + rootView.getViewTreeObserver().addOnWindowAttachListener( + new ViewTreeObserver.OnWindowAttachListener() { + @Override + public void onWindowAttached() { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } + + @Override + public void onWindowDetached() { + } + }); + } + }); + + assertTrue("Failed to wait for host to get added", + hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS)); + + boolean[] foundTrusted = new boolean[1]; + CtsWindowInfoUtils.waitForWindowInfos( + windowInfos -> { + for (var windowInfo : windowInfos) { + if (windowInfo.windowToken == tokens[0] && windowInfo.isTrustedOverlay) { + foundTrusted[0] = true; + return true; + } + } + return false; + }, TIMEOUT_S, TimeUnit.SECONDS); + + if (!foundTrusted[0]) { + CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName()); + } + + assertTrue("Failed to find window or was not marked trusted", foundTrusted[0]); + } + + private void testTrustedOverlayChildHelper(boolean expectedTrustedChild) + throws InterruptedException { + IBinder[] tokens = new IBinder[2]; + CountDownLatch hostTokenReady = new CountDownLatch(1); + mInstrumentation.runOnMainSync(() -> { + mActivity.getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY); + View rootView = mActivity.getWindow().getDecorView(); + if (rootView.isAttachedToWindow()) { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } else { + rootView.getViewTreeObserver().addOnWindowAttachListener( + new ViewTreeObserver.OnWindowAttachListener() { + @Override + public void onWindowAttached() { + tokens[0] = rootView.getWindowToken(); + hostTokenReady.countDown(); + } + + @Override + public void onWindowDetached() { + } + }); + } + }); + + assertTrue("Failed to wait for host to get added", + hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS)); + + mInstrumentation.runOnMainSync(() -> { + WindowManager wm = mActivity.getSystemService(WindowManager.class); + + View childView = new View(mActivity) { + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + tokens[1] = getWindowToken(); + } + }; + WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.token = tokens[0]; + params.type = TYPE_APPLICATION_PANEL; + wm.addView(childView, params); + }); + + boolean[] foundTrusted = new boolean[2]; + + CtsWindowInfoUtils.waitForWindowInfos( + windowInfos -> { + for (var windowInfo : windowInfos) { + if (windowInfo.windowToken == tokens[0] + && windowInfo.isTrustedOverlay) { + foundTrusted[0] = true; + } else if (windowInfo.windowToken == tokens[1] + && windowInfo.isTrustedOverlay) { + foundTrusted[1] = true; + } + } + return foundTrusted[0] && foundTrusted[1]; + }, TIMEOUT_S, TimeUnit.SECONDS); + + if (!foundTrusted[0] || !foundTrusted[1]) { + CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName()); + } + + assertTrue("Failed to find parent window or was not marked trusted", foundTrusted[0]); + assertEquals("Failed to find child window or was not marked trusted", expectedTrustedChild, + foundTrusted[1]); + } +} diff --git a/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java b/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java deleted file mode 100644 index 6801c9402538..000000000000 --- 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 18c960e3ebbc..f3bf026ddc6e 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "loadPendingEvents"); loadPendingEventsLocked(userId, pendingEvents); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - final LinkedList<Event> eventsInMem = mReportedEvents.get(userId); - if (eventsInMem != null) { - pendingEvents.addAll(eventsInMem); + synchronized (mReportedEvents) { + final LinkedList<Event> eventsInMem = mReportedEvents.get(userId); + if (eventsInMem != null) { + pendingEvents.addAll(eventsInMem); + mReportedEvents.remove(userId); + } } boolean needToFlush = !pendingEvents.isEmpty(); @@ -518,8 +520,7 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } } + @GuardedBy({"mLock", "mReportedEvents"}) private void persistPendingEventsLocked(int userId) { final LinkedList<Event> pendingEvents = mReportedEvents.get(userId); if (pendingEvents == null || pendingEvents.isEmpty()) { @@ -1019,7 +1021,7 @@ public class UsageStatsService extends SystemService implements + UserUsageStatsService.eventToString(event.mEventType); Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, traceTag); } - synchronized (mLock) { + synchronized (mReportedEvents) { LinkedList<Event> events = mReportedEvents.get(userId); if (events == null) { events = new LinkedList<>(); @@ -1943,16 +1945,19 @@ public class UsageStatsService extends SystemService implements idpw.println(); } } else { - final LinkedList<Event> pendingEvents = mReportedEvents.get(userId); - if (pendingEvents != null && !pendingEvents.isEmpty()) { - final int eventCount = pendingEvents.size(); - idpw.println("Pending events: count=" + eventCount); - idpw.increaseIndent(); - for (int idx = 0; idx < eventCount; idx++) { - UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), true); + synchronized (mReportedEvents) { + final LinkedList<Event> pendingEvents = mReportedEvents.get(userId); + if (pendingEvents != null && !pendingEvents.isEmpty()) { + final int eventCount = pendingEvents.size(); + idpw.println("Pending events: count=" + eventCount); + idpw.increaseIndent(); + for (int idx = 0; idx < eventCount; idx++) { + UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), + true); + } + idpw.decreaseIndent(); + idpw.println(); } - idpw.decreaseIndent(); - idpw.println(); } } idpw.decreaseIndent(); diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp index 5233a5b8654e..c2a70151fa13 100644 --- a/tests/BatteryStatsPerfTest/Android.bp +++ b/tests/BatteryStatsPerfTest/Android.bp @@ -27,7 +27,7 @@ android_test { static_libs: [ "androidx.test.rules", "apct-perftests-utils", - "truth-prebuilt", + "truth", ], platform_apis: true, certificate: "platform", diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp index 615990f22e3c..38cb9869f165 100644 --- a/tests/BinaryTransparencyHostTest/Android.bp +++ b/tests/BinaryTransparencyHostTest/Android.bp @@ -30,7 +30,7 @@ java_test_host { "compatibility-host-util", ], static_libs: [ - "truth-prebuilt", + "truth", ], data: [ ":BinaryTransparencyTestApp", diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp index c4faf7f4fb11..1fb73e2c0967 100644 --- a/tests/BlobStoreTestUtils/Android.bp +++ b/tests/BlobStoreTestUtils/Android.bp @@ -22,12 +22,12 @@ package { } java_library { - name: "BlobStoreTestUtils", - srcs: ["src/**/*.java"], - static_libs: [ - "truth-prebuilt", - "androidx.test.uiautomator_uiautomator", - "androidx.test.ext.junit", - ], - sdk_version: "test_current", + name: "BlobStoreTestUtils", + srcs: ["src/**/*.java"], + static_libs: [ + "truth", + "androidx.test.uiautomator_uiautomator", + "androidx.test.ext.junit", + ], + sdk_version: "test_current", } diff --git a/tests/ChoreographerTests/Android.bp b/tests/ChoreographerTests/Android.bp index ca3026705c63..5d49120ee702 100644 --- a/tests/ChoreographerTests/Android.bp +++ b/tests/ChoreographerTests/Android.bp @@ -34,7 +34,7 @@ android_test { "androidx.test.rules", "compatibility-device-util-axt", "com.google.android.material_material", - "truth-prebuilt", + "truth", ], jni_libs: [ "libchoreographertests_jni", diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp index 680952157fdc..96e4a9ea4300 100644 --- a/tests/CtsSurfaceControlTestsStaging/Android.bp +++ b/tests/CtsSurfaceControlTestsStaging/Android.bp @@ -37,7 +37,7 @@ android_test { "compatibility-device-util-axt", "com.google.android.material_material", "SurfaceFlingerProperties", - "truth-prebuilt", + "truth", ], resource_dirs: ["src/main/res"], certificate: "platform", diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp index 448d46fe5e4e..3f2c80831565 100644 --- a/tests/DynamicCodeLoggerIntegrationTests/Android.bp +++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp @@ -47,7 +47,7 @@ android_test { static_libs: [ "androidx.test.rules", - "truth-prebuilt", + "truth", ], compile_multilib: "both", diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index a2ae56eaeadc..82aa85d55e4c 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -236,7 +236,7 @@ java_library { static_libs: [ "flickerlib", "flickerlib-helpers", - "truth-prebuilt", + "truth", "app-helpers-core", ], } @@ -255,7 +255,7 @@ java_library { "flickerlib", "flickerlib-apphelpers", "flickerlib-helpers", - "truth-prebuilt", + "truth", "app-helpers-core", "wm-flicker-window-extensions", ], diff --git a/tests/FsVerityTest/TEST_MAPPING b/tests/FsVerityTest/TEST_MAPPING index 39944bed3f60..7d59d7765183 100644 --- a/tests/FsVerityTest/TEST_MAPPING +++ b/tests/FsVerityTest/TEST_MAPPING @@ -1,12 +1,7 @@ { - "postsubmit": [ + "presubmit": [ { "name": "FsVerityTest" - }, - // nextgen test only runs during postsubmit. - { - "name": "FsVerityTest", - "keywords": ["nextgen"] } ] } diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 365e00e2b652..cf2d5d69552f 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -9,6 +9,10 @@ package { android_test { name: "InputTests", + defaults: [ + // For ExtendedMockito dependencies. + "modules-utils-testable-device-config-defaults", + ], srcs: [ "src/**/*.java", "src/**/*.kt", @@ -35,7 +39,7 @@ android_test { "services.core.unboosted", "testables", "testng", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index b64775103ab2..fa86e9c4ec0a 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -32,11 +32,16 @@ import android.os.Bundle import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.provider.Settings +import android.util.proto.ProtoOutputStream import android.view.InputDevice import android.view.inputmethod.InputMethodInfo import android.view.inputmethod.InputMethodSubtype import androidx.test.core.R import androidx.test.core.app.ApplicationProvider +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.internal.os.KeyboardConfiguredProto +import com.android.internal.util.FrameworkStatsLog +import com.android.modules.utils.testing.ExtendedMockitoRule import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals @@ -46,9 +51,9 @@ import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito -import org.mockito.junit.MockitoJUnit import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.IOException @@ -96,6 +101,9 @@ class KeyboardLayoutManagerTests { private const val ENGLISH_US_LAYOUT_NAME = "keyboard_layout_english_us" private const val ENGLISH_UK_LAYOUT_NAME = "keyboard_layout_english_uk" private const val VENDOR_SPECIFIC_LAYOUT_NAME = "keyboard_layout_vendorId:1,productId:1" + const val LAYOUT_TYPE_QWERTZ = 2 + const val LAYOUT_TYPE_QWERTY = 1 + const val LAYOUT_TYPE_DEFAULT = 0 } private val ENGLISH_US_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_US_LAYOUT_NAME) @@ -103,8 +111,10 @@ class KeyboardLayoutManagerTests { private val VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR = createLayoutDescriptor(VENDOR_SPECIFIC_LAYOUT_NAME) - @get:Rule - val rule = MockitoJUnit.rule()!! + @JvmField + @Rule + val extendedMockitoRule = ExtendedMockitoRule.Builder(this) + .mockStatic(FrameworkStatsLog::class.java).build()!! @Mock private lateinit var iInputManager: IInputManager @@ -145,7 +155,9 @@ class KeyboardLayoutManagerTests { override fun finishWrite(fos: FileOutputStream?, success: Boolean) {} }) testLooper = TestLooper() - keyboardLayoutManager = KeyboardLayoutManager(context, native, dataStore, testLooper.looper) + keyboardLayoutManager = Mockito.spy( + KeyboardLayoutManager(context, native, dataStore, testLooper.looper) + ) setupInputDevices() setupBroadcastReceiver() setupIme() @@ -827,6 +839,100 @@ class KeyboardLayoutManagerTests { } } + @Test + fun testConfigurationLogged_onInputDeviceAdded_VirtualKeyboardBasedSelection() { + val imeInfos = listOf( + KeyboardLayoutManager.ImeInfo(0, imeInfo, + createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"))) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(keyboardDevice.vendorId), + ArgumentMatchers.eq(keyboardDevice.productId), + ArgumentMatchers.eq(createByteArray( + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT, + "German", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, + "de-Latn", + LAYOUT_TYPE_QWERTZ)) + ) + } + } + } + + @Test + fun testConfigurationLogged_onInputDeviceAdded_DeviceBasedSelection() { + val imeInfos = listOf( + KeyboardLayoutManager.ImeInfo(0, imeInfo, + createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"))) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId), + ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId), + ArgumentMatchers.eq(createByteArray( + "en", + LAYOUT_TYPE_QWERTY, + "English (US)", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE, + "de-Latn", + LAYOUT_TYPE_QWERTZ)) + ) + } + } + } + + @Test + fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() { + val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype())) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(keyboardDevice.vendorId), + ArgumentMatchers.eq(keyboardDevice.productId), + ArgumentMatchers.eq(createByteArray( + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT, + "Default", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT, + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT)) + ) + } + } + } + + @Test + fun testConfigurationNotLogged_onInputDeviceChanged() { + val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype())) + Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping + NewSettingsApiFlag(true).use { + keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) + ExtendedMockito.verify({ + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any(ByteArray::class.java) + ) + }, Mockito.times(0)) + } + } + private fun assertCorrectLayout( device: InputDevice, imeSubtype: InputMethodSubtype, @@ -842,18 +948,60 @@ class KeyboardLayoutManagerTests { } private fun createImeSubtype(): InputMethodSubtype = - InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++).build() + createImeSubtypeForLanguageTagAndLayoutType(null, null) private fun createImeSubtypeForLanguageTag(languageTag: String): InputMethodSubtype = - InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++) - .setLanguageTag(languageTag).build() + createImeSubtypeForLanguageTagAndLayoutType(languageTag, null) private fun createImeSubtypeForLanguageTagAndLayoutType( - languageTag: String, - layoutType: String - ): InputMethodSubtype = - InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++) - .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build() + languageTag: String?, + layoutType: String? + ): InputMethodSubtype { + val builder = InputMethodSubtype.InputMethodSubtypeBuilder() + .setSubtypeId(nextImeSubtypeId++) + .setIsAuxiliary(false) + .setSubtypeMode("keyboard") + if (languageTag != null && layoutType != null) { + builder.setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType) + } else if (languageTag != null) { + builder.setLanguageTag(languageTag) + } + return builder.build() + } + + private fun createByteArray( + expectedLanguageTag: String, expectedLayoutType: Int, expectedLayoutName: String, + expectedCriteria: Int, expectedImeLanguageTag: String, expectedImeLayoutType: Int): ByteArray { + val proto = ProtoOutputStream() + val keyboardLayoutConfigToken = proto.start( + KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG, + expectedLanguageTag + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE, + expectedLayoutType + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME, + expectedLayoutName + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA, + expectedCriteria + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG, + expectedImeLanguageTag + ) + proto.write( + KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE, + expectedImeLayoutType + ) + proto.end(keyboardLayoutConfigToken); + return proto.bytes + } private fun hasLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean { for (kl in layoutList) { diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java index 1b98887199e3..ae7fb3b29f6c 100644 --- a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java +++ b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java @@ -14,15 +14,16 @@ * limitations under the License. */ -package com.android.server.input; +package com.android.server.input.debug; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyFloat; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -31,11 +32,12 @@ import android.view.InputDevice; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; -import android.view.ViewConfiguration; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.server.input.InputManagerService; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,76 +50,50 @@ import org.junit.runner.RunWith; public class FocusEventDebugViewTest { private FocusEventDebugView mFocusEventDebugView; - private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView; - private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView; - private float mScaledVerticalScrollFactor; + private RotaryInputValueView mRotaryInputValueView; + private RotaryInputGraphView mRotaryInputGraphView; @Before public void setUp() throws Exception { Context context = InstrumentationRegistry.getContext(); - mScaledVerticalScrollFactor = - ViewConfiguration.get(context).getScaledVerticalScrollFactor(); InputManagerService mockService = mock(InputManagerService.class); when(mockService.monitorInput(anyString(), anyInt())) .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]); - mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context); - mRotaryInputGraphView = new FocusEventDebugView.RotaryInputGraphView(context); + mRotaryInputValueView = spy(new RotaryInputValueView(context)); + mRotaryInputGraphView = spy(new RotaryInputGraphView(context)); mFocusEventDebugView = new FocusEventDebugView(context, mockService, () -> mRotaryInputValueView, () -> mRotaryInputGraphView); } @Test - public void startsRotaryInputValueViewWithDefaultValue() { - assertEquals("+0.0", mRotaryInputValueView.getText()); - } - - @Test - public void startsRotaryInputGraphViewWithDefaultFrameCenter() { - assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01); - } - - @Test - public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() { - mFocusEventDebugView.handleUpdateShowRotaryInput(true); - - mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f)); - - assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor), - mRotaryInputValueView.getText()); - } - - @Test - public void handleRotaryInput_translatesRotaryInputGraphViewWithHighScrollValue() { + public void handleRotaryInput_sendsMotionEventWhenEnabled() { mFocusEventDebugView.handleUpdateShowRotaryInput(true); - mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f)); + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L)); - assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0); + verify(mRotaryInputGraphView).addValue(0.5f, 10L); + verify(mRotaryInputValueView).updateValue(0.5f); } @Test - public void updateActivityStatus_setsAndRemovesColorFilter() { - // It should not be active initially. - assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + public void handleRotaryInput_doesNotSendMotionEventWhenDisabled() { + mFocusEventDebugView.handleUpdateShowRotaryInput(false); - mRotaryInputValueView.updateActivityStatus(true); - // It should be active after rotary input. - assertNotNull(mRotaryInputValueView.getBackground().getColorFilter()); + mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L)); - mRotaryInputValueView.updateActivityStatus(false); - // It should not be active after waiting for mUpdateActivityStatusCallback. - assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + verify(mRotaryInputGraphView, never()).addValue(anyFloat(), anyLong()); + verify(mRotaryInputValueView, never()).updateValue(anyFloat()); } - private MotionEvent createRotaryMotionEvent(float scrollAxisValue) { + private MotionEvent createRotaryMotionEvent(float scrollAxisValue, long eventTime) { PointerCoords pointerCoords = new PointerCoords(); pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue); PointerProperties pointerProperties = new PointerProperties(); return MotionEvent.obtain( /* downTime */ 0, - /* eventTime */ 0, + /* eventTime */ eventTime, /* action */ MotionEvent.ACTION_SCROLL, /* pointerCount */ 1, /* pointerProperties */ new PointerProperties[] {pointerProperties}, diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java new file mode 100644 index 000000000000..af6ece414fd1 --- /dev/null +++ b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input.debug; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Build/Install/Run: + * atest RotaryInputGraphViewTest + */ +@RunWith(AndroidJUnit4.class) +public class RotaryInputGraphViewTest { + + private RotaryInputGraphView mRotaryInputGraphView; + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getContext(); + + mRotaryInputGraphView = new RotaryInputGraphView(context); + } + + @Test + public void startsWithDefaultFrameCenter() { + assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01); + } + + @Test + public void addValue_translatesRotaryInputGraphViewWithHighScrollValue() { + final float scrollAxisValue = 1000f; + final long eventTime = 0; + + mRotaryInputGraphView.addValue(scrollAxisValue, eventTime); + + assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0); + } +} diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java new file mode 100644 index 000000000000..e5e3852dc318 --- /dev/null +++ b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input.debug; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.content.Context; +import android.view.ViewConfiguration; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Locale; + +/** + * Build/Install/Run: + * atest RotaryInputValueViewTest + */ +@RunWith(AndroidJUnit4.class) +public class RotaryInputValueViewTest { + + private final Locale mDefaultLocale = Locale.getDefault(); + + private RotaryInputValueView mRotaryInputValueView; + private float mScaledVerticalScrollFactor; + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getContext(); + mScaledVerticalScrollFactor = + ViewConfiguration.get(context).getScaledVerticalScrollFactor(); + + mRotaryInputValueView = new RotaryInputValueView(context); + } + + @Test + public void startsWithDefaultValue() { + assertEquals("+0.0", mRotaryInputValueView.getText().toString()); + } + + @Test + public void updateValue_updatesTextWithScrollValue() { + final float scrollAxisValue = 1000f; + final String expectedText = String.format(mDefaultLocale, "+%.1f", + scrollAxisValue * mScaledVerticalScrollFactor); + + mRotaryInputValueView.updateValue(scrollAxisValue); + + assertEquals(expectedText, mRotaryInputValueView.getText().toString()); + } + + @Test + public void updateActivityStatus_setsAndRemovesColorFilter() { + // It should not be active initially. + assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + + mRotaryInputValueView.updateActivityStatus(true); + // It should be active after rotary input. + assertNotNull(mRotaryInputValueView.getBackground().getColorFilter()); + + mRotaryInputValueView.updateActivityStatus(false); + // It should not be active after waiting for mUpdateActivityStatusCallback. + assertNull(mRotaryInputValueView.getBackground().getColorFilter()); + } +} diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp index 84845c69fb27..58ceb3f3edf4 100644 --- a/tests/InputMethodStressTest/Android.bp +++ b/tests/InputMethodStressTest/Android.bp @@ -26,7 +26,7 @@ android_test { "compatibility-device-util-axt", "platform-test-annotations", "platform-test-rules", - "truth-prebuilt", + "truth", ], test_suites: [ "general-tests", diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp index eee486f99748..15aaa463cce7 100644 --- a/tests/InputScreenshotTest/Android.bp +++ b/tests/InputScreenshotTest/Android.bp @@ -29,7 +29,7 @@ android_test { "androidx.lifecycle_lifecycle-livedata-ktx", "androidx.lifecycle_lifecycle-runtime-compose", "androidx.navigation_navigation-compose", - "truth-prebuilt", + "truth", "androidx.compose.runtime_runtime", "androidx.test.core", "androidx.test.ext.junit", @@ -47,7 +47,7 @@ android_test { "services.core.unboosted", "testables", "testng", - "truth-prebuilt", + "truth", ], libs: [ "android.test.mock", diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp index ef45864dd93b..ddec8fa1d70a 100644 --- a/tests/Internal/Android.bp +++ b/tests/Internal/Android.bp @@ -19,7 +19,7 @@ android_test { "junit", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", "platform-test-annotations", ], java_resource_dirs: ["res"], diff --git a/tests/LocalizationTest/Android.bp b/tests/LocalizationTest/Android.bp index 4e0b0a89d972..909ca5972552 100644 --- a/tests/LocalizationTest/Android.bp +++ b/tests/LocalizationTest/Android.bp @@ -34,7 +34,7 @@ android_test { "androidx.test.ext.junit", "androidx.test.rules", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", ], jni_libs: [ // For mockito extended diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp index 254770d21818..fcacab3fb13c 100644 --- a/tests/MidiTests/Android.bp +++ b/tests/MidiTests/Android.bp @@ -31,7 +31,7 @@ android_test { "mockito-target-inline-minus-junit4", "platform-test-annotations", "services.midi", - "truth-prebuilt", + "truth", ], jni_libs: ["libdexmakerjvmtiagent"], certificate: "platform", diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp index 1e1dc8458560..e0e6c4c43b16 100644 --- a/tests/PackageWatchdog/Android.bp +++ b/tests/PackageWatchdog/Android.bp @@ -32,7 +32,7 @@ android_test { "androidx.test.rules", "services.core", "services.net", - "truth-prebuilt", + "truth", ], libs: ["android.test.runner"], jni_libs: [ diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp index f0f9c4bdd721..fd992cf415cf 100644 --- a/tests/PlatformCompatGating/Android.bp +++ b/tests/PlatformCompatGating/Android.bp @@ -38,7 +38,7 @@ android_test { "androidx.test.ext.junit", "mockito-target-minus-junit4", "testng", - "truth-prebuilt", + "truth", "platform-compat-test-rules", ], } diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp index 5f91f9d0e505..f6a41c2f44b7 100644 --- a/tests/PlatformCompatGating/test-rules/Android.bp +++ b/tests/PlatformCompatGating/test-rules/Android.bp @@ -29,7 +29,7 @@ java_library { static_libs: [ "junit", "androidx.test.core", - "truth-prebuilt", - "core-compat-test-rules" + "truth", + "core-compat-test-rules", ], } diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp index 38313f85c31d..055d6258d1ac 100644 --- a/tests/SurfaceViewBufferTests/Android.bp +++ b/tests/SurfaceViewBufferTests/Android.bp @@ -45,7 +45,7 @@ android_test { "kotlinx-coroutines-android", "flickerlib", "flickerlib-trace_processor_shell", - "truth-prebuilt", + "truth", "cts-wm-util", "CtsSurfaceValidatorLib", ], diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp index bf12f423f145..d2ade34148e2 100644 --- a/tests/TaskOrganizerTest/Android.bp +++ b/tests/TaskOrganizerTest/Android.bp @@ -43,6 +43,6 @@ android_test { "kotlinx-coroutines-android", "flickerlib", "flickerlib-trace_processor_shell", - "truth-prebuilt", + "truth", ], } diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp index 81ec265c2c29..b968e5d81148 100644 --- a/tests/TelephonyCommonTests/Android.bp +++ b/tests/TelephonyCommonTests/Android.bp @@ -32,11 +32,11 @@ android_test { static_libs: [ "mockito-target-extended", "androidx.test.rules", - "truth-prebuilt", + "truth", "platform-test-annotations", "androidx.core_core", "androidx.fragment_fragment", - "androidx.test.ext.junit" + "androidx.test.ext.junit", ], jni_libs: ["libdexmakerjvmtiagent"], diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp index c216bced81f0..4e75a1d02a41 100644 --- a/tests/TrustTests/Android.bp +++ b/tests/TrustTests/Android.bp @@ -28,7 +28,7 @@ android_test { "flag-junit", "mockito-target-minus-junit4", "servicestests-utils", - "truth-prebuilt", + "truth", ], libs: [ "android.test.runner", diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp index 9bfcc18ee301..ddb3649a8320 100644 --- a/tests/UpdatableSystemFontTest/Android.bp +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -30,7 +30,7 @@ android_test { "androidx.test.uiautomator_uiautomator", "compatibility-device-util-axt", "platform-test-annotations", - "truth-prebuilt", + "truth", ], test_suites: [ "general-tests", diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp index 97fbf5b32035..c02d8e96abb0 100644 --- a/tests/UsbManagerTests/Android.bp +++ b/tests/UsbManagerTests/Android.bp @@ -31,7 +31,7 @@ android_test { "androidx.test.rules", "mockito-target-inline-minus-junit4", "platform-test-annotations", - "truth-prebuilt", + "truth", "UsbManagerTestLib", ], jni_libs: ["libdexmakerjvmtiagent"], diff --git a/tests/UsbManagerTests/lib/Android.bp b/tests/UsbManagerTests/lib/Android.bp index 994484cd63bf..4e5a70fef0ca 100644 --- a/tests/UsbManagerTests/lib/Android.bp +++ b/tests/UsbManagerTests/lib/Android.bp @@ -34,7 +34,7 @@ android_library { "services.core", "services.net", "services.usb", - "truth-prebuilt", + "truth", "androidx.core_core", ], libs: [ diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp index c60c519ec4d4..92c271165ad7 100644 --- a/tests/UsbTests/Android.bp +++ b/tests/UsbTests/Android.bp @@ -34,7 +34,7 @@ android_test { "services.core", "services.net", "services.usb", - "truth-prebuilt", + "truth", "UsbManagerTestLib", ], jni_libs: [ diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp index 01d34e4ff645..39037f22fdcb 100644 --- a/tests/componentalias/Android.bp +++ b/tests/componentalias/Android.bp @@ -25,7 +25,7 @@ java_defaults { "androidx.test.rules", "compatibility-device-util-axt", "mockito-target-extended-minus-junit4", - "truth-prebuilt", + "truth", ], libs: ["android.test.base"], srcs: [ diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 7323b0f4c14f..977b2768e702 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -220,6 +220,7 @@ genrule { name: "aapt2-protos", tools: [":soong_zip"], srcs: [ + "ApkInfo.proto", "Configuration.proto", "ResourcesInternal.proto", "ResourceMetadata.proto", diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp index 87da09a7b054..8c644cf83339 100644 --- a/tools/aapt2/java/AnnotationProcessor.cpp +++ b/tools/aapt2/java/AnnotationProcessor.cpp @@ -49,16 +49,19 @@ struct AnnotationRule { kDeprecated = 0x01, kSystemApi = 0x02, kTestApi = 0x04, + kFlaggedApi = 0x08, }; StringPiece doc_str; uint32_t bit_mask; StringPiece annotation; + bool preserve_params; }; -static std::array<AnnotationRule, 2> sAnnotationRules = {{ - {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi"}, - {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi"}, +static std::array<AnnotationRule, 3> sAnnotationRules = {{ + {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi", true}, + {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi", false}, + {"@FlaggedApi", AnnotationRule::kFlaggedApi, "@android.annotation.FlaggedApi", true}, }}; void AnnotationProcessor::AppendCommentLine(std::string comment) { @@ -73,12 +76,11 @@ void AnnotationProcessor::AppendCommentLine(std::string comment) { std::string::size_type idx = comment.find(rule.doc_str.data()); if (idx != std::string::npos) { // Captures all parameters associated with the specified annotation rule - // by matching the first pair of parantheses after the rule. - std::regex re(std::string(rule.doc_str) += "\\s*\\((.+)\\)"); + // by matching the first pair of parentheses after the rule. + std::regex re(std::string(rule.doc_str).append(R"(\s*\((.+)\))")); std::smatch match_result; const bool is_match = std::regex_search(comment, match_result, re); - // We currently only capture and preserve parameters for SystemApi. - if (is_match && rule.bit_mask == AnnotationRule::kSystemApi) { + if (is_match && rule.preserve_params) { annotation_parameter_map_[rule.bit_mask] = match_result[1].str(); comment.erase(comment.begin() + match_result.position(), comment.begin() + match_result.position() + match_result.length()); diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp index 6bc8902a6dcf..e98e96ba3bc3 100644 --- a/tools/aapt2/java/AnnotationProcessor_test.cpp +++ b/tools/aapt2/java/AnnotationProcessor_test.cpp @@ -76,6 +76,36 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationParamsAndRemovesFromCommen EXPECT_THAT(annotations, HasSubstr("This is a system API")); } +TEST(AnnotationProcessorTest, EmitsFlaggedApiAnnotationAndRemovesFromComment) { + AnnotationProcessor processor; + processor.AppendComment("@FlaggedApi This is a flagged API"); + + std::string annotations; + StringOutputStream out(&annotations); + Printer printer(&out); + processor.Print(&printer); + out.Flush(); + + EXPECT_THAT(annotations, HasSubstr("@android.annotation.FlaggedApi")); + EXPECT_THAT(annotations, Not(HasSubstr("@FlaggedApi"))); + EXPECT_THAT(annotations, HasSubstr("This is a flagged API")); +} + +TEST(AnnotationProcessorTest, EmitsFlaggedApiAnnotationParamsAndRemovesFromComment) { + AnnotationProcessor processor; + processor.AppendComment("@FlaggedApi (\"android.flags.my_flag\") This is a flagged API"); + + std::string annotations; + StringOutputStream out(&annotations); + Printer printer(&out); + processor.Print(&printer); + out.Flush(); + + EXPECT_THAT(annotations, HasSubstr("@android.annotation.FlaggedApi(\"android.flags.my_flag\")")); + EXPECT_THAT(annotations, Not(HasSubstr("@FlaggedApi"))); + EXPECT_THAT(annotations, HasSubstr("This is a flagged API")); +} + TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) { AnnotationProcessor processor; processor.AppendComment("@TestApi This is a test API"); diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp index e7fb2debfc4e..b71e5c47c70e 100644 --- a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp +++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp @@ -24,7 +24,7 @@ java_library_host { ], static_libs: [ "junit", - "truth-prebuilt", + "truth", "mockito", // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp index 05d6a43cdb0f..f9dc305a4e3e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp @@ -104,7 +104,7 @@ java_library_host { ], static_libs: [ "junit", - "truth-prebuilt", + "truth", // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ "platform-test-annotations", diff --git a/tools/processors/immutability/Android.bp b/tools/processors/immutability/Android.bp index a7d69039fcb0..ecc283b0b37e 100644 --- a/tools/processors/immutability/Android.bp +++ b/tools/processors/immutability/Android.bp @@ -50,7 +50,7 @@ java_test_host { static_libs: [ "compile-testing-prebuilt", - "truth-prebuilt", + "truth", "junit", "kotlin-reflect", "ImmutabilityAnnotationProcessorHostLibrary", diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp index 7059c52ddc76..9c755b7d58c2 100644 --- a/tools/processors/intdef_mappings/Android.bp +++ b/tools/processors/intdef_mappings/Android.bp @@ -38,7 +38,7 @@ java_test_host { static_libs: [ "compile-testing-prebuilt", - "truth-prebuilt", + "truth", "junit", "guava", "libintdef-annotation-processor", diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 7a299694741a..5a0f742372d7 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -40,7 +40,7 @@ android_test { "frameworks-base-testutils", "guava", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], libs: [ |