diff options
332 files changed, 6074 insertions, 1167 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/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/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 871312640acf..762d9d1c3da1 100644 --- a/api/javadoc-lint-baseline +++ b/api/javadoc-lint-baseline @@ -291,76 +291,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/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/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/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/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index d8e60c8b6464..88f62f327fdd 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -19,4 +19,11 @@ flag { name: "haptics_customization_ringtone_v2_enabled" description: "Enables the usage of the new RingtoneV2 class" bug: "241918098" -}
\ No newline at end of file +} + +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/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/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/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/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 4360c5ae3dc0..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) 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/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/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/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/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/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_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/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/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 3c5785275919..e40fcb2a633b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -466,7 +466,7 @@ android_library { "hamcrest-library", "androidx.test.rules", "testables", - "truth-prebuilt", + "truth", "monet", "libmonet", "dagger2", @@ -583,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/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/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/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/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/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 6359ce60ae70..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 @@ -181,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/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-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/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/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 bb0cf6d470a2..eb7a7358cbf1 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -34,7 +34,6 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.customization.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.dagger.qualifiers.DisplaySpecific import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags.DOZING_MIGRATION_1 @@ -80,8 +79,8 @@ constructor( private val batteryController: BatteryController, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val configurationController: ConfigurationController, - @DisplaySpecific private val resources: Resources, - @DisplaySpecific private val context: Context, + @Main private val resources: Resources, + private val context: Context, @Main private val mainExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?, @@ -212,13 +211,13 @@ constructor( 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 @@ -226,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( @@ -261,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() { 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/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index a7b35ef3aba4..0d3f726b011b 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -62,6 +62,7 @@ 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; @@ -74,12 +75,15 @@ 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; @@ -128,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; @@ -204,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; @@ -233,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 = @@ -715,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."); @@ -732,7 +743,11 @@ public class LockIconViewController implements Dumpable { // 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); + } } 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/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/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/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/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/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/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/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/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 733d774f8b80..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 @@ -53,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; @@ -95,6 +96,7 @@ import javax.inject.Provider; CoordinatorsModule.class, KeyguardNotificationVisibilityProviderModule.class, ShadeEventsModule.class, + NotificationDataLayerModule.class, NotifPipelineChoreographerModule.class, NotificationSectionHeadersModule.class, NotificationListViewModelModule.class, 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 a77e67b6908d..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 @@ -119,7 +113,6 @@ constructor( private var aodBindJob: DisposableHandle? = null private var aodIconAppearTranslation = 0 private var aodIconTint = 0 - private var aodIconsVisible = false private var showLowPriority = true @VisibleForTesting @@ -132,7 +125,6 @@ constructor( } init { - statusBarStateController.addCallback(this) wakeUpCoordinator.addListener(this) demoModeController.addCallback(this) notificationListener.addNotificationSettingsListener(settingsListener) @@ -160,8 +152,11 @@ constructor( NotificationIconContainerViewBinder.bind( aodIcons, aodIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, ) - updateAodIconsVisibility(animate = false, forceUpdate = changed) if (changed) { updateAodNotificationIcons() } @@ -176,6 +171,10 @@ constructor( NotificationIconContainerViewBinder.bind( icons, shelfIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, ) shelfIcons = icons } @@ -249,20 +248,8 @@ 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) = unsupported - override fun onStateChanged(newState: Int) { - updateAodIconsVisibility(animate = false, forceUpdate = false) - } - override fun onThemeChanged() { reloadAodColor() updateAodIconColors() @@ -272,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) @@ -352,6 +297,10 @@ constructor( NotificationIconContainerViewBinder.bind( notificationIcons!!, statusBarIconsViewModel, + configurationController, + dozeParameters, + featureFlags, + screenOffAnimationController, ) } @@ -602,17 +551,6 @@ constructor( v.setDecorColor(tint) } - private fun animateInAodIconTranslation() { - if (!statusViewMigrated) { - aodIcons!! - .animate() - .setInterpolator(Interpolators.DECELERATE_QUINT) - .translationY(0f) - .setDuration(AOD_ICONS_APPEAR_DURATION) - .start() - } - } - private fun reloadAodColor() { aodIconTint = Utils.getColorAttrDefaultColor( @@ -635,69 +573,7 @@ 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 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 f8ff3e393033..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,12 +15,27 @@ */ 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]. */ @@ -28,11 +43,144 @@ object NotificationIconContainerViewBinder { fun bind( view: NotificationIconContainer, viewModel: NotificationIconContainerViewModel, + configurationController: ConfigurationController, + dozeParameters: DozeParameters, + featureFlags: FeatureFlagsClassic, + screenOffAnimationController: ScreenOffAnimationController, ): DisposableHandle { 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 90507fc82a79..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,19 +15,48 @@ */ 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. */ +@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, @@ -35,4 +64,97 @@ constructor( ) { 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 49f262de8f4a..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,12 +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 { 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 ee57b780b8c3..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 @@ -17,9 +17,11 @@ 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 @@ -35,4 +37,9 @@ constructor( ) { 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 6f8ce4b1db4b..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,6 +15,7 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.util.ui.AnimatedValue import kotlinx.coroutines.flow.Flow /** @@ -24,4 +25,22 @@ import kotlinx.coroutines.flow.Flow 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/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 dc318a392ed5..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,6 +44,7 @@ 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.dagger.SysUISingleton; @@ -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; @@ -152,6 +154,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor, NotificationLockscreenUserManager notificationLockscreenUserManager, StatusBarStateController statusBarStateController, + IStatusBarService statusBarService, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, HeadsUpManager headsUpManager, @@ -177,6 +180,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor; mLockscreenUserManager = notificationLockscreenUserManager; mStatusBarStateController = statusBarStateController; + mStatusBarService = statusBarService; mDeviceProvisionedController = deviceProvisionedController; mMetricsLogger = metricsLogger; mHeadsUpManager = headsUpManager; @@ -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); } /** 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/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/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 c1be44ab8a85..167e3417c162 100644 --- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt +++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt @@ -81,7 +81,7 @@ data class TestMocksModule( @get:Provides val notificationStackSizeCalculator: NotificationStackSizeCalculator = mock(), @get:Provides val notificationWakeUpCoordinator: NotificationWakeUpCoordinator = mock(), @get:Provides val screenLifecycle: ScreenLifecycle = mock(), - @get:Provides val screenOffAnimController: ScreenOffAnimationController = 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(), 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 ce1930a6e80f..d61ca697642a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -42,6 +42,7 @@ import android.view.accessibility.AccessibilityManager; 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 +52,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac 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/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt index 679dfefae7cd..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 @@ -12,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 @@ -22,6 +23,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @@ -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 c4cd8a4de8ab..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 @@ -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/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/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/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/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 index 99c3b194a662..31efebbc5b60 100644 --- 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 @@ -25,6 +25,7 @@ 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 @@ -37,10 +38,13 @@ 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 @@ -59,19 +63,24 @@ import org.mockito.MockitoAnnotations class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { @Mock private lateinit var dozeParams: DozeParameters + @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController private lateinit var testComponent: TestComponent - private val underTest + private val underTest: NotificationIconContainerAlwaysOnDisplayViewModel get() = testComponent.underTest - private val deviceProvisioningRepository + private val deviceEntryRepository: FakeDeviceEntryRepository + get() = testComponent.deviceEntryRepository + private val deviceProvisioningRepository: FakeDeviceProvisioningRepository get() = testComponent.deviceProvisioningRepository - private val keyguardRepository + private val keyguardRepository: FakeKeyguardRepository get() = testComponent.keyguardRepository - private val keyguardTransitionRepository + private val keyguardTransitionRepository: FakeKeyguardTransitionRepository get() = testComponent.keyguardTransitionRepository - private val powerRepository + private val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository + get() = testComponent.notifsKeyguardRepository + private val powerRepository: FakePowerRepository get() = testComponent.powerRepository - private val scope + private val scope: TestScope get() = testComponent.scope @Before @@ -84,12 +93,14 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { test = this, featureFlags = FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, value = false) + setDefault(Flags.FACE_AUTH_REFACTOR) set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) + setDefault(Flags.NEW_AOD_TRANSITION) }, mocks = TestMocksModule( dozeParameters = dozeParams, + screenOffAnimationController = screenOffAnimController, ), ) @@ -251,6 +262,204 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { 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 = @@ -264,9 +473,11 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { val underTest: NotificationIconContainerAlwaysOnDisplayViewModel + val deviceEntryRepository: FakeDeviceEntryRepository val deviceProvisioningRepository: FakeDeviceProvisioningRepository val keyguardRepository: FakeKeyguardRepository val keyguardTransitionRepository: FakeKeyguardTransitionRepository + val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository val powerRepository: FakePowerRepository val scope: TestScope 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 index d1518f7e0d08..e1e7f92265f0 100644 --- 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 @@ -82,6 +82,7 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { DaggerNotificationIconContainerStatusBarViewModelTest_TestComponent.factory() .create( test = this, + // Configurable bindings featureFlags = FakeFeatureFlagsClassicModule { set(Flags.FACE_AUTH_REFACTOR, value = false) @@ -248,6 +249,7 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { modules = [ SysUITestModule::class, + // Real impls BiometricsDomainLayerModule::class, UserDomainLayerModule::class, ] 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 1ab36b805ca6..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 @@ -195,6 +195,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mWindowRootViewVisibilityInteractor, mNotificationLockscreenUserManager, mStatusBarStateController, + mBarService, mDeviceProvisionedController, mMetricsLogger, mHeadsUpManager, 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/utils/src/com/android/systemui/FakeSystemUiModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt index 813197b171ad..dc5fd953239d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt @@ -24,6 +24,7 @@ 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( @@ -36,6 +37,7 @@ import dagger.Module 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/data/FakeSystemUiDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt index 29fb52aabd39..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,6 +15,7 @@ */ 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 @@ -29,6 +30,7 @@ import dagger.Module @Module( includes = [ + FakeAuthenticationDataLayerModule::class, FakeBouncerDataLayerModule::class, FakeCommonDataLayerModule::class, FakeDeviceEntryDataLayerModule::class, 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 26d95c0a0ea3..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,3 +1,18 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.systemui.deviceentry.data.repository import com.android.systemui.dagger.SysUISingleton @@ -13,15 +28,13 @@ import kotlinx.coroutines.flow.asStateFlow 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 } @@ -35,7 +48,7 @@ class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { } fun setBypassEnabled(isBypassEnabled: Boolean) { - this.isBypassEnabled = isBypassEnabled + _isBypassEnabled.value = isBypassEnabled } } 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 822edfc7f6cd..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,6 +16,7 @@ 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 @@ -24,6 +25,7 @@ import dagger.Module includes = [ FakeStatusBarDisableFlagsDataLayerModule::class, + FakeStatusBarNotificationsDataLayerModule::class, FakeStatusBarPipelineDataLayerModule::class, FakeStatusBarPolicyDataLayerModule::class, ] 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/util/concurrency/FakeExecutorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt index 5de05c27ba2e..1f48d940f91c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.util.concurrency +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.util.time.FakeSystemClock import dagger.Binds @@ -22,14 +23,12 @@ import dagger.Module import dagger.Provides import java.util.concurrent.Executor -@Module(includes = [FakeExecutorModule.Bindings::class]) -class FakeExecutorModule( - @get:Provides val clock: FakeSystemClock = FakeSystemClock(), -) { - @get:Provides val executor = FakeExecutor(clock) +@Module +interface FakeExecutorModule { + @Binds @Main @SysUISingleton fun bindMainExecutor(executor: FakeExecutor): Executor - @Module - interface Bindings { - @Binds @Main fun bindMainExecutor(executor: FakeExecutor): Executor + companion object { + @Provides + fun provideFake(clock: FakeSystemClock) = FakeExecutor(clock) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt new file mode 100644 index 000000000000..3e3d7cbb40b5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.time + +import com.android.systemui.dagger.SysUISingleton +import dagger.Binds +import dagger.Module +import dagger.Provides + +@Module +interface FakeSystemClockModule { + @Binds fun bindFake(fake: FakeSystemClock): SystemClock + + companion object { + @Provides @SysUISingleton fun providesFake() = FakeSystemClock() + } +} diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp index 155dc1a68295..18f783146c72 100644 --- a/packages/WallpaperBackup/Android.bp +++ b/packages/WallpaperBackup/Android.bp @@ -49,7 +49,7 @@ android_test { "androidx.test.core", "androidx.test.rules", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], resource_dirs: ["test/res"], certificate: "platform", diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp index b781602399a3..0244c0fe0533 100644 --- a/packages/overlays/tests/Android.bp +++ b/packages/overlays/tests/Android.bp @@ -34,7 +34,7 @@ android_test { "androidx.test.rules", "androidx.test.espresso.core", "mockito-target-minus-junit4", - "truth-prebuilt", + "truth", ], dxflags: ["--multi-dex"], } diff --git a/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/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 816043e65544..1ba1f55af71b 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -137,9 +137,11 @@ public class SettingsToPropertiesMapper { "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/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/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/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/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/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/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/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/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 c3977d6918e3..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 diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 44d4c4516842..074b4044fdaa 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8876,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; @@ -8900,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(); 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/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/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/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 0813bb09fd52..063af573e1f3 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -68,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/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/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 71f7f577d038..2ece8c74420c 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -61,7 +61,7 @@ android_test { "mockito-target-minus-junit4", "platform-test-annotations", "ShortcutManagerTestUtils", - "truth-prebuilt", + "truth", "testables", "androidx.test.uiautomator_uiautomator", "platformprotosnano", @@ -72,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/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/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/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 9956faaa2eb1..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()); 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/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/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/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: [ |