Merge "Revert^2 "Prevent notif flicker during entirety of swipe to unlock"" into main
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index 788e824..2c1a853 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -9,6 +9,7 @@
flag {
name: "job_debug_info_apis"
+ is_exported: true
namespace: "backstage_power"
description: "Add APIs to let apps attach debug information to jobs"
bug: "293491637"
@@ -16,6 +17,7 @@
flag {
name: "backup_jobs_exemption"
+ is_exported: true
namespace: "backstage_power"
description: "Introduce a new RUN_BACKUP_JOBS permission and exemption logic allowing for longer running jobs for apps whose primary purpose is to backup or sync content."
bug: "318731461"
diff --git a/core/api/current.txt b/core/api/current.txt
index 8a61f4a..93c34cd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10731,6 +10731,7 @@
field public static final String DROPBOX_SERVICE = "dropbox";
field public static final String EUICC_SERVICE = "euicc";
field public static final String FILE_INTEGRITY_SERVICE = "file_integrity";
+ field public static final String FINGERPRINT_SERVICE = "fingerprint";
field public static final String GAME_SERVICE = "game";
field public static final String GRAMMATICAL_INFLECTION_SERVICE = "grammatical_inflection";
field public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
@@ -20387,6 +20388,54 @@
}
+package android.hardware.fingerprint {
+
+ @Deprecated public class FingerprintManager {
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.USE_BIOMETRIC, android.Manifest.permission.USE_FINGERPRINT}) public void authenticate(@Nullable android.hardware.fingerprint.FingerprintManager.CryptoObject, @Nullable android.os.CancellationSignal, int, @NonNull android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, @Nullable android.os.Handler);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+ field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+ field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
+ field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+ field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
+ field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
+ field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
+ field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
+ field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
+ field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
+ field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+ field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
+ field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
+ }
+
+ @Deprecated public abstract static class FingerprintManager.AuthenticationCallback {
+ ctor @Deprecated public FingerprintManager.AuthenticationCallback();
+ method @Deprecated public void onAuthenticationError(int, CharSequence);
+ method @Deprecated public void onAuthenticationFailed();
+ method @Deprecated public void onAuthenticationHelp(int, CharSequence);
+ method @Deprecated public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintManager.AuthenticationResult);
+ }
+
+ @Deprecated public static class FingerprintManager.AuthenticationResult {
+ method @Deprecated public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
+ }
+
+ @Deprecated public static final class FingerprintManager.CryptoObject {
+ ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull java.security.Signature);
+ ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher);
+ ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac);
+ method @Deprecated public javax.crypto.Cipher getCipher();
+ method @Deprecated public javax.crypto.Mac getMac();
+ method @Deprecated public java.security.Signature getSignature();
+ }
+
+}
+
package android.hardware.input {
public final class HostUsiVersion implements android.os.Parcelable {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index c61f163..3c7c0d6 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -35,7 +35,6 @@
method @Deprecated @Nullable public String getFeatureId();
method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
method public abstract java.io.File getSharedPreferencesPath(String);
- field public static final String FINGERPRINT_SERVICE = "fingerprint";
}
public class ContextWrapper extends android.content.Context {
@@ -146,54 +145,6 @@
}
-package android.hardware.fingerprint {
-
- @Deprecated public class FingerprintManager {
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.USE_BIOMETRIC, android.Manifest.permission.USE_FINGERPRINT}) public void authenticate(@Nullable android.hardware.fingerprint.FingerprintManager.CryptoObject, @Nullable android.os.CancellationSignal, int, @NonNull android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, @Nullable android.os.Handler);
- method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
- method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
- field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
- field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
- field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
- field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
- field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
- field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
- field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
- field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
- field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
- field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
- field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
- field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
- field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
- field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
- field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
- field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
- field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
- }
-
- @Deprecated public abstract static class FingerprintManager.AuthenticationCallback {
- ctor public FingerprintManager.AuthenticationCallback();
- method public void onAuthenticationError(int, CharSequence);
- method public void onAuthenticationFailed();
- method public void onAuthenticationHelp(int, CharSequence);
- method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintManager.AuthenticationResult);
- }
-
- @Deprecated public static class FingerprintManager.AuthenticationResult {
- method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
- }
-
- @Deprecated public static final class FingerprintManager.CryptoObject {
- ctor public FingerprintManager.CryptoObject(@NonNull java.security.Signature);
- ctor public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher);
- ctor public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac);
- method public javax.crypto.Cipher getCipher();
- method public javax.crypto.Mac getMac();
- method public java.security.Signature getSignature();
- }
-
-}
-
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a2b847e..a76aa67 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1721,6 +1721,15 @@
}
+package android.hardware.fingerprint {
+
+ @Deprecated public class FingerprintManager {
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
+ }
+
+}
+
package android.hardware.hdmi {
public final class HdmiControlServiceWrapper {
diff --git a/core/api/test-removed.txt b/core/api/test-removed.txt
index 2e44176..d802177 100644
--- a/core/api/test-removed.txt
+++ b/core/api/test-removed.txt
@@ -1,10 +1 @@
// Signature format: 2.0
-package android.hardware.fingerprint {
-
- @Deprecated public class FingerprintManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
- method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
- }
-
-}
-
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 350b1ed..b9aa18c 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -3,6 +3,7 @@
flag {
namespace: "system_performance"
name: "app_start_info"
+ is_exported: true
description: "Control collecting of ApplicationStartInfo records and APIs."
bug: "247814855"
}
@@ -10,6 +11,7 @@
flag {
namespace: "backstage_power"
name: "get_binding_uid_importance"
+ is_exported: true
description: "API to get importance of UID that's binding to the caller"
bug: "292533010"
}
@@ -17,6 +19,7 @@
flag {
namespace: "backstage_power"
name: "app_restrictions_api"
+ is_exported: true
description: "API to track and query restrictions applied to apps"
bug: "320150834"
}
@@ -24,6 +27,7 @@
flag {
namespace: "backstage_power"
name: "uid_importance_listener_for_uids"
+ is_exported: true
description: "API to add OnUidImportanceListener with targetted UIDs"
bug: "286258140"
}
@@ -31,12 +35,14 @@
flag {
namespace: "backstage_power"
name: "introduce_new_service_ontimeout_callback"
+ is_exported: true
description: "Add a new callback in Service to indicate a FGS has reached its timeout."
bug: "317799821"
}
flag {
name: "bcast_event_timestamps"
+ is_exported: true
namespace: "backstage_power"
description: "Add APIs for clients to provide broadcast event trigger timestamps"
bug: "325136414"
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 3ec6fe7..4fa45be 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -5,6 +5,7 @@
flag {
name: "policy_engine_migration_v2_enabled"
+ is_exported: true
namespace: "enterprise"
description: "V2 of the policy engine migrations for Android V"
bug: "289520697"
@@ -12,6 +13,7 @@
flag {
name: "device_policy_size_tracking_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Add feature to track the total policy size and have a max threshold - public API changes"
bug: "281543351"
@@ -26,6 +28,7 @@
flag {
name: "onboarding_bugreport_v2_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Add feature to track required changes for enabled V2 of auto-capturing of onboarding bug reports."
bug: "302517677"
@@ -47,6 +50,7 @@
flag {
name: "dedicated_device_control_api_enabled"
+ is_exported: true
namespace: "enterprise"
description: "(API) Allow the device management role holder to control which platform features are available on dedicated devices."
bug: "281964214"
@@ -54,6 +58,7 @@
flag {
name: "permission_migration_for_zero_trust_api_enabled"
+ is_exported: true
namespace: "enterprise"
description: "(API) Migrate existing APIs to permission based, and enable DMRH to call them to collect Zero Trust signals."
bug: "289520697"
@@ -68,6 +73,7 @@
flag {
name: "device_theft_api_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Add new API for theft detection."
bug: "325073410"
@@ -89,6 +95,7 @@
flag {
name: "security_log_v2_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Improve access to security logging in the context of Zero Trust."
bug: "295324350"
@@ -103,6 +110,7 @@
flag {
name: "allow_querying_profile_type"
+ is_exported: true
namespace: "enterprise"
description: "Public APIs to query if a user is a profile and what kind of profile type it is."
bug: "323001115"
@@ -117,6 +125,7 @@
flag {
name: "assist_content_user_restriction_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Prevent work data leakage by sending assist content to privileged apps."
bug: "322975406"
@@ -134,6 +143,7 @@
flag {
name: "backup_service_security_log_event_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Emit a security log event when DPM.setBackupServiceEnabled is called"
bug: "304999634"
@@ -141,6 +151,7 @@
flag {
name: "esim_management_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Enable APIs to provision and manage eSIMs"
bug: "295301164"
@@ -148,6 +159,7 @@
flag {
name: "headless_device_owner_single_user_enabled"
+ is_exported: true
namespace: "enterprise"
description: "Add Headless DO support."
bug: "289515470"
@@ -155,6 +167,7 @@
flag {
name: "is_mte_policy_enforced"
+ is_exported: true
namespace: "enterprise"
description: "Allow to query whether MTE is enabled or not to check for compliance for enterprise policy"
bug: "322777918"
diff --git a/core/java/android/app/background_install_control_manager.aconfig b/core/java/android/app/background_install_control_manager.aconfig
index 4473b95..5f3bb07 100644
--- a/core/java/android/app/background_install_control_manager.aconfig
+++ b/core/java/android/app/background_install_control_manager.aconfig
@@ -3,6 +3,7 @@
flag {
namespace: "preload_safety"
name: "bic_client"
+ is_exported: true
description: "System API for background install control."
is_fixed_read_only: true
bug: "287507984"
diff --git a/core/java/android/app/grammatical_inflection_manager.aconfig b/core/java/android/app/grammatical_inflection_manager.aconfig
index 68d12ba..0d7bf65 100644
--- a/core/java/android/app/grammatical_inflection_manager.aconfig
+++ b/core/java/android/app/grammatical_inflection_manager.aconfig
@@ -2,6 +2,7 @@
flag {
name: "system_terms_of_address_enabled"
+ is_exported: true
namespace: "globalintl"
description: "Feature flag for System Terms of Address"
bug: "297798866"
diff --git a/core/java/android/app/multitasking.aconfig b/core/java/android/app/multitasking.aconfig
index ab00891..dbf3173 100644
--- a/core/java/android/app/multitasking.aconfig
+++ b/core/java/android/app/multitasking.aconfig
@@ -2,6 +2,7 @@
flag {
name: "enable_pip_ui_state_callback_on_entering"
+ is_exported: true
namespace: "multitasking"
description: "Enables PiP UI state callback on entering"
bug: "303718131"
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 274d02a..e9a7460 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -2,6 +2,7 @@
flag {
name: "modes_api"
+ is_exported: true
namespace: "systemui"
description: "This flag controls new and updated DND apis"
bug: "300477976"
@@ -16,6 +17,7 @@
flag {
name: "api_tvextender"
+ is_exported: true
namespace: "systemui"
description: "Guards new android.app.Notification.TvExtender api"
bug: "308164892"
@@ -24,6 +26,7 @@
flag {
name: "lifetime_extension_refactor"
+ is_exported: true
namespace: "systemui"
description: "Enables moving notification lifetime extension management from SystemUI to "
"Notification Manager Service"
@@ -46,6 +49,7 @@
flag {
name: "category_voicemail"
+ is_exported: true
namespace: "wear_sysui"
description: "Adds a new voicemail category for notifications"
bug: "322806700"
@@ -53,6 +57,7 @@
flag {
name: "notification_channel_vibration_effect_api"
+ is_exported: true
namespace: "systemui"
description: "This flag enables the API to allow setting VibrationEffect for NotificationChannels"
bug: "241732519"
diff --git a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
index 44f3329..dd9210f 100644
--- a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
+++ b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
@@ -2,6 +2,7 @@
flag {
name: "enable_on_device_intelligence"
+ is_exported: true
namespace: "ondeviceintelligence"
description: "Make methods on OnDeviceIntelligenceManager available for local inference."
bug: "304755128"
diff --git a/core/java/android/app/pinner-client.aconfig b/core/java/android/app/pinner-client.aconfig
index b60ad9e..0f7fa14 100644
--- a/core/java/android/app/pinner-client.aconfig
+++ b/core/java/android/app/pinner-client.aconfig
@@ -3,6 +3,7 @@
flag {
namespace: "system_performance"
name: "pinner_service_client_api"
+ is_exported: true
description: "Control exposing PinnerService APIs."
bug: "307594624"
}
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/flags.aconfig b/core/java/android/app/smartspace/flags.aconfig
index 12af888..e90ba67 100644
--- a/core/java/android/app/smartspace/flags.aconfig
+++ b/core/java/android/app/smartspace/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "remote_views"
+ is_exported: true
namespace: "sysui_integrations"
description: "Flag to enable the FlaggedApi to include RemoteViews in SmartspaceTarget"
bug: "300157758"
@@ -9,6 +10,7 @@
flag {
name: "access_smartspace"
+ is_exported: true
namespace: "sysui_integrations"
description: "Flag to enable the ACCESS_SMARTSPACE check in SmartspaceManagerService"
bug: "297207196"
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index 4d9d911..9a2d2e5 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "user_interaction_type_api"
+ is_exported: true
namespace: "backstage_power"
description: "Feature flag for user interaction event report/query API"
bug: "296061232"
@@ -9,6 +10,7 @@
flag {
name: "report_usage_stats_permission"
+ is_exported: true
namespace: "backstage_power"
description: "Feature flag for the new REPORT_USAGE_STATS permission."
bug: "296056771"
@@ -31,6 +33,7 @@
flag {
name: "filter_based_event_query_api"
+ is_exported: true
namespace: "backstage_power"
description: " Feature flag to support filter based event query API"
bug: "194321117"
@@ -38,6 +41,7 @@
flag {
name: "get_app_bytes_by_data_type_api"
+ is_exported: true
namespace: "system_performance"
description: "Feature flag for collecting app data size by file type API"
bug: "294088945"
diff --git a/core/java/android/app/wearable/flags.aconfig b/core/java/android/app/wearable/flags.aconfig
index b4f628f..d1d7b5d 100644
--- a/core/java/android/app/wearable/flags.aconfig
+++ b/core/java/android/app/wearable/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "enable_unsupported_operation_status_code"
+ is_exported: true
namespace: "machine_learning"
description: "This flag enables the WearableSensingManager#STATUS_UNSUPPORTED_OPERATION status code API."
bug: "301427767"
@@ -9,6 +10,7 @@
flag {
name: "enable_data_request_observer_api"
+ is_exported: true
namespace: "machine_learning"
description: "This flag enables the API to register a data request observer on WearableSensingManager."
bug: "301427767"
@@ -16,6 +18,7 @@
flag {
name: "enable_provide_wearable_connection_api"
+ is_exported: true
namespace: "machine_learning"
description: "This flag enables the WearableSensingManager#provideWearableConnection API."
bug: "301427767"
@@ -30,6 +33,7 @@
flag {
name: "enable_hotword_wearable_sensing_api"
+ is_exported: true
namespace: "machine_learning"
description: "This flag enables the APIs related to hotword in WearableSensingManager and WearableSensingService."
bug: "310055381"
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 822f02f..4511954 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "generated_previews"
+ is_exported: true
namespace: "app_widgets"
description: "Enable support for generated previews in AppWidgetManager"
bug: "306546610"
@@ -26,6 +27,7 @@
flag {
name: "draw_data_parcel"
+ is_exported: true
namespace: "app_widgets"
description: "Enable support for transporting draw instructions as data parcel"
bug: "286130467"
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index d634b64..ecc5e1b 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "new_association_builder"
+ is_exported: true
namespace: "companion"
description: "Controls if the new Builder is exposed to test apis."
bug: "296251481"
@@ -16,6 +17,7 @@
flag {
name: "association_tag"
+ is_exported: true
namespace: "companion"
description: "Enable Association tag APIs "
bug: "289241123"
@@ -23,6 +25,7 @@
flag {
name: "device_presence"
+ is_exported: true
namespace: "companion"
description: "Enable device presence APIs"
bug: "283000075"
@@ -30,6 +33,7 @@
flag {
name: "perm_sync_user_consent"
+ is_exported: true
namespace: "companion"
description: "Expose perm sync user consent API"
bug: "309528663"
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 588e4fc..a6a4f5e 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -19,6 +19,7 @@
flag {
name: "dynamic_policy"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable dynamic policy API"
bug: "298401780"
@@ -26,6 +27,7 @@
flag {
name: "cross_device_clipboard"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable cross-device clipboard API"
bug: "306622082"
@@ -40,6 +42,7 @@
flag {
name: "vdm_custom_ime"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable custom IME API"
bug: "287269288"
@@ -47,6 +50,7 @@
flag {
name: "vdm_custom_home"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable custom home API"
bug: "297168328"
@@ -54,6 +58,7 @@
flag {
name: "vdm_public_apis"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable public VDM API for device capabilities"
bug: "297253526"
@@ -61,6 +66,7 @@
flag {
name: "virtual_camera"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable Virtual Camera"
bug: "270352264"
@@ -82,6 +88,7 @@
flag {
name: "persistent_device_id_api"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable persistent device ID notification API"
bug: "295258915"
@@ -96,6 +103,7 @@
flag {
name: "interactive_screen_mirror"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable interactive screen mirroring using Virtual Devices"
bug: "292212199"
@@ -103,6 +111,7 @@
flag {
name: "virtual_stylus"
+ is_exported: true
namespace: "virtual_devices"
description: "Enable virtual stylus input"
bug: "304829446"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 284e318..c0c91cb 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5067,7 +5067,6 @@
* {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
*
- * @removed See {@link android.hardware.biometrics.BiometricPrompt}
* @see #getSystemService(String)
* @see android.hardware.fingerprint.FingerprintManager
*/
diff --git a/core/java/android/content/flags/flags.aconfig b/core/java/android/content/flags/flags.aconfig
index 3445fb5..27bce5b 100644
--- a/core/java/android/content/flags/flags.aconfig
+++ b/core/java/android/content/flags/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "enable_bind_package_isolated_process"
+ is_exported: true
namespace: "machine_learning"
description: "This flag enables the newly added flag for binding package-private isolated processes."
bug: "312706530"
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 92cb9cc..cde565b 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "quarantined_enabled"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag for Quarantined state"
bug: "269127435"
@@ -9,6 +10,7 @@
flag {
name: "archiving"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to enable the archiving feature."
bug: "278553670"
@@ -24,6 +26,7 @@
flag {
name: "stay_stopped"
+ is_exported: true
namespace: "backstage_power"
description: "Feature flag to improve stopped state enforcement"
bug: "296644915"
@@ -39,6 +42,7 @@
flag {
name: "get_package_info"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to enable the feature to retrieve package info without installation."
bug: "269149275"
@@ -54,6 +58,7 @@
flag {
name: "sdk_lib_independence"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to keep app working even if its declared sdk-library dependency is unavailable."
bug: "295827951"
@@ -78,6 +83,7 @@
flag {
name: "get_resolved_apk_path"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to retrieve resolved path of the base APK during an app install."
bug: "269728874"
@@ -92,6 +98,7 @@
flag {
name: "read_install_info"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to read install related information from an APK."
bug: "275658500"
@@ -113,6 +120,7 @@
flag {
name: "relative_reference_intent_filters"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to enable relative reference intent filters"
bug: "307556883"
@@ -121,6 +129,7 @@
flag {
name: "fix_duplicated_flags"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to fix duplicated PackageManager flag values"
bug: "314815969"
@@ -128,6 +137,7 @@
flag {
name: "provide_info_of_apk_in_apex"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to provide the information of APK-in-APEX"
bug: "306329516"
@@ -144,6 +154,7 @@
flag {
name: "introduce_media_processing_type"
+ is_exported: true
namespace: "backstage_power"
description: "Add a new FGS type for media processing use cases."
bug: "317788011"
@@ -182,6 +193,7 @@
flag {
name: "emergency_install_permission"
+ is_exported: true
namespace: "permissions"
description: "Feature flag to enable permission EMERGENCY_INSTALL_PACKAGES"
bug: "321080601"
@@ -189,6 +201,7 @@
flag {
name: "asl_in_apk_app_metadata_source"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to allow to know if the Android Safety Label (ASL) of an app is provided by the app's APK itself, or provided by an installer."
bug: "287487923"
@@ -205,6 +218,7 @@
flag {
name: "set_pre_verified_domains"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to enable pre-verified domains"
bug: "307327678"
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 2d32aed..4963a4f 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -24,6 +24,7 @@
flag {
name: "support_communal_profile"
+ is_exported: true
namespace: "multiuser"
description: "Framework support for communal profile."
bug: "285426179"
@@ -31,6 +32,7 @@
flag {
name: "support_communal_profile_nextgen"
+ is_exported: true
namespace: "multiuser"
description: "Further framework support for communal profile, beyond the basics, for later releases."
bug: "285426179"
@@ -59,6 +61,7 @@
flag {
name: "enable_biometrics_to_unlock_private_space"
+ is_exported: true
namespace: "profile_experiences"
description: "Add support to unlock the private space using biometrics"
bug: "312184187"
@@ -102,6 +105,7 @@
flag {
name: "enable_system_user_only_for_services_and_providers"
+ is_exported: true
namespace: "multiuser"
description: "Enable systemUserOnly manifest attribute for services and providers."
bug: "302354856"
@@ -118,6 +122,7 @@
flag {
name: "enable_permission_to_access_hidden_profiles"
+ is_exported: true
namespace: "profile_experiences"
description: "Add permission to access API hidden users data via system APIs"
bug: "321988638"
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 7fd0b03..8f5c912 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "default_locale"
+ is_exported: true
namespace: "resource_manager"
description: "Feature flag for default locale in LocaleConfig"
bug: "117306409"
@@ -11,6 +12,7 @@
flag {
name: "font_scale_converter_public"
+ is_exported: true
namespace: "accessibility"
description: "Enables the public API for FontScaleConverter, including enabling thread-safe caching."
bug: "239736383"
@@ -20,6 +22,7 @@
flag {
name: "asset_file_descriptor_frro"
+ is_exported: true
namespace: "resource_manager"
description: "Feature flag for passing in an AssetFileDescriptor to create an frro"
bug: "304478666"
@@ -27,6 +30,7 @@
flag {
name: "manifest_flagging"
+ is_exported: true
namespace: "resource_manager"
description: "Feature flag for flagging manifest entries"
bug: "297373084"
@@ -36,6 +40,7 @@
flag {
name: "nine_patch_frro"
+ is_exported: true
namespace: "resource_manager"
description: "Feature flag for creating an frro from a 9-patch"
bug: "296324826"
@@ -43,6 +48,7 @@
flag {
name: "register_resource_paths"
+ is_exported: true
namespace: "resource_manager"
description: "Feature flag for register resource paths for shared library"
bug: "306202569"
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 16ca31f..d077329 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -3,6 +3,7 @@
flag {
namespace: "credential_manager"
name: "settings_activity_enabled"
+ is_exported: true
description: "Enable the Credential Manager Settings Activity APIs"
bug: "300014059"
}
@@ -24,6 +25,7 @@
flag {
namespace: "credential_manager"
name: "new_settings_intents"
+ is_exported: true
description: "Enables settings intents to redirect to new settings page"
bug: "307587989"
}
@@ -45,6 +47,7 @@
flag {
namespace: "credential_manager"
name: "configurable_selector_ui_enabled"
+ is_exported: true
description: "Enables OEM configurable Credential Selector UI"
bug: "319448437"
is_exported: true
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index 92ef9c2..7ecffaf 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "sqlite_apis_35"
+ is_exported: true
namespace: "system_performance"
is_fixed_read_only: true
description: "SQLite APIs held back for Android 15"
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index ff07498..9836eec 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -18,6 +18,7 @@
flag {
name: "get_op_id_crypto_object"
+ is_exported: true
namespace: "biometrics_framework"
description: "Feature flag for adding a get operation id api to CryptoObject."
bug: "307601768"
@@ -25,8 +26,8 @@
flag {
name: "custom_biometric_prompt"
+ is_exported: true
namespace: "biometrics_framework"
description: "Feature flag for adding a custom content view API to BiometricPrompt.Builder."
bug: "302735104"
}
-
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index 73a9e34..e474603 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "device_state_property_api"
+ is_exported: true
namespace: "windowing_sdk"
description: "Updated DeviceState hasProperty API"
bug: "293636629"
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b0f69f5..81e321d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -83,8 +83,7 @@
/**
* A class that coordinates access to the fingerprint hardware.
- *
- * @removed See {@link BiometricPrompt} which shows a system-provided dialog upon starting
+ * @deprecated See {@link BiometricPrompt} which shows a system-provided dialog upon starting
* authentication. In a world where devices may have different types of biometric authentication,
* it's much more realistic to have a system-provided authentication dialog since the method may
* vary by vendor/device.
@@ -95,6 +94,7 @@
@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
public class FingerprintManager implements BiometricAuthenticator, BiometricFingerprintConstants {
private static final String TAG = "FingerprintManager";
+ private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
private static final int MSG_ACQUIRED = 101;
private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
@@ -196,7 +196,6 @@
/**
* Retrieves a test session for FingerprintManager.
- *
* @hide
*/
@TestApi
@@ -255,10 +254,9 @@
}
/**
- * A wrapper class for the crypto objects supported by FingerprintManager. Currently, the
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
- *
- * @removed See {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}
+ * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}
*/
@Deprecated
public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
@@ -332,8 +330,7 @@
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}.
- *
- * @removed See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}
+ * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}
*/
@Deprecated
public static class AuthenticationResult {
@@ -395,8 +392,7 @@
* FingerprintManager#authenticate(CryptoObject, CancellationSignal,
* int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
- *
- * @removed See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationCallback}
+ * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationCallback}
*/
@Deprecated
public static abstract class AuthenticationCallback
@@ -459,7 +455,6 @@
/**
* Callback structure provided for {@link #detectFingerprint(CancellationSignal,
* FingerprintDetectionCallback, int, Surface)}.
- *
* @hide
*/
public interface FingerprintDetectionCallback {
@@ -613,8 +608,7 @@
* by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
* facility</a>.
* @throws IllegalStateException if the crypto primitive is not initialized.
- *
- * @removed See {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+ * @deprecated See {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
* BiometricPrompt.AuthenticationCallback)} and {@link BiometricPrompt#authenticate(
* BiometricPrompt.CryptoObject, CancellationSignal, Executor,
* BiometricPrompt.AuthenticationCallback)}
@@ -629,7 +623,6 @@
/**
* Per-user version of authenticate.
* @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FingerprintAuthenticateOptions)}.
- *
* @hide
*/
@Deprecated
@@ -642,7 +635,6 @@
/**
* Per-user and per-sensor version of authenticate.
* @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FingerprintAuthenticateOptions)}.
- *
* @hide
*/
@Deprecated
@@ -659,7 +651,6 @@
/**
* Version of authenticate with additional options.
- *
* @hide
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
@@ -707,7 +698,6 @@
/**
* Uses the fingerprint hardware to detect for the presence of a finger, without giving details
* about accept/reject/lockout.
- *
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -750,7 +740,6 @@
* @param callback an object to receive enrollment events
* @param shouldLogMetrics a flag that indicates if enrollment failure/success metrics
* should be logged.
- *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -821,7 +810,6 @@
/**
* Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
* enumerated sensor.
- *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -836,7 +824,6 @@
/**
* Revokes the specified challenge.
- *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -862,7 +849,6 @@
* @param sensorId Sensor ID that this operation takes effect for
* @param userId User ID that this operation takes effect for.
* @param hardwareAuthToken An opaque token returned by password confirmation.
- *
* @hide
*/
@RequiresPermission(RESET_FINGERPRINT_LOCKOUT)
@@ -900,7 +886,6 @@
/**
* Removes all fingerprint templates for the given user.
- *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -1020,7 +1005,6 @@
/**
* Forwards BiometricStateListener to FingerprintService
* @param listener new BiometricStateListener being added
- *
* @hide
*/
public void registerBiometricStateListener(@NonNull BiometricStateListener listener) {
@@ -1172,8 +1156,7 @@
}
/**
- * This is triggered by SideFpsEventHandler.
- *
+ * This is triggered by SideFpsEventHandler
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -1186,8 +1169,7 @@
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
- *
- * @removed See {@link BiometricPrompt} and
+ * @deprecated See {@link BiometricPrompt} and
* {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
*/
@Deprecated
@@ -1221,8 +1203,7 @@
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
- *
- * @removed See {@link BiometricPrompt} and
+ * @deprecated See {@link BiometricPrompt} and
* {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
*/
@Deprecated
@@ -1248,7 +1229,6 @@
/**
* Get statically configured sensor properties.
- *
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -1267,7 +1247,6 @@
/**
* Returns whether the device has a power button fingerprint sensor.
* @return boolean indicating whether power button is fingerprint sensor
- *
* @hide
*/
public boolean isPowerbuttonFps() {
diff --git a/core/java/android/hardware/flags/overlayproperties_flags.aconfig b/core/java/android/hardware/flags/overlayproperties_flags.aconfig
index c6a352e..1165e65 100644
--- a/core/java/android/hardware/flags/overlayproperties_flags.aconfig
+++ b/core/java/android/hardware/flags/overlayproperties_flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "overlayproperties_class_api"
+ is_exported: true
namespace: "core_graphics"
description: "public OverlayProperties class, OverlayProperties#supportMixedColorSpaces and Display#getOverlaySupport API"
bug: "267234573"
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index e070fe5..9684e64 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -27,6 +27,7 @@
flag {
namespace: "input_native"
name: "pointer_coords_is_resampled_api"
+ is_exported: true
description: "Makes MotionEvent.PointerCoords#isResampled() a public API"
bug: "298197511"
}
@@ -34,6 +35,7 @@
flag {
namespace: "input_native"
name: "emoji_and_screenshot_keycodes_available"
+ is_exported: true
description: "Add new KeyEvent keycodes for opening Emoji Picker and Taking Screenshots"
bug: "315307777"
}
diff --git a/core/java/android/hardware/radio/flags.aconfig b/core/java/android/hardware/radio/flags.aconfig
index dbc1a4b..d0d10c1 100644
--- a/core/java/android/hardware/radio/flags.aconfig
+++ b/core/java/android/hardware/radio/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "hd_radio_improved"
+ is_exported: true
namespace: "car_framework"
description: "Feature flag for improved HD radio support with less vendor extensions"
bug: "280300929"
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 9e487e1..fac02ce 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "enable_usb_data_compliance_warning"
+ is_exported: true
namespace: "system_sw_usb"
description: "Enable USB data compliance warnings when set"
bug: "296119135"
diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
index a495631..3dd746c 100644
--- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "enable_is_pd_compliant_api"
+ is_exported: true
namespace: "usb"
description: "Feature flag for the api to check if a port is PD compliant"
bug: "323470419"
@@ -9,6 +10,7 @@
flag {
name: "enable_is_mode_change_supported_api"
+ is_exported: true
namespace: "usb"
description: "Feature flag for the api to check if a port supports mode change"
bug: "323470419"
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 375d729..311e991 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -26,6 +26,7 @@
flag {
name: "remove_app_profiler_pss_collection"
+ is_exported: true
namespace: "backstage_power"
description: "Replaces background PSS collection in AppProfiler with RSS"
bug: "297542292"
@@ -33,6 +34,7 @@
flag {
name: "allow_thermal_headroom_thresholds"
+ is_exported: true
namespace: "game"
description: "Enable thermal headroom thresholds API"
bug: "288119641"
@@ -41,6 +43,7 @@
# This flag guards the private space feature, its APIs, and some of the feature implementations. The flag android.multiuser.Flags.enable_private_space_features exclusively guards all the implementations.
flag {
name: "allow_private_profile"
+ is_exported: true
namespace: "profile_experiences"
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
bug: "299069460"
@@ -49,6 +52,7 @@
flag {
name: "bugreport_mode_max_value"
+ is_exported: true
namespace: "telephony"
description: "Introduce a constant as maximum value of bugreport mode."
bug: "305067125"
@@ -56,6 +60,7 @@
flag {
name: "adpf_prefer_power_efficiency"
+ is_exported: true
namespace: "game"
description: "Guards the ADPF power efficiency API"
bug: "288117936"
@@ -63,6 +68,7 @@
flag {
name: "security_state_service"
+ is_exported: true
namespace: "dynamic_spl"
description: "Guards the Security State API."
bug: "302189431"
@@ -70,6 +76,7 @@
flag {
name: "battery_saver_supported_check_api"
+ is_exported: true
namespace: "backstage_power"
description: "Guards a new API in PowerManager to check if battery saver is supported or not."
bug: "305067031"
@@ -77,6 +84,7 @@
flag {
name: "adpf_gpu_report_actual_work_duration"
+ is_exported: true
namespace: "game"
description: "Guards the ADPF GPU APIs."
bug: "284324521"
@@ -114,6 +122,7 @@
flag {
name: "battery_part_status_api"
+ is_exported: true
namespace: "phoenix"
description: "Feature flag for adding Health HAL v3 APIs."
is_fixed_read_only: true
@@ -122,6 +131,7 @@
flag {
name: "storage_lifetime_api"
+ is_exported: true
namespace: "phoenix"
description: "Feature flag for adding storage component health APIs."
is_fixed_read_only: true
@@ -131,6 +141,7 @@
flag {
namespace: "system_performance"
name: "telemetry_apis_framework_initialization"
+ is_exported: true
description: "Control framework initialization APIs of telemetry APIs feature."
is_fixed_read_only: true
bug: "324241334"
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index d485eca..bb0498e 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -10,6 +10,7 @@
flag {
namespace: "haptics"
name: "haptics_customization_enabled"
+ is_exported: true
description: "Enables the haptics customization feature"
bug: "241918098"
}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 999bc99..2710df2 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "device_aware_permission_apis_enabled"
+ is_exported: true
is_fixed_read_only: true
namespace: "permissions"
description: "enable device aware permission APIs"
@@ -10,6 +11,7 @@
flag {
name: "voice_activation_permission_apis"
+ is_exported: true
namespace: "permissions"
description: "enable voice activation permission APIs"
bug: "287264308"
@@ -17,6 +19,7 @@
flag {
name: "system_server_role_controller_enabled"
+ is_exported: true
is_fixed_read_only: true
namespace: "permissions"
description: "enable role controller in system server"
@@ -25,6 +28,7 @@
flag {
name: "set_next_attribution_source"
+ is_exported: true
namespace: "permissions"
description: "enable AttributionSource.setNextAttributionSource"
bug: "304478648"
@@ -32,6 +36,7 @@
flag {
name: "should_register_attribution_source"
+ is_exported: true
namespace: "permissions"
description: "enable the shouldRegisterAttributionSource API"
bug: "305057691"
@@ -39,6 +44,7 @@
flag {
name: "attribution_source_constructor"
+ is_exported: true
namespace: "permissions"
description: "enable AttributionSource(int, int, String, String, IBinder, String[], AttributionSource)"
bug: "304478648"
@@ -46,6 +52,7 @@
flag {
name: "enhanced_confirmation_mode_apis_enabled"
+ is_exported: true
is_fixed_read_only: true
namespace: "permissions"
description: "enable enhanced confirmation mode apis"
@@ -54,6 +61,7 @@
flag {
name: "op_enable_mobile_data_by_user"
+ is_exported: true
namespace: "permissions"
description: "enables logging of the OP_ENABLE_MOBILE_DATA_BY_USER"
bug: "222650148"
@@ -61,6 +69,7 @@
flag {
name: "factory_reset_prep_permission_apis"
+ is_exported: true
namespace: "wallet_integration"
description: "enable Permission PREPARE_FACTORY_RESET."
bug: "302016478"
@@ -68,6 +77,7 @@
flag {
name: "retail_demo_role_enabled"
+ is_exported: true
namespace: "permissions"
description: "default retail demo role holder"
bug: "274132354"
@@ -82,6 +92,7 @@
flag {
name: "wallet_role_enabled"
+ is_exported: true
namespace: "wallet_integration"
description: "This flag is used to enabled the Wallet Role for all users on the device"
bug: "283989236"
@@ -114,6 +125,7 @@
flag {
name: "get_emergency_role_holder_api_enabled"
+ is_exported: true
is_fixed_read_only: true
namespace: "permissions"
description: "Enables the getEmergencyRoleHolder API."
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 25c2b0e..d0593e7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12389,6 +12389,10 @@
/**
* Whether or not secure windows should be disabled. This only works on debuggable builds.
*
+ * <p>When this setting is set to a non-zero value, all windows are treated as non-secure.
+ * Content in windows with {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} will
+ * appear in screenshots and recordings.
+ *
* @hide
*/
public static final String DISABLE_SECURE_WINDOWS = "disable_secure_windows";
diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig
index ea1ac27..9245557 100644
--- a/core/java/android/provider/flags.aconfig
+++ b/core/java/android/provider/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "system_settings_default"
+ is_exported: true
namespace: "package_manager_service"
description: "Enable Settings.System.resetToDefault APIs."
bug: "279083734"
@@ -9,6 +10,7 @@
flag {
name: "user_keys"
+ is_exported: true
namespace: "privacy_infra_policy"
description: "This flag controls new E2EE contact keys API"
bug: "290696572"
@@ -16,6 +18,7 @@
flag {
name: "backup_tasks_settings_screen"
+ is_exported: true
namespace: "backstage_power"
description: "Add a new settings page for the RUN_BACKUP_JOBS permission."
bug: "320563660"
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 3c77c44..7f5b550 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -10,6 +10,7 @@
flag {
name: "fsverity_api"
+ is_exported: true
namespace: "hardware_backed_security"
description: "Feature flag for fs-verity API"
bug: "285185747"
@@ -64,6 +65,7 @@
flag {
name: "frp_enforcement"
+ is_exported: true
namespace: "hardware_backed_security"
description: "This flag controls whether PDB enforces FRP"
bug: "290312729"
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 0bae459..548f8aa 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -9,6 +9,7 @@
flag {
name: "asm_restrictions_enabled"
+ is_exported: true
namespace: "responsible_apis"
description: "Enables ASM restrictions for activity starts and finishes"
bug: "230590090"
@@ -23,6 +24,7 @@
flag {
name: "content_uri_permission_apis"
+ is_exported: true
namespace: "responsible_apis"
description: "Enables the content URI permission APIs"
bug: "293467489"
@@ -30,6 +32,7 @@
flag {
name: "enforce_intent_filter_match"
+ is_exported: true
namespace: "responsible_apis"
description: "Make delivered intents match components' intent filters"
bug: "293560872"
diff --git a/core/java/android/service/appprediction/flags/flags.aconfig b/core/java/android/service/appprediction/flags/flags.aconfig
index c7e47d4..7f9764e 100644
--- a/core/java/android/service/appprediction/flags/flags.aconfig
+++ b/core/java/android/service/appprediction/flags/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "service_features_api"
+ is_exported: true
namespace: "systemui"
description: "Guards the new requestServiceFeatures api"
bug: "292565550"
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index 00236df..a3eff3b 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "chooser_album_text"
+ is_exported: true
namespace: "intentresolver"
description: "Flag controlling the album text subtype hint for sharesheet"
bug: "323380224"
@@ -9,6 +10,7 @@
flag {
name: "enable_sharesheet_metadata_extra"
+ is_exported: true
namespace: "intentresolver"
description: "This flag enables sharesheet metadata to be displayed to users."
bug: "318942069"
@@ -16,6 +18,7 @@
flag {
name: "chooser_payload_toggling"
+ is_exported: true
namespace: "intentresolver"
description: "This flag controls content toggling in Chooser"
bug: "302691505"
@@ -23,6 +26,7 @@
flag {
name: "enable_chooser_result"
+ is_exported: true
namespace: "intentresolver"
description: "Provides additional callbacks with information about user actions in ChooserResult"
bug: "263474465"
diff --git a/core/java/android/service/controls/flags/flags.aconfig b/core/java/android/service/controls/flags/flags.aconfig
index 3a28844..197f1bc 100644
--- a/core/java/android/service/controls/flags/flags.aconfig
+++ b/core/java/android/service/controls/flags/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "home_panel_dream"
+ is_exported: true
namespace: "systemui"
description: "Enables the home controls dream feature."
bug: "298025023"
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index c5acc2c..35cd3ed 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -10,6 +10,7 @@
flag {
name: "redact_sensitive_notifications_from_untrusted_listeners"
+ is_exported: true
namespace: "systemui"
description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"
bug: "306271190"
@@ -18,6 +19,7 @@
flag {
name: "callstyle_callback_api"
+ is_exported: true
namespace: "systemui"
description: "Guards the new CallStyleNotificationEventsCallback"
bug: "305095040"
diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig
index 22e8cdd..633304b 100644
--- a/core/java/android/service/voice/flags/flags.aconfig
+++ b/core/java/android/service/voice/flags/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "allow_training_data_egress_from_hds"
+ is_exported: true
namespace: "machine_learning"
description: "This flag allows the hotword detection service to egress training data to the default assistant."
bug: "296074924"
@@ -9,6 +10,7 @@
flag {
name: "allow_hotword_bump_egress"
+ is_exported: true
namespace: "machine_learning"
description: "This flag allows hotword detection service to egress reason code for hotword bump."
bug: "290951024"
@@ -16,6 +18,7 @@
flag {
name: "allow_foreground_activities_in_on_show"
+ is_exported: true
namespace: "machine_learning"
description: "This flag allows providing foreground app component along with onShow args."
bug: "319409708"
@@ -23,6 +26,7 @@
flag {
name: "allow_various_attention_types"
+ is_exported: true
namespace: "visual_query"
description: "This flag allows visual query detection service to set different attention types."
bug: "318617199"
@@ -30,6 +34,7 @@
flag {
name: "allow_complex_results_egress_from_vqds"
+ is_exported: true
namespace: "visual_query"
description: "This flag allows visual query detection service egress detailed results. "
bug: "318617199"
@@ -37,6 +42,7 @@
flag {
name: "allow_speaker_id_egress"
+ is_exported: true
namespace: "machine_learning"
description: "This flag allows hotword detection service and visual query detection service to egress current speaker profile id."
bug: "318617199"
diff --git a/core/java/android/speech/flags/speech_flags.aconfig b/core/java/android/speech/flags/speech_flags.aconfig
index fd80127..fa33592 100644
--- a/core/java/android/speech/flags/speech_flags.aconfig
+++ b/core/java/android/speech/flags/speech_flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "multilang_extra_launch"
+ is_exported: true
namespace: "machine_learning"
description: "Feature flag for adding new extra for multi-lang feature"
bug: "312489931"
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 30b1a2e..8e1ac63 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -10,6 +10,7 @@
flag {
name: "new_fonts_fallback_xml"
+ is_exported: true
namespace: "text"
description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
# Make read only, as it could be used before the Settings provider is initialized.
@@ -26,6 +27,7 @@
flag {
name: "fix_line_height_for_locale"
+ is_exported: true
namespace: "text"
description: "Feature flag that preserve the line height of the TextView and EditText even if the the locale is different from Latin"
bug: "303326708"
@@ -33,6 +35,7 @@
flag {
name: "no_break_no_hyphenation_span"
+ is_exported: true
namespace: "text"
description: "A feature flag that adding new spans that prevents line breaking and hyphenation."
bug: "283193586"
@@ -57,6 +60,7 @@
flag {
name: "use_bounds_for_width"
+ is_exported: true
namespace: "text"
description: "Feature flag for preventing horizontal clipping."
bug: "63938206"
@@ -71,6 +75,7 @@
flag {
name: "word_style_auto"
+ is_exported: true
namespace: "text"
description: "A feature flag that implements line break word style auto."
bug: "280005585"
@@ -78,6 +83,7 @@
flag {
name: "letter_spacing_justification"
+ is_exported: true
namespace: "text"
description: "A feature flag that implement inter character justification."
bug: "283193133"
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 5b99c71f..91bd4ea 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -4,6 +4,7 @@
flag {
name: "a11y_overlay_callbacks"
+ is_exported: true
namespace: "accessibility"
description: "Whether to allow the passing of result callbacks when attaching a11y overlays."
bug: "304478691"
@@ -26,6 +27,7 @@
flag {
namespace: "accessibility"
name: "braille_display_hid"
+ is_exported: true
description: "Enables new APIs for an AccessibilityService to communicate with a HID Braille display"
bug: "303522222"
}
@@ -40,6 +42,7 @@
flag {
namespace: "accessibility"
name: "collection_info_item_counts"
+ is_exported: true
description: "Fields for total items and the number of important for accessibility items in a collection"
bug: "302376158"
}
@@ -61,6 +64,7 @@
flag {
namespace: "accessibility"
name: "flash_notification_system_api"
+ is_exported: true
description: "Makes flash notification APIs as system APIs for calling from mainline module"
bug: "303131332"
}
@@ -74,6 +78,7 @@
flag {
name: "motion_event_observing"
+ is_exported: true
namespace: "accessibility"
description: "Allows accessibility services to intercept but not consume motion events from specified sources."
bug: "297595990"
@@ -82,6 +87,7 @@
flag {
namespace: "accessibility"
name: "granular_scrolling"
+ is_exported: true
description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
bug: "302376158"
}
@@ -103,6 +109,7 @@
flag {
namespace: "accessibility"
name: "add_type_window_control"
+ is_exported: true
description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations"
bug: "320445550"
}
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index 5d3153c..4de0f29 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -23,6 +23,7 @@
flag {
name: "create_accessibility_overlay_app_op_enabled"
+ is_exported: true
namespace: "content_protection"
description: "If true, an appop is logged on creation of accessibility overlays."
bug: "289081465"
@@ -30,6 +31,7 @@
flag {
name: "rapid_clear_notifications_by_listener_app_op_enabled"
+ is_exported: true
namespace: "content_protection"
description: "If true, an appop is logged when a notification is rapidly cleared by a notification listener."
bug: "289080543"
@@ -37,6 +39,7 @@
flag {
name: "manage_device_policy_enabled"
+ is_exported: true
namespace: "content_protection"
description: "If true, the APIs to manage content protection device policy will be enabled."
bug: "319477846"
diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index 05cabd5..06598b3 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "view_velocity_api"
+ is_exported: true
namespace: "toolkit"
description: "Feature flag for view content velocity api"
bug: "293513816"
@@ -16,6 +17,7 @@
flag {
name: "toolkit_set_frame_rate_read_only"
+ is_exported: true
namespace: "toolkit"
description: "Feature flag for toolkit to set frame rate"
bug: "293512962"
@@ -24,6 +26,7 @@
flag {
name: "expected_presentation_time_api"
+ is_exported: true
namespace: "toolkit"
description: "Feature flag for using expected presentation time of the Choreographer"
bug: "278730197"
@@ -31,6 +34,7 @@
flag {
name: "expected_presentation_time_read_only"
+ is_exported: true
namespace: "toolkit"
description: "Feature flag for using expected presentation time of the Choreographer"
bug: "278730197"
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index d1d871c2..a7c4104 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -3,6 +3,7 @@
flag {
namespace: "toolkit"
name: "scroll_feedback_api"
+ is_exported: true
description: "Enable the scroll feedback APIs"
bug: "239594271"
}
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index 6cf89d6..c482f8b 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -28,6 +28,7 @@
flag {
name: "sensitive_content_app_protection_api"
+ is_exported: true
namespace: "permissions"
description: "This flag controls the new sensitive content protection API,"
" The API will be used by other ui toolkits (i.e. compose, webview, custom virtual views)."
diff --git a/core/java/android/view/flags/window_insets.aconfig b/core/java/android/view/flags/window_insets.aconfig
index 201b7ad..bf6df5c 100644
--- a/core/java/android/view/flags/window_insets.aconfig
+++ b/core/java/android/view/flags/window_insets.aconfig
@@ -2,6 +2,7 @@
flag {
name: "customizable_window_headers"
+ is_exported: true
namespace: "lse_desktop_experience"
description: "Flag to control the caption bar appearance and to fit app content in its empty space"
bug: "316387515"
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 8d3920f..be74a65 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -10,6 +10,7 @@
flag {
name: "editorinfo_handwriting_enabled"
+ is_exported: true
namespace: "input_method"
description: "Feature flag for adding EditorInfo#mStylusHandwritingEnabled"
bug: "293898187"
@@ -18,6 +19,7 @@
flag {
name: "imm_userhandle_hostsidetests"
+ is_exported: true
namespace: "input_method"
description: "Feature flag for replacing UserIdInt with UserHandle in some helper IMM functions"
bug: "301713309"
@@ -26,6 +28,7 @@
flag {
name: "concurrent_input_methods"
+ is_exported: true
namespace: "input_method"
description: "Feature flag for concurrent multi-session IME"
bug: "284527000"
@@ -34,6 +37,7 @@
flag {
name: "home_screen_handwriting_delegator"
+ is_exported: true
namespace: "input_method"
description: "Feature flag for supporting stylus handwriting delegation from RemoteViews on the home screen"
bug: "279959705"
@@ -49,6 +53,7 @@
flag {
name: "use_zero_jank_proxy"
+ is_exported: true
namespace: "input_method"
description: "Feature flag for using a proxy that uses async calls to achieve zero jank for IMMS calls."
bug: "293640003"
@@ -57,6 +62,7 @@
flag {
name: "ime_switcher_revamp"
+ is_exported: true
namespace: "input_method"
description: "Feature flag for revamping the Input Method Switcher menu"
bug: "311791923"
@@ -73,6 +79,7 @@
flag {
name: "connectionless_handwriting"
+ is_exported: true
namespace: "input_method"
description: "Feature flag for connectionless stylus handwriting APIs"
bug: "300979854"
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index 6938b29..2d834a8 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "update_service_ipc_wrapper"
+ is_exported: true
namespace: "webview"
description: "New API: proper wrapper for IWebViewUpdateService"
bug: "319292658"
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 254f4f7..7fbec67 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -18,6 +18,7 @@
flag {
name: "density_390_api"
+ is_exported: true
namespace: "large_screen_experiences_app_compat"
description: "Whether the API DisplayMetrics.DENSITY_390 is available"
bug: "297550533"
@@ -26,6 +27,7 @@
flag {
name: "app_compat_properties_api"
+ is_exported: true
namespace: "large_screen_experiences_app_compat"
description: "Whether app compat property APIs are public. Which includes: /n"
"WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE,/n"
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index ea9da96..dea9497 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -2,6 +2,7 @@
flag {
name: "always_update_wallpaper_permission"
+ is_exported: true
namespace: "wear_frameworks"
description: "Allow out of focus process to update wallpaper complications"
bug: "271132915"
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 3f48341..00b600c 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -45,6 +45,7 @@
flag {
namespace: "window_surfaces"
name: "trusted_presentation_listener_for_window"
+ is_exported: true
description: "Enable trustedPresentationListener on windows public API"
is_fixed_read_only: true
bug: "278027319"
@@ -53,6 +54,7 @@
flag {
namespace: "window_surfaces"
name: "sdk_desired_present_time"
+ is_exported: true
description: "Feature flag for the new SDK API to set desired present time"
is_fixed_read_only: true
bug: "295038072"
@@ -61,6 +63,7 @@
flag {
namespace: "window_surfaces"
name: "surface_control_input_receiver"
+ is_exported: true
description: "Enable public API to register an InputReceiver for a SurfaceControl"
is_fixed_read_only: true
bug: "278757236"
@@ -69,6 +72,7 @@
flag {
namespace: "window_surfaces"
name: "screen_recording_callbacks"
+ is_exported: true
description: "Enable screen recording callbacks public API"
is_fixed_read_only: true
bug: "304574518"
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 65bf241..247f28c 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -16,6 +16,7 @@
flag {
name: "enforce_edge_to_edge"
+ is_exported: true
namespace: "windowing_frontend"
description: "Make app go edge-to-edge when targeting SDK level 35 or greater"
bug: "309578419"
@@ -88,6 +89,7 @@
flag {
name: "supports_multi_instance_system_ui"
+ is_exported: true
namespace: "multitasking"
description: "Feature flag to enable a multi-instance system ui component property."
bug: "262864589"
@@ -96,6 +98,7 @@
flag {
name: "delegate_unhandled_drags"
+ is_exported: true
namespace: "multitasking"
description: "Enables delegating unhandled drags to SystemUI"
bug: "320797628"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 82e613e..4b3d8e8 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -43,6 +43,7 @@
flag {
namespace: "windowing_sdk"
name: "untrusted_embedding_any_app_permission"
+ is_exported: true
description: "Feature flag to enable the permission to embed any app in untrusted mode."
bug: "293647332"
is_fixed_read_only: true
@@ -59,6 +60,7 @@
flag {
namespace: "windowing_sdk"
name: "untrusted_embedding_state_sharing"
+ is_exported: true
description: "Feature flag to enable state sharing in untrusted embedding when apps opt in."
bug: "293647332"
is_fixed_read_only: true
@@ -74,6 +76,7 @@
flag {
namespace: "windowing_sdk"
name: "cover_display_opt_in"
+ is_exported: true
description: "Properties to allow apps and activities to opt-in to cover display rendering"
bug: "312530526"
is_fixed_read_only: true
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
index 6c81a60..1e41b4d 100644
--- a/graphics/java/android/framework_graphics.aconfig
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -2,6 +2,7 @@
flag {
name: "exact_compute_bounds"
+ is_exported: true
namespace: "core_graphics"
description: "Add a function without unused exact param for computeBounds."
bug: "304478551"
@@ -9,6 +10,7 @@
flag {
name: "yuv_image_compress_to_ultra_hdr"
+ is_exported: true
namespace: "core_graphics"
description: "Feature flag for YUV image compress to Ultra HDR."
bug: "308978825"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
index 4c34971..9e8dfb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -21,11 +21,9 @@
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.os.UserHandle
-import android.view.WindowManager
import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.R
-import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
import com.android.wm.shell.util.KtProtoLog
import java.util.Arrays
@@ -37,7 +35,8 @@
private val context: Context,
private val packageManager: PackageManager,
private val staticAppsSupportingMultiInstance: Array<String> = context.resources
- .getStringArray(R.array.config_appsSupportMultiInstancesSplit)) {
+ .getStringArray(R.array.config_appsSupportMultiInstancesSplit),
+ private val supportsMultiInstanceProperty: Boolean) {
/**
* Returns whether a specific component desires to be launched in multiple instances.
@@ -59,6 +58,11 @@
}
}
+ if (!supportsMultiInstanceProperty) {
+ // If not checking the multi-instance properties, then return early
+ return false;
+ }
+
// Check the activity property first
try {
val activityProp = packageManager.getProperty(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 8d489e1..5122114 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -29,6 +29,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ProtoLogController;
import com.android.wm.shell.R;
import com.android.wm.shell.RootDisplayAreaOrganizer;
@@ -326,7 +327,8 @@
@WMSingleton
@Provides
static MultiInstanceHelper provideMultiInstanceHelper(Context context) {
- return new MultiInstanceHelper(context, context.getPackageManager());
+ return new MultiInstanceHelper(context, context.getPackageManager(),
+ Flags.supportsMultiInstanceSystemUi());
}
//
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
index 2f5fe11..bec91e9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
@@ -32,9 +32,12 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.eq
+import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@@ -77,7 +80,7 @@
@Test
fun supportsMultiInstanceSplit_inStaticAllowList() {
val allowList = arrayOf(TEST_PACKAGE)
- val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList, true)
val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
assertEquals(true, helper.supportsMultiInstanceSplit(component))
}
@@ -85,7 +88,7 @@
@Test
fun supportsMultiInstanceSplit_notInStaticAllowList() {
val allowList = arrayOf(TEST_PACKAGE)
- val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList, true)
val component = ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY)
assertEquals(false, helper.supportsMultiInstanceSplit(component))
}
@@ -104,7 +107,7 @@
eq(component.packageName)))
.thenReturn(appProp)
- val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
// Expect activity property to override application property
assertEquals(true, helper.supportsMultiInstanceSplit(component))
}
@@ -123,7 +126,7 @@
eq(component.packageName)))
.thenReturn(appProp)
- val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
// Expect activity property to override application property
assertEquals(false, helper.supportsMultiInstanceSplit(component))
}
@@ -141,7 +144,7 @@
eq(component.packageName)))
.thenReturn(appProp)
- val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
// Expect fall through to app property
assertEquals(true, helper.supportsMultiInstanceSplit(component))
}
@@ -158,10 +161,30 @@
eq(component.packageName)))
.thenThrow(PackageManager.NameNotFoundException())
- val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
assertEquals(false, helper.supportsMultiInstanceSplit(component))
}
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun checkNoMultiInstancePropertyFlag_ignoreProperty() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ val activityProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenReturn(activityProp)
+ val appProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenReturn(appProp)
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), false)
+ // Expect we only check the static list and not the property
+ assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ verify(pm, never()).getProperty(any(), any<ComponentName>())
+ }
+
companion object {
val TEST_PACKAGE = "com.android.wm.shell.common"
val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake";
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 76a0a64..659bcdc 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "clip_shader"
+ is_exported: true
namespace: "core_graphics"
description: "API for canvas shader clipping operations"
bug: "280116960"
@@ -9,6 +10,7 @@
flag {
name: "matrix_44"
+ is_exported: true
namespace: "core_graphics"
description: "API for 4x4 matrix and related canvas functions"
bug: "280116960"
@@ -16,6 +18,7 @@
flag {
name: "limited_hdr"
+ is_exported: true
namespace: "core_graphics"
description: "API to enable apps to restrict the amount of HDR headroom that is used"
bug: "234181960"
@@ -44,6 +47,7 @@
flag {
name: "gainmap_animations"
+ is_exported: true
namespace: "core_graphics"
description: "APIs to help enable animations involving gainmaps"
bug: "296482289"
@@ -51,6 +55,7 @@
flag {
name: "gainmap_constructor_with_metadata"
+ is_exported: true
namespace: "core_graphics"
description: "APIs to create a new gainmap with a bitmap for metadata."
bug: "304478551"
@@ -65,6 +70,7 @@
flag {
name: "requested_formats_v"
+ is_exported: true
namespace: "core_graphics"
description: "Enable r_8, r_16_uint, rg_1616_uint, and rgba_10101010 in the SDK"
bug: "292545615"
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index f33bcb7..ce689ac 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -9,6 +9,7 @@
flag {
name: "location_bypass"
+ is_exported: true
namespace: "location"
description: "Enable location bypass appops behavior"
bug: "329151785"
diff --git a/media/java/android/media/flags/editing.aconfig b/media/java/android/media/flags/editing.aconfig
index c3997e9..5bf1b4e 100644
--- a/media/java/android/media/flags/editing.aconfig
+++ b/media/java/android/media/flags/editing.aconfig
@@ -2,6 +2,7 @@
flag {
name: "add_media_metrics_editing"
+ is_exported: true
namespace: "media_solutions"
description: "Add media metrics for transcoding/editing events."
bug: "297487694"
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index bf39425..40929f7 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -2,6 +2,7 @@
flag {
name: "enable_rlp_callbacks_in_media_router2"
+ is_exported: true
namespace: "media_solutions"
description: "Make RouteListingPreference getter and callbacks public in MediaRouter2."
bug: "281067101"
@@ -16,6 +17,7 @@
flag {
name: "enable_audio_policies_device_and_bluetooth_controller"
+ is_exported: true
namespace: "media_solutions"
description: "Use Audio Policies implementation for device and Bluetooth route controllers."
bug: "280576228"
@@ -44,6 +46,7 @@
flag {
name: "enable_new_media_route_2_info_types"
+ is_exported: true
namespace: "media_solutions"
description: "Enables the following type constants in MediaRoute2Info: CAR, COMPUTER, GAME_CONSOLE, SMARTPHONE, SMARTWATCH, TABLET, TABLET_DOCKED. Note that this doesn't gate any behavior. It only guards some API int symbols."
bug: "301713440"
@@ -51,6 +54,7 @@
flag {
name: "enable_privileged_routing_for_media_routing_control"
+ is_exported: true
namespace: "media_solutions"
description: "Allow access to privileged routing capabilities to MEDIA_ROUTING_CONTROL holders."
bug: "305919655"
@@ -58,6 +62,7 @@
flag {
name: "enable_cross_user_routing_in_media_router2"
+ is_exported: true
namespace: "media_solutions"
description: "Allows clients of privileged MediaRouter2 that hold INTERACT_ACROSS_USERS_FULL to control routing across users."
bug: "288580225"
@@ -72,6 +77,7 @@
flag {
name: "enable_built_in_speaker_route_suitability_statuses"
+ is_exported: true
namespace: "media_solutions"
description: "Make MediaRoute2Info provide information about routes suitability for transfer."
bug: "279555229"
@@ -79,6 +85,7 @@
flag {
name: "enable_notifying_activity_manager_with_media_session_status_change"
+ is_exported: true
namespace: "media_solutions"
description: "Notify ActivityManager with the changes in playback state of the media session."
bug: "295518668"
@@ -86,6 +93,7 @@
flag {
name: "enable_get_transferable_routes"
+ is_exported: true
namespace: "media_solutions"
description: "Exposes RoutingController#getTransferableRoutes() (previously hidden) to the public API."
bug: "323154573"
@@ -100,6 +108,7 @@
flag {
name: "enable_screen_off_scanning"
+ is_exported: true
namespace: "media_solutions"
description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
bug: "281072508"
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index f110705..1731e5e 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -2,6 +2,7 @@
flag {
name: "broadcast_visibility_types"
+ is_exported: true
namespace: "media_tv"
description: "Constants for standardizing broadcast visibility types."
bug: "222402395"
@@ -9,6 +10,7 @@
flag {
name: "enable_ad_service_fw"
+ is_exported: true
namespace: "media_tv"
description: "Enable the TV client-side AD framework."
bug: "303506816"
@@ -16,6 +18,7 @@
flag {
name: "tiaf_v_apis"
+ is_exported: true
namespace: "media_tv"
description: "TIAF V3.0 APIs for Android V"
bug: "303323657"
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index ba084c0..6d4a17c 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -63,6 +63,7 @@
flag {
name: "enable_nfc_charging"
+ is_exported: true
namespace: "nfc"
description: "Flag for NFC charging changes"
bug: "292143899"
diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig
index 572a669..8627eac 100644
--- a/packages/CrashRecovery/aconfig/flags.aconfig
+++ b/packages/CrashRecovery/aconfig/flags.aconfig
@@ -10,6 +10,7 @@
flag {
name: "enable_crashrecovery"
+ is_exported: true
namespace: "crashrecovery"
description: "Enables various dependencies of crashrecovery module"
bug: "289203818"
diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm
index 071f9f4..854c2fd 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_english_uk.kcm
@@ -23,8 +23,8 @@
### ROW 1
key GRAVE {
- label: '`'
- base: '`'
+ label: '\u0300'
+ base: '\u0300'
shift: '\u00AC'
ralt: '\u00A6'
}
@@ -39,6 +39,7 @@
label: '2'
base: '2'
shift: '"'
+ ralt: '\u0308'
}
key 3 {
@@ -64,6 +65,7 @@
label: '6'
base: '6'
shift: '^'
+ ralt: '\u0302'
}
key 7 {
@@ -202,6 +204,7 @@
label: ']'
base: ']'
shift: '}'
+ shift+ralt: '|'
}
### ROW 3
@@ -282,14 +285,16 @@
label: '\''
base: '\''
shift: '@'
+ ralt: '\u0301'
+ shift+ralt: '`'
}
key POUND {
label: '#'
base: '#'
shift: '~'
- ralt: '\\'
- shift+ralt: '|'
+ ralt: '\u0303'
+ shift+ralt: '\\'
}
### ROW 4
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 416b369..baccda7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -163,6 +163,16 @@
default void onAclConnectionStateChanged(
@NonNull CachedBluetoothDevice cachedDevice, int state) {}
+ /**
+ * Called when the Auto-on state is changed for any user. Listens to intent
+ * {@link android.bluetooth.BluetoothAdapter#ACTION_AUTO_ON_STATE_CHANGED }
+ *
+ * @param state the Auto-on state, the possible values are:
+ * {@link android.bluetooth.BluetoothAdapter#AUTO_ON_STATE_ENABLED},
+ * {@link android.bluetooth.BluetoothAdapter#AUTO_ON_STATE_DISABLED}
+ */
+ default void onAutoOnStateChanged(int state) {}
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "STATE_" }, value = {
STATE_DISCONNECTED,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 647fcb9..0996d52 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -133,6 +133,8 @@
addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler());
addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler());
+ addHandler(BluetoothAdapter.ACTION_AUTO_ON_STATE_CHANGED, new AutoOnStateChangedHandler());
+
registerAdapterIntentReceiver();
}
@@ -552,4 +554,21 @@
dispatchAudioModeChanged();
}
}
+
+ private class AutoOnStateChangedHandler implements Handler {
+
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "AutoOnStateChangedHandler() action is null");
+ return;
+ }
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_AUTO_ON_STATE,
+ BluetoothAdapter.ERROR);
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onAutoOnStateChanged(state);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 13635c3..48bbf4e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -489,4 +490,17 @@
verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
eq(R.string.bluetooth_pairing_pin_error_message));
}
+
+ /**
+ * Intent ACTION_AUTO_ON_STATE_CHANGED should dispatch to callback.
+ */
+ @Test
+ public void intentWithExtraState_autoOnStateChangedShouldDispatchToRegisterCallback() {
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothAdapter.ACTION_AUTO_ON_STATE_CHANGED);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback).onAutoOnStateChanged(anyInt());
+ }
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a155dc4..f057acc 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -25,6 +25,16 @@
}
flag {
+ name: "notification_minimalism_prototype"
+ namespace: "systemui"
+ description: "Prototype of notification minimalism; the new 'Intermediate' lockscreen customization proposal."
+ bug: "330387368"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_view_flipper_pausing"
namespace: "systemui"
description: "Pause ViewFlippers inside Notification custom layouts when the shade is closed."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 1da6c1e..0f3d3dc2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -484,6 +484,7 @@
onChangeScene = {},
transitions = SceneTransitions,
modifier = modifier,
+ enableInterruptions = false,
) {
scene(SceneKeys.ContiguousSceneKey) {
FoldableScene(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index d0c4984..a1d8c29 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -71,6 +71,7 @@
currentScene,
onChangeScene = { viewModel.onSceneChanged(it) },
transitions = sceneTransitions,
+ enableInterruptions = false,
)
val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index bc4e555..1178cc8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -74,6 +74,7 @@
transitions =
transitions { sceneKeyByBlueprintId.values.forEach { sceneKey -> to(sceneKey) } },
modifier = modifier,
+ enableInterruptions = false,
) {
sceneKeyByBlueprint.entries.forEach { (blueprint, sceneKey) ->
scene(sceneKey) { with(blueprint) { Content(Modifier.fillMaxSize()) } }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
index d9ed497..a12f099 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
@@ -80,5 +80,14 @@
object ClockElementKeys {
val largeClockElementKey = ElementKey("large-clock")
val smallClockElementKey = ElementKey("small-clock")
+ val weatherSmallClockElementKey = ElementKey("weather-small-clock")
val smartspaceElementKey = ElementKey("smart-space")
}
+
+object WeatherClockElementKeys {
+ val timeElementKey = ElementKey("weather-large-clock-time")
+ val dateElementKey = ElementKey("weather-large-clock-date")
+ val weatherIconElementKey = ElementKey("weather-large-clock-weather-icon")
+ val temperatureElementKey = ElementKey("weather-large-clock-temperature")
+ val dndAlarmElementKey = ElementKey("weather-large-clock-dnd-alarm")
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index ee4e2d6..fe774a0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -23,6 +23,8 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
@@ -33,11 +35,14 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
+import com.android.keyguard.KeyguardClockSwitch.LARGE
import com.android.systemui.Flags
+import com.android.systemui.customization.R as customizationR
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
@@ -47,8 +52,8 @@
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import dagger.Binds
@@ -71,6 +76,7 @@
private val settingsMenuSection: SettingsMenuSection,
private val clockInteractor: KeyguardClockInteractor,
private val mediaCarouselSection: MediaCarouselSection,
+ private val clockViewModel: KeyguardClockViewModel,
) : ComposableLockscreenSceneBlueprint {
override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
@@ -79,7 +85,7 @@
val isUdfpsVisible = viewModel.isUdfpsVisible
val burnIn = rememberBurnIn(clockInteractor)
val resources = LocalContext.current.resources
-
+ val currentClockState = clockViewModel.currentClock.collectAsState()
LockscreenLongPress(
viewModel = viewModel.longPress,
modifier = modifier,
@@ -91,7 +97,34 @@
modifier = Modifier.fillMaxWidth(),
) {
with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- // TODO: Add weather clock for small and large clock
+ val currentClock = currentClockState.value
+ val clockSize by clockViewModel.clockSize.collectAsState()
+ with(weatherClockSection) {
+ if (currentClock == null) {
+ return@with
+ }
+
+ if (clockSize == LARGE) {
+ Time(
+ clock = currentClock,
+ modifier =
+ Modifier.padding(
+ start =
+ dimensionResource(
+ customizationR.dimen.clock_padding_start
+ )
+ )
+ )
+ } else {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ modifier =
+ Modifier.align(Alignment.Start)
+ .onTopPlacementChanged(burnIn.onSmallClockTopChanged),
+ clock = currentClock
+ )
+ }
+ }
with(smartSpaceSection) {
SmartSpace(
burnInParams = burnIn.parameters,
@@ -119,6 +152,12 @@
)
}
}
+ with(weatherClockSection) {
+ if (currentClock == null || clockSize != LARGE) {
+ return@with
+ }
+ LargeClockSectionBelowSmartspace(clock = currentClock)
+ }
if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
with(ambientIndicationSectionOptional.get()) {
@@ -234,6 +273,7 @@
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val weatherClockSection: WeatherClockSection,
private val mediaCarouselSection: MediaCarouselSection,
+ private val clockViewModel: KeyguardClockViewModel,
) : ComposableLockscreenSceneBlueprint {
override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
@@ -242,7 +282,7 @@
val isUdfpsVisible = viewModel.isUdfpsVisible
val burnIn = rememberBurnIn(clockInteractor)
val resources = LocalContext.current.resources
-
+ val currentClockState = clockViewModel.currentClock.collectAsState()
LockscreenLongPress(
viewModel = viewModel.longPress,
modifier = modifier,
@@ -257,11 +297,42 @@
Row(
modifier = Modifier.fillMaxSize(),
) {
- // TODO: Add weather clock for small and large clock
Column(
modifier = Modifier.fillMaxHeight().weight(weight = 1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ val currentClock = currentClockState.value
+ val clockSize by clockViewModel.clockSize.collectAsState()
+ with(weatherClockSection) {
+ if (currentClock == null) {
+ return@with
+ }
+
+ if (clockSize == LARGE) {
+ Time(
+ clock = currentClock,
+ modifier =
+ Modifier.align(Alignment.Start)
+ .padding(
+ start =
+ dimensionResource(
+ customizationR.dimen
+ .clock_padding_start
+ )
+ )
+ )
+ } else {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ modifier =
+ Modifier.align(Alignment.Start)
+ .onTopPlacementChanged(
+ burnIn.onSmallClockTopChanged
+ ),
+ clock = currentClock,
+ )
+ }
+ }
with(smartSpaceSection) {
SmartSpace(
burnInParams = burnIn.parameters,
@@ -284,6 +355,14 @@
}
with(mediaCarouselSection) { MediaCarousel() }
+
+ with(weatherClockSection) {
+ if (currentClock == null || clockSize != LARGE) {
+ return@with
+ }
+
+ LargeClockSectionBelowSmartspace(currentClock)
+ }
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 1d86b15..2781f39 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -20,6 +20,7 @@
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -32,6 +33,7 @@
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
import com.android.systemui.customization.R as customizationR
+import com.android.systemui.customization.R
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
@@ -58,19 +60,21 @@
if (currentClock?.smallClock?.view == null) {
return
}
-
val context = LocalContext.current
MovableElement(key = smallClockElementKey, modifier = modifier) {
content {
AndroidView(
factory = { context ->
FrameLayout(context).apply {
- addClockView(checkNotNull(currentClock).smallClock.view)
+ ensureClockViewExists(checkNotNull(currentClock).smallClock.view)
}
},
- update = { it.addClockView(checkNotNull(currentClock).smallClock.view) },
+ update = {
+ it.ensureClockViewExists(checkNotNull(currentClock).smallClock.view)
+ },
modifier =
- Modifier.padding(
+ Modifier.height(dimensionResource(R.dimen.small_clock_height))
+ .padding(
horizontal =
dimensionResource(customizationR.dimen.clock_padding_start)
)
@@ -91,23 +95,24 @@
if (currentClock?.largeClock?.view == null) {
return
}
-
MovableElement(key = largeClockElementKey, modifier = modifier) {
content {
AndroidView(
factory = { context ->
FrameLayout(context).apply {
- addClockView(checkNotNull(currentClock).largeClock.view)
+ ensureClockViewExists(checkNotNull(currentClock).largeClock.view)
}
},
- update = { it.addClockView(checkNotNull(currentClock).largeClock.view) },
+ update = {
+ it.ensureClockViewExists(checkNotNull(currentClock).largeClock.view)
+ },
modifier = Modifier.fillMaxSize()
)
}
}
}
- private fun FrameLayout.addClockView(clockView: View) {
+ private fun FrameLayout.ensureClockViewExists(clockView: View) {
if (contains(clockView)) {
return
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 7635841..d72d5ca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -92,6 +92,7 @@
currentScene = currentScene,
onChangeScene = {},
transitions = ClockTransition.defaultClockTransitions,
+ enableInterruptions = false,
) {
scene(ClockScenes.splitShadeLargeClockScene) {
Row(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index 2e7bc2a..d358453 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -16,45 +16,177 @@
package com.android.systemui.keyguard.ui.composable.section
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
+import com.android.systemui.customization.R
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.weatherSmallClockElementKey
+import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys
+import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
+import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.plugins.clocks.ClockController
import javax.inject.Inject
/** Provides small clock and large clock composables for the weather clock layout. */
-class WeatherClockSection @Inject constructor() {
+class WeatherClockSection
+@Inject
+constructor(
+ private val viewModel: KeyguardClockViewModel,
+ private val aodBurnInViewModel: AodBurnInViewModel,
+) {
@Composable
fun SceneScope.Time(
+ clock: ClockController,
modifier: Modifier = Modifier,
) {
- // TODO: compose view
+ WeatherElement(
+ weatherClockElementViewId = R.id.weather_clock_time,
+ clock = clock,
+ elementKey = WeatherClockElementKeys.timeElementKey,
+ modifier = modifier.wrapContentSize(),
+ )
}
@Composable
- fun SceneScope.Date(
+ private fun SceneScope.Date(
+ clock: ClockController,
modifier: Modifier = Modifier,
) {
- // TODO: compose view
+ WeatherElement(
+ weatherClockElementViewId = R.id.weather_clock_date,
+ clock = clock,
+ elementKey = WeatherClockElementKeys.dateElementKey,
+ modifier = modifier,
+ )
}
@Composable
- fun SceneScope.Weather(
+ private fun SceneScope.Weather(
+ clock: ClockController,
modifier: Modifier = Modifier,
) {
- // TODO: compose view
+ WeatherElement(
+ weatherClockElementViewId = R.id.weather_clock_weather_icon,
+ clock = clock,
+ elementKey = WeatherClockElementKeys.weatherIconElementKey,
+ modifier = modifier.wrapContentSize(),
+ )
}
@Composable
- fun SceneScope.DndAlarmStatus(
+ private fun SceneScope.DndAlarmStatus(
+ clock: ClockController,
modifier: Modifier = Modifier,
) {
- // TODO: compose view
+ WeatherElement(
+ weatherClockElementViewId = R.id.weather_clock_alarm_dnd,
+ clock = clock,
+ elementKey = WeatherClockElementKeys.dndAlarmElementKey,
+ modifier = modifier.wrapContentSize(),
+ )
}
@Composable
- fun SceneScope.Temperature(
+ private fun SceneScope.Temperature(
+ clock: ClockController,
modifier: Modifier = Modifier,
) {
- // TODO: compose view
+ WeatherElement(
+ weatherClockElementViewId = R.id.weather_clock_temperature,
+ clock = clock,
+ elementKey = WeatherClockElementKeys.temperatureElementKey,
+ modifier = modifier.wrapContentSize(),
+ )
+ }
+
+ @Composable
+ private fun SceneScope.WeatherElement(
+ weatherClockElementViewId: Int,
+ clock: ClockController,
+ elementKey: ElementKey,
+ modifier: Modifier
+ ) {
+ MovableElement(key = elementKey, modifier) {
+ content {
+ AndroidView(
+ factory = {
+ val view =
+ clock.largeClock.layout.views.first {
+ it.id == weatherClockElementViewId
+ }
+ (view.parent as? ViewGroup)?.removeView(view)
+ view
+ },
+ update = {},
+ modifier = modifier
+ )
+ }
+ }
+ }
+
+ @Composable
+ fun SceneScope.LargeClockSectionBelowSmartspace(
+ clock: ClockController,
+ ) {
+ Row(
+ modifier =
+ Modifier.height(IntrinsicSize.Max)
+ .padding(horizontal = dimensionResource(R.dimen.clock_padding_start))
+ ) {
+ Date(clock = clock, modifier = Modifier.wrapContentSize())
+ Box(modifier = Modifier.fillMaxSize()) {
+ Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart))
+ Temperature(clock = clock, modifier = Modifier.align(Alignment.BottomEnd))
+ DndAlarmStatus(clock = clock, modifier = Modifier.align(Alignment.TopEnd))
+ }
+ }
+ }
+
+ @Composable
+ fun SceneScope.SmallClock(
+ burnInParams: BurnInParameters,
+ modifier: Modifier = Modifier,
+ clock: ClockController,
+ ) {
+ val localContext = LocalContext.current
+ MovableElement(key = weatherSmallClockElementKey, modifier) {
+ content {
+ AndroidView(
+ factory = {
+ val view = clock.smallClock.view
+ if (view.parent != null) {
+ (view.parent as? ViewGroup)?.removeView(view)
+ }
+ view
+ },
+ modifier =
+ modifier
+ .height(dimensionResource(R.dimen.small_clock_height))
+ .padding(start = dimensionResource(R.dimen.clock_padding_start))
+ .padding(top = { viewModel.getSmallClockTopMargin(localContext) })
+ .burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ update = {},
+ )
+ }
+ }
}
}
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 0fdaabe..fe6701c 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
@@ -79,6 +79,7 @@
initialScene = currentSceneKey,
canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
transitions = SceneContainerTransitions,
+ enableInterruptions = false,
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 6cff30c..da07f6d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -138,8 +138,9 @@
// that will actually animate it.
layoutState.startTransition(transition, transitionKey)
- // The transformation now contains the spec that we should use to instantiate the Animatable.
- val animationSpec = layoutState.transformationSpec.progressSpec
+ // The transition now contains the transformation spec that we should use to instantiate the
+ // Animatable.
+ val animationSpec = transition.transformationSpec.progressSpec
val visibilityThreshold =
(animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
val animatable =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 82083f9..1b06275 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -18,10 +18,8 @@
package com.android.compose.animation.scene
-import android.util.Log
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
@@ -145,16 +143,6 @@
}
val transitionState = layoutImpl.state.transitionState
- if (transitionState is TransitionState.Transition) {
- // TODO(b/290184746): Better handle interruptions here if state != idle.
- Log.w(
- TAG,
- "start from TransitionState.Transition is not fully supported: from" +
- " ${transitionState.fromScene} to ${transitionState.toScene} " +
- "(progress ${transitionState.progress})"
- )
- }
-
val fromScene = layoutImpl.scene(transitionState.currentScene)
val swipes = computeSwipes(fromScene, startedPosition, pointersDown)
val result =
@@ -269,19 +257,6 @@
fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) {
if (isDrivingTransition || force) {
layoutState.startTransition(newTransition, newTransition.key)
-
- // Initialize SwipeTransition.transformationSpec and .swipeSpec. Note that this must be
- // called right after layoutState.startTransition() is called, because it computes the
- // current layoutState.transformationSpec().
- val transformationSpec = layoutState.transformationSpec
- newTransition.transformationSpec = transformationSpec
- newTransition.swipeSpec =
- transformationSpec.swipeSpec ?: layoutState.transitions.defaultSwipeSpec
- } else {
- // We were not driving the transition and we don't force the update, so the specs won't
- // be used and it doesn't matter which ones we set here.
- newTransition.transformationSpec = TransformationSpec.Empty
- newTransition.swipeSpec = SceneTransitions.DefaultSwipeSpec
}
swipeTransition = newTransition
@@ -616,18 +591,6 @@
override val isUserInputOngoing: Boolean
get() = offsetAnimation == null
- /**
- * The [TransformationSpecImpl] associated to this transition.
- *
- * Note: This is lateinit because this [SwipeTransition] is needed by
- * [BaseSceneTransitionLayoutState] to compute the [TransitionSpec], and it will be set right
- * after [BaseSceneTransitionLayoutState.startTransition] is called with this transition.
- */
- lateinit var transformationSpec: TransformationSpecImpl
-
- /** The spec to use when animating this transition to either [fromScene] or [toScene]. */
- lateinit var swipeSpec: SpringSpec<Float>
-
override val overscrollScope: OverscrollScope =
object : OverscrollScope {
override val absoluteDistance: Float
@@ -701,6 +664,9 @@
coroutineScope
.launch {
try {
+ val swipeSpec =
+ transformationSpec.swipeSpec
+ ?: layoutState.transitions.defaultSwipeSpec
animatable.animateTo(
targetValue = targetOffset,
animationSpec = swipeSpec,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 15712b5..69f1d45 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -203,7 +203,7 @@
measurable: Measurable,
constraints: Constraints,
): MeasureResult {
- val overscrollScene = layoutImpl.state.currentOverscrollSpec?.scene
+ val overscrollScene = layoutImpl.state.currentTransition?.currentOverscrollSpec?.scene
if (overscrollScene != null && overscrollScene != scene.key) {
// There is an overscroll in progress on another scene
// By measuring composable elements, Compose can cache relevant information.
@@ -269,13 +269,12 @@
transition == null ||
transition.fromScene !in element.sceneStates ||
transition.toScene !in element.sceneStates ||
- layoutImpl.state.currentOverscrollSpec?.scene == scene.key
+ transition.currentOverscrollSpec?.scene == scene.key
) {
return true
}
- val sharedTransformation =
- sharedElementTransformation(layoutImpl.state, transition, element.key)
+ val sharedTransformation = sharedElementTransformation(transition, element.key)
if (sharedTransformation?.enabled == false) {
return true
}
@@ -305,23 +304,21 @@
fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
) == scene
- return chosenByPicker || layoutImpl.state.currentOverscrollSpec?.scene == scene
+ return chosenByPicker || transition.currentOverscrollSpec?.scene == scene
}
private fun isSharedElementEnabled(
- layoutState: BaseSceneTransitionLayoutState,
transition: TransitionState.Transition,
element: ElementKey,
): Boolean {
- return sharedElementTransformation(layoutState, transition, element)?.enabled ?: true
+ return sharedElementTransformation(transition, element)?.enabled ?: true
}
internal fun sharedElementTransformation(
- layoutState: BaseSceneTransitionLayoutState,
transition: TransitionState.Transition,
element: ElementKey,
): SharedElementTransformation? {
- val transformationSpec = layoutState.transformationSpec
+ val transformationSpec = transition.transformationSpec
val sharedInFromScene = transformationSpec.transformations(element, transition.fromScene).shared
val sharedInToScene = transformationSpec.transformations(element, transition.toScene).shared
@@ -360,11 +357,11 @@
}
val isSharedElement = fromState != null && toState != null
- if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) {
+ if (isSharedElement && isSharedElementEnabled(transition, element.key)) {
return true
}
- return layoutImpl.state.transformationSpec.transformations(element.key, scene.key).alpha == null
+ return transition.transformationSpec.transformations(element.key, scene.key).alpha == null
}
/**
@@ -559,7 +556,7 @@
}
if (transition is TransitionState.HasOverscrollProperties) {
- val overscroll = layoutImpl.state.currentOverscrollSpec
+ val overscroll = transition.currentOverscrollSpec
if (overscroll?.scene == scene.key) {
val elementSpec = overscroll.transformationSpec.transformations(element.key, scene.key)
val propertySpec = transformation(elementSpec) ?: return currentValue()
@@ -597,7 +594,7 @@
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
val isSharedElement = fromState != null && toState != null
- if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) {
+ if (isSharedElement && isSharedElementEnabled(transition, element.key)) {
val start = sceneValue(fromState!!)
val end = sceneValue(toState!!)
@@ -607,7 +604,7 @@
}
val transformation =
- transformation(layoutImpl.state.transformationSpec.transformations(element.key, scene.key))
+ transformation(transition.transformationSpec.transformations(element.key, scene.key))
// If there is no transformation explicitly associated to this element value, let's use
// the value given by the system (like the current position and size given by the layout
// pass).
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index ebc9099..c7c874c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -96,9 +96,17 @@
modifier: Modifier = Modifier,
swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector,
@FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f,
+ enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
scenes: SceneTransitionLayoutScope.() -> Unit,
) {
- val state = updateSceneTransitionLayoutState(currentScene, onChangeScene, transitions)
+ val state =
+ updateSceneTransitionLayoutState(
+ currentScene,
+ onChangeScene,
+ transitions,
+ enableInterruptions = enableInterruptions,
+ )
+
SceneTransitionLayout(
state,
modifier,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 617a8ea..f13c016 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -16,15 +16,16 @@
package com.android.compose.animation.scene
+import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.SnapshotStateList
+import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastFilter
import androidx.compose.ui.util.fastForEach
import com.android.compose.animation.scene.transition.link.LinkedTransition
@@ -50,10 +51,21 @@
*/
val transitionState: TransitionState
- /** The current transition, or `null` if we are idle. */
+ /**
+ * The current transition, or `null` if we are idle.
+ *
+ * Note: If you need to handle interruptions and multiple transitions running in parallel, use
+ * [currentTransitions] instead.
+ */
val currentTransition: TransitionState.Transition?
get() = transitionState as? TransitionState.Transition
+ /**
+ * The list of [TransitionState.Transition] currently running. This will be the empty list if we
+ * are idle.
+ */
+ val currentTransitions: List<TransitionState.Transition>
+
/** The [SceneTransitions] used when animating this state. */
val transitions: SceneTransitions
@@ -120,12 +132,14 @@
transitions: SceneTransitions = SceneTransitions.Empty,
canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
+ enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
): MutableSceneTransitionLayoutState {
return MutableSceneTransitionLayoutStateImpl(
initialScene,
transitions,
canChangeScene,
stateLinks,
+ enableInterruptions,
)
}
@@ -154,6 +168,7 @@
transitions: SceneTransitions = SceneTransitions.Empty,
canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
+ enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
): SceneTransitionLayoutState {
return remember {
HoistedSceneTransitionLayoutState(
@@ -162,9 +177,19 @@
onChangeScene,
canChangeScene,
stateLinks,
+ enableInterruptions,
)
}
- .apply { update(currentScene, onChangeScene, canChangeScene, transitions, stateLinks) }
+ .apply {
+ update(
+ currentScene,
+ onChangeScene,
+ canChangeScene,
+ transitions,
+ stateLinks,
+ enableInterruptions,
+ )
+ }
}
@Stable
@@ -204,6 +229,30 @@
/** Whether user input is currently driving the transition. */
abstract val isUserInputOngoing: Boolean
+ /**
+ * The current [TransformationSpecImpl] and [OverscrollSpecImpl] associated to this
+ * transition.
+ *
+ * Important: These will be set exactly once, when this transition is
+ * [started][BaseSceneTransitionLayoutState.startTransition].
+ */
+ internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
+ private var fromOverscrollSpec: OverscrollSpecImpl? = null
+ private var toOverscrollSpec: OverscrollSpecImpl? = null
+
+ /** The current [OverscrollSpecImpl], if this transition is currently overscrolling. */
+ internal val currentOverscrollSpec: OverscrollSpecImpl?
+ get() {
+ if (this !is HasOverscrollProperties) return null
+ val progress = progress
+ val bouncingScene = bouncingScene
+ return when {
+ progress < 0f || bouncingScene == fromScene -> fromOverscrollSpec
+ progress > 1f || bouncingScene == toScene -> toOverscrollSpec
+ else -> null
+ }
+ }
+
init {
check(fromScene != toScene)
}
@@ -232,6 +281,14 @@
return isTransitioning(from = scene, to = other) ||
isTransitioning(from = other, to = scene)
}
+
+ internal fun updateOverscrollSpecs(
+ fromSpec: OverscrollSpecImpl?,
+ toSpec: OverscrollSpecImpl?,
+ ) {
+ fromOverscrollSpec = fromSpec
+ toOverscrollSpec = toSpec
+ }
}
interface HasOverscrollProperties {
@@ -270,38 +327,41 @@
internal abstract class BaseSceneTransitionLayoutState(
initialScene: SceneKey,
protected var stateLinks: List<StateLink>,
+
+ // TODO(b/290930950): Remove this flag.
+ internal var enableInterruptions: Boolean,
) : SceneTransitionLayoutState {
- override var transitionState: TransitionState by
- mutableStateOf(TransitionState.Idle(initialScene))
- protected set
-
/**
- * The current [transformationSpec] associated to [transitionState]. Accessing this value makes
- * sense only if [transitionState] is a [TransitionState.Transition].
+ * The current [TransitionState]. This list will either be:
+ * 1. A list with a single [TransitionState.Idle] element, when we are idle.
+ * 2. A list with one or more [TransitionState.Transition], when we are transitioning.
*/
- internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
+ @VisibleForTesting
+ internal val transitionStates: MutableList<TransitionState> =
+ SnapshotStateList<TransitionState>().apply { add(TransitionState.Idle(initialScene)) }
- private var fromOverscrollSpec: OverscrollSpecImpl? = null
- private var toOverscrollSpec: OverscrollSpecImpl? = null
+ override val transitionState: TransitionState
+ get() = transitionStates.last()
- /**
- * @return the overscroll [OverscrollSpecImpl] if it is defined for the current
- * [transitionState] and we are currently over scrolling.
- */
- internal val currentOverscrollSpec: OverscrollSpecImpl?
+ private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+
+ override val currentTransitions: List<TransitionState.Transition>
get() {
- val transition = currentTransition ?: return null
- if (transition !is TransitionState.HasOverscrollProperties) return null
- val progress = transition.progress
- val bouncingScene = transition.bouncingScene
- return when {
- progress < 0f || bouncingScene == transition.fromScene -> fromOverscrollSpec
- progress > 1f || bouncingScene == transition.toScene -> toOverscrollSpec
- else -> null
+ if (transitionStates.last() is TransitionState.Idle) {
+ check(transitionStates.size == 1)
+ return emptyList()
+ } else {
+ @Suppress("UNCHECKED_CAST")
+ return transitionStates as List<TransitionState.Transition>
}
}
- private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+ /**
+ * The mapping of transitions that are finished, i.e. for which [finishTransition] was called,
+ * to their idle scene.
+ */
+ @VisibleForTesting
+ internal val finishedTransitions = mutableMapOf<TransitionState.Transition, SceneKey>()
/** Whether we can transition to the given [scene]. */
internal abstract fun canChangeScene(scene: SceneKey): Boolean
@@ -324,7 +384,11 @@
return transition.isTransitioningBetween(scene, other)
}
- /** Start a new [transition], instantly interrupting any ongoing transition if there was one. */
+ /**
+ * Start a new [transition], instantly interrupting any ongoing transition if there was one.
+ *
+ * Important: you *must* call [finishTransition] once the transition is finished.
+ */
internal fun startTransition(
transition: TransitionState.Transition,
transitionKey: TransitionKey?,
@@ -333,13 +397,81 @@
val fromScene = transition.fromScene
val toScene = transition.toScene
val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation
- transformationSpec =
+
+ // Update the transition specs.
+ transition.transformationSpec =
transitions.transitionSpec(fromScene, toScene, key = transitionKey).transformationSpec()
- fromOverscrollSpec = orientation?.let { transitions.overscrollSpec(fromScene, it) }
- toOverscrollSpec = orientation?.let { transitions.overscrollSpec(toScene, it) }
+ if (orientation != null) {
+ transition.updateOverscrollSpecs(
+ fromSpec = transitions.overscrollSpec(fromScene, orientation),
+ toSpec = transitions.overscrollSpec(toScene, orientation),
+ )
+ } else {
+ transition.updateOverscrollSpecs(fromSpec = null, toSpec = null)
+ }
+
+ // Handle transition links.
cancelActiveTransitionLinks()
setupTransitionLinks(transition)
- transitionState = transition
+
+ if (!enableInterruptions) {
+ // Set the current transition.
+ check(transitionStates.size == 1)
+ transitionStates[0] = transition
+ return
+ }
+
+ when (val currentState = transitionStates.last()) {
+ is TransitionState.Idle -> {
+ // Replace [Idle] by [transition].
+ check(transitionStates.size == 1)
+ transitionStates[0] = transition
+ }
+ is TransitionState.Transition -> {
+ // Force the current transition to finish to currentScene.
+ currentState.finish().invokeOnCompletion {
+ // Make sure [finishTransition] is called at the end of the transition.
+ finishTransition(currentState, currentState.currentScene)
+ }
+
+ // Check that we don't have too many concurrent transitions.
+ if (transitionStates.size >= MAX_CONCURRENT_TRANSITIONS) {
+ Log.wtf(
+ TAG,
+ buildString {
+ appendLine("Potential leak detected in SceneTransitionLayoutState!")
+ appendLine(
+ " Some transition(s) never called STLState.finishTransition()."
+ )
+ appendLine(" Transitions (size=${transitionStates.size}):")
+ transitionStates.fastForEach { state ->
+ val transition = state as TransitionState.Transition
+ val from = transition.fromScene
+ val to = transition.toScene
+ val indicator =
+ if (finishedTransitions.contains(transition)) "x" else " "
+ appendLine(" [$indicator] $from => $to ($transition)")
+ }
+ }
+ )
+
+ // Force finish all transitions.
+ while (currentTransitions.isNotEmpty()) {
+ val transition = transitionStates[0] as TransitionState.Transition
+ finishTransition(transition, transition.currentScene)
+ }
+
+ // We finished all transitions, so we are now idle. We remove this state so that
+ // we end up only with the new transition after appending it.
+ check(transitionStates.size == 1)
+ check(transitionStates[0] is TransitionState.Idle)
+ transitionStates.clear()
+ }
+
+ // Append the new transition.
+ transitionStates.add(transition)
+ }
+ }
}
private fun cancelActiveTransitionLinks() {
@@ -379,13 +511,54 @@
* nothing if [transition] was interrupted since it was started.
*/
internal fun finishTransition(transition: TransitionState.Transition, idleScene: SceneKey) {
- resolveActiveTransitionLinks(idleScene)
- if (transitionState == transition) {
- transitionState = TransitionState.Idle(idleScene)
+ val existingIdleScene = finishedTransitions[transition]
+ if (existingIdleScene != null) {
+ // This transition was already finished.
+ check(idleScene == existingIdleScene) {
+ "Transition $transition was finished multiple times with different " +
+ "idleScene ($existingIdleScene != $idleScene)"
+ }
+ return
+ }
+
+ if (!transitionStates.contains(transition)) {
+ // This transition was already removed from transitionStates.
+ return
+ }
+
+ check(transitionStates.fastAll { it is TransitionState.Transition })
+
+ // Mark this transition as finished and save the scene it is settling at.
+ finishedTransitions[transition] = idleScene
+
+ // Finish all linked transitions.
+ finishActiveTransitionLinks(idleScene)
+
+ // Keep a reference to the idle scene of the last removed transition, in case we remove all
+ // transitions and should settle to Idle.
+ var lastRemovedIdleScene: SceneKey? = null
+
+ // Remove all first n finished transitions.
+ while (transitionStates.isNotEmpty()) {
+ val firstTransition = transitionStates[0]
+ if (!finishedTransitions.contains(firstTransition)) {
+ // Stop here.
+ break
+ }
+
+ // Remove the transition from the list and from the set of finished transitions.
+ transitionStates.removeAt(0)
+ lastRemovedIdleScene = finishedTransitions.remove(firstTransition)
+ }
+
+ // If all transitions are finished, we are idle.
+ if (transitionStates.isEmpty()) {
+ check(finishedTransitions.isEmpty())
+ transitionStates.add(TransitionState.Idle(checkNotNull(lastRemovedIdleScene)))
}
}
- private fun resolveActiveTransitionLinks(idleScene: SceneKey) {
+ private fun finishActiveTransitionLinks(idleScene: SceneKey) {
val previousTransition = this.transitionState as? TransitionState.Transition ?: return
for ((link, linkedTransition) in activeTransitionLinks) {
if (previousTransition.fromScene == idleScene) {
@@ -406,20 +579,39 @@
* Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap
* to the closest scene.
*
+ * Important: Snapping to the closest scene will instantly finish *all* ongoing transitions,
+ * only the progress of the last transition will be checked.
+ *
* @return true if snapped to the closest scene.
*/
internal fun snapToIdleIfClose(threshold: Float): Boolean {
val transition = currentTransition ?: return false
val progress = transition.progress
+
fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold
+ fun finishAllTransitions(lastTransitionIdleScene: SceneKey) {
+ // Force finish all transitions.
+ while (currentTransitions.isNotEmpty()) {
+ val transition = transitionStates[0] as TransitionState.Transition
+ val idleScene =
+ if (transitionStates.size == 1) {
+ lastTransitionIdleScene
+ } else {
+ transition.currentScene
+ }
+
+ finishTransition(transition, idleScene)
+ }
+ }
+
return when {
isProgressCloseTo(0f) -> {
- finishTransition(transition, transition.fromScene)
+ finishAllTransitions(transition.fromScene)
true
}
isProgressCloseTo(1f) -> {
- finishTransition(transition, transition.toScene)
+ finishAllTransitions(transition.toScene)
true
}
else -> false
@@ -437,7 +629,8 @@
private var changeScene: (SceneKey) -> Unit,
private var canChangeScene: (SceneKey) -> Boolean,
stateLinks: List<StateLink> = emptyList(),
-) : BaseSceneTransitionLayoutState(initialScene, stateLinks) {
+ enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
+) : BaseSceneTransitionLayoutState(initialScene, stateLinks, enableInterruptions) {
private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED)
override fun canChangeScene(scene: SceneKey): Boolean = canChangeScene.invoke(scene)
@@ -451,12 +644,14 @@
canChangeScene: (SceneKey) -> Boolean,
transitions: SceneTransitions,
stateLinks: List<StateLink>,
+ enableInterruptions: Boolean,
) {
SideEffect {
this.changeScene = onChangeScene
this.canChangeScene = canChangeScene
this.transitions = transitions
this.stateLinks = stateLinks
+ this.enableInterruptions = enableInterruptions
targetSceneChannel.trySend(currentScene)
}
@@ -482,7 +677,10 @@
override var transitions: SceneTransitions,
private val canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
-) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene, stateLinks) {
+ enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
+) :
+ MutableSceneTransitionLayoutState,
+ BaseSceneTransitionLayoutState(initialScene, stateLinks, enableInterruptions) {
override fun setTargetScene(
targetScene: SceneKey,
coroutineScope: CoroutineScope,
@@ -501,3 +699,15 @@
setTargetScene(scene, coroutineScope = this)
}
}
+
+private const val TAG = "SceneTransitionLayoutState"
+
+/** Whether support for interruptions in enabled by default. */
+internal const val DEFAULT_INTERRUPTIONS_ENABLED = true
+
+/**
+ * The max number of concurrent transitions. If the number of transitions goes past this number,
+ * this probably means that there is a leak and we will Log.wtf before clearing the list of
+ * transitions.
+ */
+private const val MAX_CONCURRENT_TRANSITIONS = 100
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 59cc63a..af13896 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -26,7 +26,6 @@
name: "PlatformComposeSceneTransitionLayoutTests",
manifest: "AndroidManifest.xml",
test_suites: ["device-tests"],
- sdk_version: "current",
certificate: "platform",
srcs: [
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 1e9a7e2..2ed51eb 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -984,14 +984,14 @@
val scene = layoutState.transitionState.currentScene
// We should have overscroll spec for scene C
assertThat(layoutState.transitions.overscrollSpec(scene, Orientation.Vertical)).isNotNull()
- assertThat(layoutState.currentOverscrollSpec).isNull()
+ assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNull()
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f))
// We scrolled down, under scene C there is nothing, so we can use the overscroll spec
- assertThat(layoutState.currentOverscrollSpec).isNotNull()
- assertThat(layoutState.currentOverscrollSpec?.scene).isEqualTo(SceneC)
+ assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNotNull()
+ assertThat(layoutState.currentTransition?.currentOverscrollSpec?.scene).isEqualTo(SceneC)
val transition = layoutState.currentTransition
assertThat(transition).isNotNull()
assertThat(transition!!.progress).isEqualTo(-0.1f)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 597da9e..2453e25 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -595,7 +595,7 @@
}
assertThat(state.currentTransition).isNull()
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// Swipe by half of verticalSwipeDistance.
rule.onRoot().performTouchInput {
@@ -643,7 +643,7 @@
// Scroll 150% (Scene B overscroll by 50%)
assertThat(transition.progress).isEqualTo(1.5f)
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
// animatedFloat cannot overflow (canOverflow = false)
assertThat(animatedFloat).isEqualTo(100f)
@@ -655,7 +655,7 @@
// Scroll 250% (Scene B overscroll by 150%)
assertThat(transition.progress).isEqualTo(2.5f)
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
assertThat(animatedFloat).isEqualTo(100f)
}
@@ -707,7 +707,7 @@
}
assertThat(state.currentTransition).isNull()
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
fooElement.assertTopPositionInRootIsEqualTo(0.dp)
@@ -720,7 +720,7 @@
}
val transition = state.currentTransition
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
assertThat(transition).isNotNull()
assertThat(transition!!.progress).isEqualTo(-0.5f)
fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
@@ -732,7 +732,7 @@
// Scroll 150% (Scene B overscroll by 50%)
assertThat(transition.progress).isEqualTo(-1.5f)
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f)
}
@@ -771,7 +771,7 @@
// Scroll 150% (100% scroll + 50% overscroll)
assertThat(transition!!.progress).isEqualTo(1.5f)
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f)
assertThat(animatedFloat).isEqualTo(100f)
@@ -782,7 +782,7 @@
// Scroll 250% (100% scroll + 150% overscroll)
assertThat(transition.progress).isEqualTo(2.5f)
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 1.5f)
assertThat(animatedFloat).isEqualTo(100f)
}
@@ -828,7 +828,7 @@
// Scroll 150% (100% scroll + 50% overscroll)
assertThat(transition.progress).isEqualTo(1.5f)
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * (transition.progress - 1f))
assertThat(animatedFloat).isEqualTo(100f)
@@ -840,7 +840,7 @@
rule.waitUntil(timeoutMillis = 10_000) { transition.progress < 1f }
assertThat(transition.progress).isLessThan(1f)
- assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
assertThat(transition.bouncingScene).isEqualTo(transition.toScene)
assertThat(animatedFloat).isEqualTo(100f)
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 9baabc3..93e94f8 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import android.util.Log
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.junit4.createComposeRule
@@ -28,9 +29,12 @@
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
-import kotlinx.coroutines.job
import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -271,11 +275,21 @@
}
@Test
- fun linkedTransition_startsLinkButLinkedStateIsTakenOver() {
+ fun linkedTransition_startsLinkButLinkedStateIsTakenOver() = runTest {
val (parentState, childState) = setupLinkedStates()
- val childTransition = transition(SceneA, SceneB)
- val parentTransition = transition(SceneC, SceneA)
+ val childTransition =
+ transition(
+ SceneA,
+ SceneB,
+ onFinish = { launch { /* Do nothing. */} },
+ )
+ val parentTransition =
+ transition(
+ SceneC,
+ SceneA,
+ onFinish = { launch { /* Do nothing. */} },
+ )
childState.startTransition(childTransition, null)
parentState.startTransition(parentTransition, null)
@@ -303,7 +317,7 @@
// Default transition from A to B.
assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
- assertThat(state.transformationSpec.transformations).hasSize(1)
+ assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(1)
// Go back to A.
state.setTargetScene(SceneA, coroutineScope = this)
@@ -320,14 +334,14 @@
)
)
.isNotNull()
- assertThat(state.transformationSpec.transformations).hasSize(2)
+ assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(2)
}
@Test
fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
state.startTransition(
- transition(from = SceneA, to = TestScenes.SceneB, progress = { 0.2f }),
+ transition(from = SceneA, to = SceneB, progress = { 0.2f }),
transitionKey = null
)
assertThat(state.isTransitioning()).isTrue()
@@ -346,7 +360,7 @@
fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
state.startTransition(
- transition(from = SceneA, to = TestScenes.SceneB, progress = { 0.8f }),
+ transition(from = SceneA, to = SceneB, progress = { 0.8f }),
transitionKey = null
)
assertThat(state.isTransitioning()).isTrue()
@@ -358,7 +372,35 @@
// Go to the final scene if it is close to 1.
assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
assertThat(state.isTransitioning()).isFalse()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB))
+ assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
+ }
+
+ @Test
+ fun snapToIdleIfClose_multipleTransitions() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
+
+ val aToB =
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.5f },
+ onFinish = { launch { /* do nothing */} },
+ )
+ state.startTransition(aToB, transitionKey = null)
+ assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
+
+ val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f })
+ state.startTransition(bToC, transitionKey = null)
+ assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
+
+ // Ignore the request if the progress is not close to 0 or 1, using the threshold.
+ assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
+ assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
+
+ // Go to the final scene if it is close to 1.
+ assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
+ assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC))
+ assertThat(state.currentTransitions).isEmpty()
}
@Test
@@ -435,23 +477,23 @@
overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
}
)
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// overscroll for SceneA is NOT defined
progress.value = -0.1f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// scroll from SceneA to SceneB
progress.value = 0.5f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
progress.value = 1f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// overscroll for SceneB is defined
progress.value = 1.1f
- assertThat(state.currentOverscrollSpec).isNotNull()
- assertThat(state.currentOverscrollSpec?.scene).isEqualTo(SceneB)
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec?.scene).isEqualTo(SceneB)
}
@Test
@@ -465,23 +507,23 @@
overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
}
)
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// overscroll for SceneA is defined
progress.value = -0.1f
- assertThat(state.currentOverscrollSpec).isNotNull()
- assertThat(state.currentOverscrollSpec?.scene).isEqualTo(SceneA)
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNotNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec?.scene).isEqualTo(SceneA)
// scroll from SceneA to SceneB
progress.value = 0.5f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
progress.value = 1f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// overscroll for SceneB is NOT defined
progress.value = 1.1f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
}
@Test
@@ -492,21 +534,99 @@
progress = { progress.value },
sceneTransitions = transitions {}
)
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// overscroll for SceneA is NOT defined
progress.value = -0.1f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// scroll from SceneA to SceneB
progress.value = 0.5f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
progress.value = 1f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
// overscroll for SceneB is NOT defined
progress.value = 1.1f
- assertThat(state.currentOverscrollSpec).isNull()
+ assertThat(state.currentTransition?.currentOverscrollSpec).isNull()
+ }
+
+ @Test
+ fun multipleTransitions() = runTest {
+ val finishingTransitions = mutableSetOf<TransitionState.Transition>()
+ fun onFinish(transition: TransitionState.Transition): Job {
+ // Instead of letting the transition finish, we put the transition in the
+ // finishingTransitions set so that we can verify that finish() is called when expected
+ // and then we call state STLState.finishTransition() ourselves.
+ finishingTransitions.add(transition)
+
+ return backgroundScope.launch {
+ // Try to acquire a locked mutex so that this code never completes.
+ Mutex(locked = true).withLock {}
+ }
+ }
+
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
+ val aToB = transition(SceneA, SceneB, onFinish = ::onFinish)
+ val bToC = transition(SceneB, SceneC, onFinish = ::onFinish)
+ val cToA = transition(SceneC, SceneA, onFinish = ::onFinish)
+
+ // Starting state.
+ assertThat(finishingTransitions).isEmpty()
+ assertThat(state.currentTransitions).isEmpty()
+
+ // A => B.
+ state.startTransition(aToB, transitionKey = null)
+ assertThat(finishingTransitions).isEmpty()
+ assertThat(state.finishedTransitions).isEmpty()
+ assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
+
+ // B => C. This should automatically call finish() on aToB.
+ state.startTransition(bToC, transitionKey = null)
+ assertThat(finishingTransitions).containsExactly(aToB)
+ assertThat(state.finishedTransitions).isEmpty()
+ assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
+
+ // C => A. This should automatically call finish() on bToC.
+ state.startTransition(cToA, transitionKey = null)
+ assertThat(finishingTransitions).containsExactly(aToB, bToC)
+ assertThat(state.finishedTransitions).isEmpty()
+ assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
+
+ // Mark bToC as finished. The list of current transitions does not change because aToB is
+ // still not marked as finished.
+ state.finishTransition(bToC, idleScene = bToC.currentScene)
+ assertThat(state.finishedTransitions).containsExactly(bToC, bToC.currentScene)
+ assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
+
+ // Mark aToB as finished. This will remove both aToB and bToC from the list of transitions.
+ state.finishTransition(aToB, idleScene = aToB.currentScene)
+ assertThat(state.finishedTransitions).isEmpty()
+ assertThat(state.currentTransitions).containsExactly(cToA).inOrder()
+ }
+
+ @Test
+ fun tooManyTransitionsLogsWtfAndClearsTransitions() = runTest {
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
+
+ fun startTransition() {
+ val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */} })
+ state.startTransition(transition, transitionKey = null)
+ }
+
+ var hasLoggedWtf = false
+ val originalHandler = Log.setWtfHandler { _, _, _ -> hasLoggedWtf = true }
+ try {
+ repeat(100) { startTransition() }
+ assertThat(hasLoggedWtf).isFalse()
+ assertThat(state.currentTransitions).hasSize(100)
+
+ startTransition()
+ assertThat(hasLoggedWtf).isTrue()
+ assertThat(state.currentTransitions).hasSize(1)
+ } finally {
+ Log.setWtfHandler(originalHandler)
+ }
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index efaea71..723a182 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -299,6 +299,11 @@
.isWithin(DpOffsetSubject.DefaultTolerance)
.of(DpOffset(expectedOffset, expectedOffset))
+ // Wait for the transition to C to finish.
+ rule.mainClock.advanceTimeBy(TestTransitionDuration)
+ assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
+ assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneC)
+
// Go back to scene A. This should happen instantly (once the animation started, i.e. after
// 2 frames) given that we use a snap() animation spec.
currentScene = TestScenes.SceneA
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 99372a5..f034c18 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -547,12 +547,12 @@
}
assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
- assertThat(state.transformationSpec.transformations).hasSize(1)
+ assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(1)
// Move the pointer up to swipe to scene B using the new transition.
rule.onRoot().performTouchInput { moveBy(Offset(0f, -1.dp.toPx()), delayMillis = 1_000) }
assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
- assertThat(state.transformationSpec.transformations).hasSize(2)
+ assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(2)
}
@Test
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
index a32fe22..767057b 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
@@ -29,6 +29,7 @@
isUpOrLeft: Boolean = false,
bouncingScene: SceneKey? = null,
orientation: Orientation = Orientation.Horizontal,
+ onFinish: ((TransitionState.Transition) -> Job)? = null,
): TransitionState.Transition {
return object : TransitionState.Transition(from, to), TransitionState.HasOverscrollProperties {
override val currentScene: SceneKey = from
@@ -46,7 +47,13 @@
}
override fun finish(): Job {
- error("finish() is not supported in test transitions")
+ val onFinish =
+ onFinish
+ ?: error(
+ "onFinish() must be provided if finish() is called on test transitions"
+ )
+
+ return onFinish(this)
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index 69ff5ab..b4f87c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -21,6 +21,8 @@
import android.view.View
import android.widget.FrameLayout
import android.widget.RemoteViews.RemoteResponse
+import androidx.core.util.component1
+import androidx.core.util.component2
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -29,6 +31,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.refEq
import org.mockito.Mock
import org.mockito.Mockito.isNull
import org.mockito.Mockito.notNull
@@ -62,6 +65,7 @@
val parent = FrameLayout(context)
val view = CommunalAppWidgetHostView(context)
parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
underTest.onInteraction(view, testIntent, testResponse)
@@ -70,6 +74,8 @@
eq(testIntent),
isNull(),
notNull(),
+ refEq(fillInIntent),
+ refEq(activityOptions.toBundle()),
)
}
@@ -78,10 +84,17 @@
val parent = FrameLayout(context)
val view = View(context)
parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
underTest.onInteraction(view, testIntent, testResponse)
verify(activityStarter)
- .startPendingIntentMaybeDismissingKeyguard(eq(testIntent), isNull(), isNull())
+ .startPendingIntentMaybeDismissingKeyguard(
+ eq(testIntent),
+ isNull(),
+ isNull(),
+ refEq(fillInIntent),
+ refEq(activityOptions.toBundle()),
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index 8aa0e3fc..c8062fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.phone
+import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
+import android.os.Bundle
import android.os.RemoteException
import android.os.UserHandle
import android.view.View
import android.widget.FrameLayout
+import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -48,6 +51,7 @@
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
@@ -173,6 +177,53 @@
)
}
+ fun startPendingIntentDismissingKeyguard_fillInIntentAndExtraOptions_sendAndReturnResult() {
+ val pendingIntent = mock(PendingIntent::class.java)
+ val fillInIntent = mock(Intent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ whenever(pendingIntent.isActivity).thenReturn(true)
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ .thenReturn(false)
+
+ // extra activity options to set on pending intent
+ val activityOptions = mock(ActivityOptions::class.java)
+ activityOptions.splashScreenStyle = SPLASH_SCREEN_STYLE_SOLID_COLOR
+ activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission = false
+ val bundleCaptor = argumentCaptor<Bundle>()
+
+ underTest.startPendingIntentMaybeDismissingKeyguard(
+ intent = pendingIntent,
+ animationController = controller,
+ intentSentUiThreadCallback = null,
+ fillInIntent = fillInIntent,
+ extraOptions = activityOptions.toBundle(),
+ )
+ mainExecutor.runAllReady()
+
+ // Fill-in intent is passed and options contain extra values specified
+ verify(pendingIntent)
+ .sendAndReturnResult(
+ eq(context),
+ eq(0),
+ eq(fillInIntent),
+ nullable(),
+ nullable(),
+ nullable(),
+ bundleCaptor.capture()
+ )
+ val options = ActivityOptions.fromBundle(bundleCaptor.value)
+ assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
+ assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
+ }
+
@Test
fun startPendingIntentDismissingKeyguard_associatedView_getAnimatorController() {
val pendingIntent = mock(PendingIntent::class.java)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 1126ec3..072ec99 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -17,6 +17,7 @@
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Intent;
+import android.os.Bundle;
import android.os.UserHandle;
import android.view.View;
@@ -67,6 +68,17 @@
@Nullable ActivityTransitionAnimator.Controller animationController);
/**
+ * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable,
+ * ActivityTransitionAnimator.Controller)}, but also specifies a fill-in intent and extra
+ * options that could be used to populate the pending intent and launch the activity.
+ */
+ void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent,
+ @Nullable Runnable intentSentUiThreadCallback,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
+ @Nullable Intent fillInIntent,
+ @Nullable Bundle extraOptions);
+
+ /**
* The intent flag can be specified in startActivity().
*/
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 4c1e77b..778d8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -16,9 +16,14 @@
package com.android.systemui.communal.widgets
+import android.app.ActivityOptions
import android.app.PendingIntent
+import android.content.Intent
+import android.util.Pair
import android.view.View
import android.widget.RemoteViews
+import androidx.core.util.component1
+import androidx.core.util.component2
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.common.ui.view.getNearestParent
import com.android.systemui.plugins.ActivityStarter
@@ -33,21 +38,33 @@
view: View,
pendingIntent: PendingIntent,
response: RemoteViews.RemoteResponse
- ): Boolean =
- when {
- pendingIntent.isActivity -> startActivity(view, pendingIntent)
- else ->
- RemoteViews.startPendingIntent(view, pendingIntent, response.getLaunchOptions(view))
+ ): Boolean {
+ val launchOptions = response.getLaunchOptions(view)
+ return when {
+ pendingIntent.isActivity ->
+ // Forward the fill-in intent and activity options retrieved from the response
+ // to populate the pending intent, so that list items can launch respective
+ // activities.
+ startActivity(view, pendingIntent, launchOptions)
+ else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
}
+ }
- private fun startActivity(view: View, pendingIntent: PendingIntent): Boolean {
+ private fun startActivity(
+ view: View,
+ pendingIntent: PendingIntent,
+ launchOptions: Pair<Intent, ActivityOptions>,
+ ): Boolean {
val hostView = view.getNearestParent<CommunalAppWidgetHostView>()
val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView)
+ val (fillInIntent, activityOptions) = launchOptions
activityStarter.startPendingIntentMaybeDismissingKeyguard(
pendingIntent,
/* intentSentUiThreadCallback = */ null,
- animationController
+ animationController,
+ fillInIntent,
+ activityOptions.toBundle(),
)
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 18d2f30..b0707db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -111,7 +111,7 @@
@Override
protected void handleClick(@Nullable View view) {
if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
- mDialogViewModel.showDialog(mContext, view);
+ mDialogViewModel.showDialog(view);
} else {
// Secondary clicks are header clicks, just toggle.
final boolean isEnabled = mState.value;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
index 1247854..59fc81c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
@@ -19,8 +19,6 @@
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
/** Interactor class responsible for interacting with the Bluetooth Auto-On feature. */
@SysUISingleton
@@ -30,14 +28,10 @@
private val bluetoothAutoOnRepository: BluetoothAutoOnRepository,
) {
- val isEnabled = bluetoothAutoOnRepository.isAutoOn.map { it == ENABLED }.distinctUntilChanged()
+ val isEnabled = bluetoothAutoOnRepository.isAutoOn
- /**
- * Checks if the auto on value is present in the repository.
- *
- * @return `true` if a value is present (i.e, the feature is enabled by the Bluetooth server).
- */
- suspend fun isValuePresent(): Boolean = bluetoothAutoOnRepository.isValuePresent()
+ /** Checks if the auto on feature is supported. */
+ suspend fun isAutoOnSupported(): Boolean = bluetoothAutoOnRepository.isAutoOnSupported()
/**
* Sets enabled or disabled based on the provided value.
@@ -45,17 +39,14 @@
* @param value `true` to enable the feature, `false` to disable it.
*/
suspend fun setEnabled(value: Boolean) {
- if (!isValuePresent()) {
+ if (!isAutoOnSupported()) {
Log.e(TAG, "Trying to set toggle value while feature not available.")
} else {
- val newValue = if (value) ENABLED else DISABLED
- bluetoothAutoOnRepository.setAutoOn(newValue)
+ bluetoothAutoOnRepository.setAutoOn(value)
}
}
companion object {
private const val TAG = "BluetoothAutoOnInteractor"
- const val DISABLED = 0
- const val ENABLED = 1
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
index f97fc38..9ee582a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
@@ -16,22 +16,23 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
+import android.bluetooth.BluetoothAdapter
+import android.util.Log
+import com.android.settingslib.bluetooth.BluetoothCallback
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -44,61 +45,87 @@
class BluetoothAutoOnRepository
@Inject
constructor(
- private val secureSettings: SecureSettings,
- private val userRepository: UserRepository,
+ localBluetoothManager: LocalBluetoothManager?,
+ private val bluetoothAdapter: BluetoothAdapter?,
@Application private val coroutineScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
- // Flow representing the auto on setting value for the current user
- @OptIn(ExperimentalCoroutinesApi::class)
- internal val isAutoOn: StateFlow<Int> =
- userRepository.selectedUserInfo
- .flatMapLatest { userInfo ->
- secureSettings
- .observerFlow(userInfo.id, SETTING_NAME)
- .onStart { emit(Unit) }
- .map { secureSettings.getIntForUser(SETTING_NAME, UNSET, userInfo.id) }
- }
- .distinctUntilChanged()
- .flowOn(backgroundDispatcher)
- .stateIn(
- coroutineScope,
- SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
- UNSET
- )
+ // Flow representing the auto on state for the current user
+ internal val isAutoOn: Flow<Boolean> =
+ localBluetoothManager?.eventManager?.let { eventManager ->
+ conflatedCallbackFlow {
+ val listener =
+ object : BluetoothCallback {
+ override fun onAutoOnStateChanged(autoOnState: Int) {
+ super.onAutoOnStateChanged(autoOnState)
+ if (
+ autoOnState == BluetoothAdapter.AUTO_ON_STATE_ENABLED ||
+ autoOnState == BluetoothAdapter.AUTO_ON_STATE_DISABLED
+ ) {
+ trySendWithFailureLogging(
+ autoOnState == BluetoothAdapter.AUTO_ON_STATE_ENABLED,
+ TAG,
+ "onAutoOnStateChanged"
+ )
+ }
+ }
+ }
+ eventManager.registerCallback(listener)
+ awaitClose { eventManager.unregisterCallback(listener) }
+ }
+ .onStart { emit(isAutoOnEnabled()) }
+ .flowOn(backgroundDispatcher)
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
+ initialValue = false
+ )
+ }
+ ?: flowOf(false)
/**
- * Checks if the auto on setting value is ever set for the current user.
+ * Checks if the auto on feature is supported for the current user.
*
- * @return `true` if the setting value is not UNSET, `false` otherwise.
+ * @throws Exception if an error occurs while checking auto-on support.
*/
- suspend fun isValuePresent(): Boolean =
+ suspend fun isAutoOnSupported(): Boolean =
withContext(backgroundDispatcher) {
- secureSettings.getIntForUser(
- SETTING_NAME,
- UNSET,
- userRepository.getSelectedUserInfo().id
- ) != UNSET
+ try {
+ bluetoothAdapter?.isAutoOnSupported ?: false
+ } catch (e: Exception) {
+ // Server could throw TimeoutException, InterruptedException or ExecutionException
+ Log.e(TAG, "Error calling isAutoOnSupported", e)
+ false
+ }
}
- /**
- * Sets the Bluetooth Auto-On setting value for the current user.
- *
- * @param value The new setting value to be applied.
- */
- suspend fun setAutoOn(value: Int) {
+ /** Sets the Bluetooth Auto-On for the current user. */
+ suspend fun setAutoOn(value: Boolean) {
withContext(backgroundDispatcher) {
- secureSettings.putIntForUser(
- SETTING_NAME,
- value,
- userRepository.getSelectedUserInfo().id
- )
+ try {
+ bluetoothAdapter?.setAutoOnEnabled(value)
+ } catch (e: Exception) {
+ // Server could throw IllegalStateException, TimeoutException, InterruptedException
+ // or ExecutionException
+ Log.e(TAG, "Error calling setAutoOnEnabled", e)
+ }
}
}
- companion object {
- const val SETTING_NAME = "bluetooth_automatic_turn_on"
- const val UNSET = -1
+ private suspend fun isAutoOnEnabled() =
+ withContext(backgroundDispatcher) {
+ try {
+ bluetoothAdapter?.isAutoOnEnabled ?: false
+ } catch (e: Exception) {
+ // Server could throw IllegalStateException, TimeoutException, InterruptedException
+ // or ExecutionException
+ Log.e(TAG, "Error calling isAutoOnEnabled", e)
+ false
+ }
+ }
+
+ private companion object {
+ const val TAG = "BluetoothAutoOnRepository"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegate.kt
index 9d53703..a8d9e78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegate.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
-import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -58,7 +57,6 @@
class BluetoothTileDialogDelegate
@AssistedInject
internal constructor(
- @Assisted private val context: Context,
@Assisted private val initialUiProperties: BluetoothTileDialogViewModel.UiProperties,
@Assisted private val cachedContentHeight: Int,
@Assisted private val bluetoothToggleInitialValue: Boolean,
@@ -69,11 +67,8 @@
private val uiEventLogger: UiEventLogger,
private val logger: BluetoothTileDialogLogger,
private val systemuiDialogFactory: SystemUIDialog.Factory,
- mainLayoutInflater: LayoutInflater,
) : SystemUIDialog.Delegate {
- private val layoutInflater = mainLayoutInflater.cloneInContext(context)
-
private val mutableBluetoothStateToggle: MutableStateFlow<Boolean> =
MutableStateFlow(bluetoothToggleInitialValue)
internal val bluetoothStateToggle
@@ -102,7 +97,6 @@
@AssistedFactory
internal interface Factory {
fun create(
- context: Context,
initialUiProperties: BluetoothTileDialogViewModel.UiProperties,
cachedContentHeight: Int,
bluetoothEnabled: Boolean,
@@ -112,16 +106,15 @@
}
override fun createDialog(): SystemUIDialog {
- val dialog = systemuiDialogFactory.create(this, context)
-
- return dialog
+ return systemuiDialogFactory.create(this)
}
override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
SystemUIDialog.registerDismissListener(dialog, dismissListener)
uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN)
+ val context = dialog.context
- layoutInflater.inflate(R.layout.bluetooth_tile_dialog, null).apply {
+ LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null).apply {
accessibilityPaneTitle = context.getText(R.string.accessibility_desc_quick_settings)
dialog.setContentView(this)
}
@@ -201,7 +194,7 @@
setEnabled(true)
alpha = ENABLED_ALPHA
}
- getSubtitleTextView(dialog).text = context.getString(uiProperties.subTitleResId)
+ getSubtitleTextView(dialog).text = dialog.context.getString(uiProperties.subTitleResId)
getAutoOnToggleView(dialog).visibility = uiProperties.autoOnToggleVisibility
}
@@ -215,7 +208,7 @@
setEnabled(true)
alpha = ENABLED_ALPHA
}
- getAutoOnToggleInfoTextView(dialog).text = context.getString(infoResId)
+ getAutoOnToggleInfoTextView(dialog).text = dialog.context.getString(infoResId)
}
private fun setupToggle(dialog: SystemUIDialog) {
@@ -288,7 +281,7 @@
private fun setupRecyclerView(dialog: SystemUIDialog) {
getDeviceListView(dialog).apply {
- layoutManager = LinearLayoutManager(context)
+ layoutManager = LinearLayoutManager(dialog.context)
adapter = deviceItemAdapter
}
}
@@ -343,7 +336,9 @@
private val asyncListDiffer = AsyncListDiffer(this, diffUtilCallback)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceItemViewHolder {
- val view = layoutInflater.inflate(R.layout.bluetooth_device_item, parent, false)
+ val view =
+ LayoutInflater.from(parent.context)
+ .inflate(R.layout.bluetooth_device_item, parent, false)
return DeviceItemViewHolder(view)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index e4f3c19..fd624d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
-import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
@@ -29,7 +28,6 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.flags.Flags.bluetoothQsTileDialogAutoOnToggle
import com.android.systemui.Prefs
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
@@ -78,19 +76,19 @@
/**
* Shows the dialog.
*
- * @param context The context in which the dialog is displayed.
* @param view The view from which the dialog is shown.
*/
@kotlinx.coroutines.ExperimentalCoroutinesApi
- fun showDialog(context: Context, view: View?) {
+ fun showDialog(view: View?) {
cancelJob()
job =
coroutineScope.launch(mainDispatcher) {
var updateDeviceItemJob: Job?
var updateDialogUiJob: Job? = null
- val dialogDelegate = createBluetoothTileDialog(context)
+ val dialogDelegate = createBluetoothTileDialog()
val dialog = dialogDelegate.createDialog()
+ val context = dialog.context
view?.let {
dialogTransitionAnimator.showFromView(
@@ -213,7 +211,7 @@
}
}
- private suspend fun createBluetoothTileDialog(context: Context): BluetoothTileDialogDelegate {
+ private suspend fun createBluetoothTileDialog(): BluetoothTileDialogDelegate {
val cachedContentHeight =
withContext(backgroundDispatcher) {
sharedPreferences.getInt(
@@ -223,7 +221,6 @@
}
return bluetoothDialogDelegateFactory.create(
- context,
UiProperties.build(
bluetoothStateInteractor.isBluetoothEnabled,
isAutoOnToggleFeatureAvailable()
@@ -277,7 +274,7 @@
@VisibleForTesting
internal suspend fun isAutoOnToggleFeatureAvailable() =
- bluetoothQsTileDialogAutoOnToggle() && bluetoothAutoOnInteractor.isValuePresent()
+ bluetoothAutoOnInteractor.isAutoOnSupported()
companion object {
private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java
index 06c0b8b..c89b476 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java
@@ -33,6 +33,7 @@
import android.content.Intent.CaptureContentForNoteStatusCodes;
import android.content.res.Resources;
import android.os.IBinder;
+import android.util.Log;
import androidx.annotation.Nullable;
@@ -58,6 +59,8 @@
*/
public class AppClipsService extends Service {
+ private static final String TAG = AppClipsService.class.getSimpleName();
+
@Application private final Context mContext;
private final FeatureFlags mFeatureFlags;
private final Optional<Bubbles> mOptionalBubbles;
@@ -77,14 +80,22 @@
private boolean checkIndependentVariables() {
if (!mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)) {
+ Log.d(TAG, "Feature flag disabled");
return false;
}
if (mOptionalBubbles.isEmpty()) {
+ Log.d(TAG, "Bubbles not available");
return false;
}
- return isComponentValid();
+ if (isComponentValid()) {
+ Log.d(TAG, "checkIndependentVariables returned true");
+ return true;
+ }
+
+ Log.d(TAG, "checkIndependentVariables returned false");
+ return false;
}
private boolean isComponentValid() {
@@ -93,12 +104,27 @@
componentName = ComponentName.unflattenFromString(
mContext.getString(R.string.config_screenshotAppClipsActivityComponent));
} catch (Resources.NotFoundException e) {
+ Log.d(TAG, "AppClips activity component resource not defined");
return false;
}
- return componentName != null
- && !componentName.getPackageName().isEmpty()
- && !componentName.getClassName().isEmpty();
+ if (componentName == null) {
+ Log.d(TAG, "AppClips component name not defined");
+ return false;
+ }
+
+ if (componentName.getPackageName().isEmpty()) {
+ Log.d(TAG, "AppClips component package name is empty");
+ return false;
+ }
+
+ if (componentName.getClassName().isEmpty()) {
+ Log.d(TAG, "AppClips component class name is empty");
+ return false;
+ }
+
+ Log.d(TAG, "isComponentValid returned true");
+ return true;
}
@Nullable
@@ -107,24 +133,39 @@
return new IAppClipsService.Stub() {
@Override
public boolean canLaunchCaptureContentActivityForNote(int taskId) {
- return canLaunchCaptureContentActivityForNoteInternal(taskId)
- == CAPTURE_CONTENT_FOR_NOTE_SUCCESS;
+ if (canLaunchCaptureContentActivityForNoteInternal(taskId)
+ == CAPTURE_CONTENT_FOR_NOTE_SUCCESS) {
+ Log.d(TAG, String.format("Can launch AppClips returned true for %d", taskId));
+ return true;
+ }
+
+ Log.d(TAG, String.format("Can launch AppClips returned false for %d", taskId));
+ return false;
}
@Override
@CaptureContentForNoteStatusCodes
public int canLaunchCaptureContentActivityForNoteInternal(int taskId) {
if (!mAreTaskAndTimeIndependentPrerequisitesMet) {
+ Log.d(TAG,
+ String.format("Task (%d) and time independent prereqs not met", taskId));
return CAPTURE_CONTENT_FOR_NOTE_FAILED;
}
if (!mOptionalBubbles.get().isAppBubbleTaskId(taskId)) {
+ Log.d(TAG, String.format("Taskid %d is not app bubble task", taskId));
return CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED;
}
- return mDevicePolicyManager.getScreenCaptureDisabled(null)
- ? CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN
- : CAPTURE_CONTENT_FOR_NOTE_SUCCESS;
+ if (mDevicePolicyManager.getScreenCaptureDisabled(null)) {
+ Log.d(TAG,
+ String.format("Screen capture disabled by admin, taskId %d", taskId));
+ return CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN;
+ }
+
+ Log.d(TAG,
+ String.format("Can launch AppClips (internal) successful for %d", taskId));
+ return CAPTURE_CONTENT_FOR_NOTE_SUCCESS;
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 0c69a65..8531eaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -22,6 +22,7 @@
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
+import com.android.systemui.Flags.notificationMinimalismPrototype
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
@@ -59,6 +60,7 @@
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -260,8 +262,11 @@
}
}
- private suspend fun trackUnseenFilterSettingChanges() {
- secureSettings
+ private fun unseenFeatureEnabled(): Flow<Boolean> {
+ if (notificationMinimalismPrototype()) {
+ return flowOf(true)
+ }
+ return secureSettings
// emit whenever the setting has changed
.observerFlow(
UserHandle.USER_ALL,
@@ -283,17 +288,20 @@
// only track the most recent emission, if events are happening faster than they can be
// consumed
.conflate()
- .collectLatest { setting ->
- // update local field and invalidate if necessary
- if (setting != unseenFilterEnabled) {
- unseenFilterEnabled = setting
- unseenNotifFilter.invalidateList("unseen setting changed")
- }
- // if the setting is enabled, then start tracking and filtering unseen notifications
- if (setting) {
- trackSeenNotifications()
- }
+ }
+
+ private suspend fun trackUnseenFilterSettingChanges() {
+ unseenFeatureEnabled().collectLatest { setting ->
+ // update local field and invalidate if necessary
+ if (setting != unseenFilterEnabled) {
+ unseenFilterEnabled = setting
+ unseenNotifFilter.invalidateList("unseen setting changed")
}
+ // if the setting is enabled, then start tracking and filtering unseen notifications
+ if (setting) {
+ trackSeenNotifications()
+ }
+ }
}
private val collectionListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index f792898..adcbbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -58,6 +58,7 @@
private FooterViewButton mClearAllButton;
private FooterViewButton mManageOrHistoryButton;
+ private boolean mShouldBeHidden;
private boolean mShowHistory;
// String cache, for performance reasons.
// Reading them from a Resources object can be quite slow sometimes.
@@ -110,6 +111,20 @@
setSecondaryVisible(visible, animate, onAnimationEnded);
}
+ /** See {@link this#setShouldBeHidden} below. */
+ public boolean shouldBeHidden() {
+ return mShouldBeHidden;
+ }
+
+ /**
+ * Whether this view's visibility should be set to INVISIBLE. Note that this is different from
+ * the {@link StackScrollerDecorView#setVisible} method, which in turn handles visibility
+ * transitions between VISIBLE and GONE.
+ */
+ public void setShouldBeHidden(boolean hide) {
+ mShouldBeHidden = hide;
+ }
+
@Override
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b8b4a03..eb6c7b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1767,9 +1767,7 @@
*/
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
this(context, attrs, context);
- if (com.android.systemui.Flags.notificationRowUserContext()) {
- Log.wtf(TAG, "This constructor shouldn't be called");
- }
+ Log.wtf(TAG, "This constructor shouldn't be called");
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 609b15e..3e932aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -31,7 +31,6 @@
import com.android.internal.widget.ImageResolver;
import com.android.internal.widget.LocalImageResolver;
import com.android.internal.widget.MessagingMessage;
-import com.android.systemui.Flags;
import java.util.HashSet;
import java.util.List;
@@ -67,11 +66,7 @@
* @param imageCache The implementation of internal cache.
*/
public NotificationInlineImageResolver(Context context, ImageCache imageCache) {
- if (Flags.notificationRowUserContext()) {
- mContext = context;
- } else {
- mContext = context.getApplicationContext();
- }
+ mContext = context;
mImageCache = imageCache;
if (mImageCache != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index ea3036e..5fbcebd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -66,9 +66,7 @@
mInflateOrigin = new Throwable("inflate requested here");
}
mListener = listener;
- AsyncLayoutInflater inflater = com.android.systemui.Flags.notificationRowUserContext()
- ? new AsyncLayoutInflater(context, makeRowInflater(entry))
- : new AsyncLayoutInflater(context);
+ AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, makeRowInflater(entry));
mEntry = entry;
entry.setInflationTask(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 2d9c63e..1b53cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -20,6 +20,7 @@
import android.util.Log
import android.view.View.GONE
import androidx.annotation.VisibleForTesting
+import com.android.systemui.Flags.notificationMinimalismPrototype
import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
@@ -66,6 +67,11 @@
*/
private var maxKeyguardNotifications by notNull<Int>()
+ /**
+ * Whether [maxKeyguardNotifications] will have 1 added to it when media is shown in the stack.
+ */
+ private var maxNotificationsExcludesMedia = false
+
/** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
private var dividerHeight by notNull<Float>()
@@ -168,7 +174,11 @@
log { "\n" }
val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfHeight)
+
+ // TODO: Avoid making this split shade assumption by simply checking the stack for media
val isMediaShowing = mediaDataManager.hasActiveMediaOrRecommendation()
+ val isMediaShowingInStack = isMediaShowing && !splitShadeStateController
+ .shouldUseSplitNotificationShade(resources)
log { "\tGet maxNotifWithoutSavingSpace ---" }
val maxNotifWithoutSavingSpace =
@@ -181,12 +191,11 @@
}
// How many notifications we can show at heightWithoutLockscreenConstraints
- var minCountAtHeightWithoutConstraints =
- if (isMediaShowing && !splitShadeStateController
- .shouldUseSplitNotificationShade(resources)) 2 else 1
+ val minCountAtHeightWithoutConstraints = if (isMediaShowingInStack) 2 else 1
log {
"\t---maxNotifWithoutSavingSpace=$maxNotifWithoutSavingSpace " +
"isMediaShowing=$isMediaShowing" +
+ "isMediaShowingInStack=$isMediaShowingInStack" +
"minCountAtHeightWithoutConstraints=$minCountAtHeightWithoutConstraints"
}
log { "\n" }
@@ -223,7 +232,9 @@
}
if (onLockscreen()) {
- maxNotifications = min(maxKeyguardNotifications, maxNotifications)
+ val increaseMaxForMedia = maxNotificationsExcludesMedia && isMediaShowingInStack
+ val lockscreenMax = maxKeyguardNotifications.safeIncrementIf(increaseMaxForMedia)
+ maxNotifications = min(lockscreenMax, maxNotifications)
}
// Could be < 0 if the space available is less than the shelf size. Returns 0 in this case.
@@ -276,7 +287,7 @@
height = notifsHeight + shelfHeightWithSpaceBefore
log {
"--- computeHeight(maxNotifs=$maxNotifs, shelfHeight=$shelfHeight)" +
- " -> ${height}=($notifsHeight+$shelfHeightWithSpaceBefore)" +
+ " -> $height=($notifsHeight+$shelfHeightWithSpaceBefore)" +
" | saveSpaceOnLockscreen=$saveSpaceOnLockscreen"
}
}
@@ -367,8 +378,9 @@
}
fun updateResources() {
- maxKeyguardNotifications =
- infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count))
+ maxKeyguardNotifications = if (notificationMinimalismPrototype()) 1
+ else infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count))
+ maxNotificationsExcludesMedia = notificationMinimalismPrototype()
dividerHeight =
max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat())
@@ -486,6 +498,13 @@
v
}
+ private fun Int.safeIncrementIf(condition: Boolean): Int =
+ if (condition && this != Int.MAX_VALUE) {
+ this + 1
+ } else {
+ this
+ }
+
/** Returns the last index where [predicate] returns true, or -1 if it was always false. */
private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int =
takeWhile(predicate).count() - 1
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index b42c07d..5eaccd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -594,15 +594,16 @@
);
if (view instanceof FooterView) {
if (FooterViewRefactor.isEnabled()) {
- final float footerEnd = algorithmState.mCurrentExpandedYPosition
- + view.getIntrinsicHeight();
- final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
- // TODO(b/293167744): May be able to keep only noSpaceForFooter here if we add an
- // emission when clearAllNotifications is called, and then use that in the footer
- // visibility flow.
- ((FooterView.FooterViewState) viewState).hideContent =
- noSpaceForFooter || (ambientState.isClearAllInProgress()
- && !hasNonClearableNotifs(algorithmState));
+ if (((FooterView) view).shouldBeHidden()) {
+ viewState.hidden = true;
+ } else {
+ final float footerEnd = algorithmState.mCurrentExpandedYPosition
+ + view.getIntrinsicHeight();
+ final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
+ ((FooterView.FooterViewState) viewState).hideContent =
+ noSpaceForFooter || (ambientState.isClearAllInProgress()
+ && !hasNonClearableNotifs(algorithmState));
+ }
} else {
final boolean shadeClosed = !ambientState.isShadeExpanded();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 97cbbe8..18bb5119 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -193,13 +193,14 @@
},
)
launch {
- viewModel.shouldShowFooterView.collect { animatedVisibility ->
+ viewModel.shouldIncludeFooterView.collect { animatedVisibility ->
footerView.setVisible(
/* visible = */ animatedVisibility.value,
/* animate = */ animatedVisibility.isAnimating,
)
}
}
+ launch { viewModel.shouldHideFooterView.collect { footerView.setShouldBeHidden(it) } }
disposableHandle.awaitCancellationThenDispose()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index a6ca027..5a7433d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -31,7 +31,6 @@
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
-import com.android.systemui.util.kotlin.combine
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
@@ -111,7 +110,32 @@
}
}
- val shouldShowFooterView: Flow<AnimatedValue<Boolean>> by lazy {
+ /**
+ * Whether the footer should not be visible for the user, even if it's present in the list (as
+ * per [shouldIncludeFooterView] below).
+ *
+ * This essentially corresponds to having the view set to INVISIBLE.
+ */
+ val shouldHideFooterView: Flow<Boolean> by lazy {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ flowOf(false)
+ } else {
+ // When the shade is closed, the footer is still present in the list, but not visible.
+ // This prevents the footer from being shown when a HUN is present, while still allowing
+ // the footer to be counted as part of the shade for measurements.
+ shadeInteractor.shadeExpansion.map { it == 0f }.distinctUntilChanged()
+ }
+ }
+
+ /**
+ * Whether the footer should be part of the list or not, and whether the transition from one
+ * state to another should be animated. This essentially corresponds to transitioning the view
+ * visibility from VISIBLE to GONE and vice versa.
+ *
+ * Note that this value being true doesn't necessarily mean that the footer is visible. It could
+ * be hidden by another condition (see [shouldHideFooterView] above).
+ */
+ val shouldIncludeFooterView: Flow<AnimatedValue<Boolean>> by lazy {
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(AnimatedValue.NotAnimating(false))
} else {
@@ -120,34 +144,30 @@
userSetupInteractor.isUserSetUp,
notificationStackInteractor.isShowingOnLockscreen,
shadeInteractor.isQsFullscreen,
- remoteInputInteractor.isRemoteInputActive,
- shadeInteractor.shadeExpansion.map { it == 0f }.distinctUntilChanged(),
+ remoteInputInteractor.isRemoteInputActive
) {
hasNotifications,
isUserSetUp,
isShowingOnLockscreen,
qsFullScreen,
- isRemoteInputActive,
- isShadeClosed ->
+ isRemoteInputActive ->
when {
- !hasNotifications -> VisibilityChange.HIDE_WITH_ANIMATION
+ !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
// Hide the footer until the user setup is complete, to prevent access
// to settings (b/193149550).
- !isUserSetUp -> VisibilityChange.HIDE_WITH_ANIMATION
+ !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
// Do not show the footer if the lockscreen is visible (incl. AOD),
// except if the shade is opened on top. See also b/219680200.
// Do not animate, as that makes the footer appear briefly when
// transitioning between the shade and keyguard.
- isShowingOnLockscreen -> VisibilityChange.HIDE_WITHOUT_ANIMATION
+ isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
// Do not show the footer if quick settings are fully expanded (except
// for the foldable split shade view). See b/201427195 && b/222699879.
- qsFullScreen -> VisibilityChange.HIDE_WITH_ANIMATION
+ qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
// Hide the footer if remote input is active (i.e. user is replying to a
// notification). See b/75984847.
- isRemoteInputActive -> VisibilityChange.HIDE_WITH_ANIMATION
- // Never show the footer if the shade is collapsed (e.g. when HUNing).
- isShadeClosed -> VisibilityChange.HIDE_WITHOUT_ANIMATION
- else -> VisibilityChange.SHOW_WITH_ANIMATION
+ isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ else -> VisibilityChange.APPEAR_WITH_ANIMATION
}
}
.flowOn(bgDispatcher)
@@ -180,9 +200,9 @@
}
enum class VisibilityChange(val visible: Boolean, val canAnimate: Boolean) {
- HIDE_WITHOUT_ANIMATION(visible = false, canAnimate = false),
- HIDE_WITH_ANIMATION(visible = false, canAnimate = true),
- SHOW_WITH_ANIMATION(visible = true, canAnimate = true)
+ DISAPPEAR_WITHOUT_ANIMATION(visible = false, canAnimate = false),
+ DISAPPEAR_WITH_ANIMATION(visible = false, canAnimate = true),
+ APPEAR_WITH_ANIMATION(visible = true, canAnimate = true)
}
// TODO(b/308591475): This should be tracked separately by the empty shade.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index a55de25..37646ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -21,6 +21,7 @@
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
+import android.os.Bundle
import android.os.RemoteException
import android.os.UserHandle
import android.provider.Settings
@@ -149,6 +150,23 @@
)
}
+ override fun startPendingIntentMaybeDismissingKeyguard(
+ intent: PendingIntent,
+ intentSentUiThreadCallback: Runnable?,
+ animationController: ActivityTransitionAnimator.Controller?,
+ fillInIntent: Intent?,
+ extraOptions: Bundle?,
+ ) {
+ activityStarterInternal.startPendingIntentDismissingKeyguard(
+ intent = intent,
+ intentSentUiThreadCallback = intentSentUiThreadCallback,
+ animationController = animationController,
+ showOverLockscreen = true,
+ fillInIntent = fillInIntent,
+ extraOptions = extraOptions,
+ )
+ }
+
/**
* TODO(b/279084380): Change callers to just call startActivityDismissingKeyguard and deprecate
* this.
@@ -554,6 +572,8 @@
associatedView: View? = null,
animationController: ActivityTransitionAnimator.Controller? = null,
showOverLockscreen: Boolean = false,
+ fillInIntent: Intent? = null,
+ extraOptions: Bundle? = null,
) {
val animationController =
if (associatedView is ExpandableNotificationRow) {
@@ -614,9 +634,10 @@
val options =
ActivityOptions(
CentralSurfaces.getActivityOptions(
- displayId,
- animationAdapter
- )
+ displayId,
+ animationAdapter
+ )
+ .apply { extraOptions?.let { putAll(it) } }
)
// TODO b/221255671: restrict this to only be set for
// notifications
@@ -625,9 +646,9 @@
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
)
return intent.sendAndReturnResult(
- null,
+ context,
0,
- null,
+ fillInIntent,
null,
null,
null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 60b8599..b085d80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -301,7 +301,7 @@
.flatMapLatest { it.networkName }
.logDiffsForTable(
tableLogBuffer,
- columnPrefix = "",
+ columnPrefix = "intent",
initialValue = activeRepo.value.networkName.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value)
@@ -311,7 +311,7 @@
.flatMapLatest { it.carrierName }
.logDiffsForTable(
tableLogBuffer,
- columnPrefix = "",
+ columnPrefix = "sub",
initialValue = activeRepo.value.carrierName.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.carrierName.value)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index f01ac0e..5ab2ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -358,7 +358,13 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), telephonyManager.simCarrierId)
- /** BroadcastDispatcher does not handle sticky broadcasts, so we can't use it here */
+ /**
+ * BroadcastDispatcher does not handle sticky broadcasts, so we can't use it here. Note that we
+ * now use the [SharingStarted.Eagerly] strategy, because there have been cases where the sticky
+ * broadcast does not represent the correct state.
+ *
+ * See b/322432056 for context.
+ */
@SuppressLint("RegisterReceiverViaContext")
override val networkName: StateFlow<NetworkNameModel> =
conflatedCallbackFlow {
@@ -388,7 +394,7 @@
awaitClose { context.unregisterReceiver(receiver) }
}
.flowOn(bgDispatcher)
- .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
+ .stateIn(scope, SharingStarted.Eagerly, defaultNetworkName)
override val dataEnabled = run {
val initial = telephonyManager.isDataConnectionAllowed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt
index 3710713..036d3c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt
@@ -16,22 +16,25 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
-import android.content.pm.UserInfo
+import android.bluetooth.BluetoothAdapter
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
-import com.google.common.truth.Truth
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import kotlin.test.Test
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
@@ -41,8 +44,17 @@
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
- private var secureSettings: FakeSettings = FakeSettings()
- private val userRepository: FakeUserRepository = FakeUserRepository()
+ private val bluetoothAdapter =
+ mock<BluetoothAdapter> {
+ var autoOn = false
+ whenever(isAutoOnEnabled).thenAnswer { autoOn }
+
+ whenever(setAutoOnEnabled(anyBoolean())).thenAnswer { invocation ->
+ autoOn = invocation.getArgument(0) as Boolean
+ autoOn
+ }
+ }
+ @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
private lateinit var bluetoothAutoOnInteractor: BluetoothAutoOnInteractor
@Before
@@ -50,49 +62,35 @@
bluetoothAutoOnInteractor =
BluetoothAutoOnInteractor(
BluetoothAutoOnRepository(
- secureSettings,
- userRepository,
+ localBluetoothManager,
+ bluetoothAdapter,
testScope.backgroundScope,
- testDispatcher
+ testDispatcher,
)
)
}
@Test
- fun testSet_bluetoothAutoOnUnset_doNothing() {
+ fun testSetEnabled_bluetoothAutoOnUnsupported_doNothing() {
testScope.runTest {
+ whenever(bluetoothAdapter.isAutoOnSupported).thenReturn(false)
+
bluetoothAutoOnInteractor.setEnabled(true)
-
- val actualValue by collectLastValue(bluetoothAutoOnInteractor.isEnabled)
-
runCurrent()
- Truth.assertThat(actualValue).isEqualTo(false)
+ assertFalse(bluetoothAdapter.isAutoOnEnabled)
}
}
@Test
- fun testSet_bluetoothAutoOnSet_setNewValue() {
+ fun testSetEnabled_bluetoothAutoOnSupported_setNewValue() {
testScope.runTest {
- userRepository.setUserInfos(listOf(SYSTEM_USER))
- secureSettings.putIntForUser(
- BluetoothAutoOnRepository.SETTING_NAME,
- BluetoothAutoOnInteractor.DISABLED,
- SYSTEM_USER_ID
- )
+ whenever(bluetoothAdapter.isAutoOnSupported).thenReturn(true)
+
bluetoothAutoOnInteractor.setEnabled(true)
-
- val actualValue by collectLastValue(bluetoothAutoOnInteractor.isEnabled)
-
runCurrent()
- Truth.assertThat(actualValue).isEqualTo(true)
+ assertTrue(bluetoothAdapter.isAutoOnEnabled)
}
}
-
- companion object {
- private const val SYSTEM_USER_ID = 0
- private val SYSTEM_USER =
- UserInfo(/* id= */ SYSTEM_USER_ID, /* name= */ "system user", /* flags= */ 0)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
index cd1452a..3119284 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
@@ -16,18 +16,14 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
-import android.content.pm.UserInfo
-import android.os.UserHandle
+import android.bluetooth.BluetoothAdapter
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.BluetoothEventManager
+import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnInteractor.Companion.DISABLED
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnInteractor.Companion.ENABLED
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnRepository.Companion.SETTING_NAME
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnRepository.Companion.UNSET
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -37,6 +33,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
@@ -46,83 +43,57 @@
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
- private var secureSettings: FakeSettings = FakeSettings()
- private val userRepository: FakeUserRepository = FakeUserRepository()
+ @Mock private lateinit var bluetoothAdapter: BluetoothAdapter
+ @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+ @Mock private lateinit var eventManager: BluetoothEventManager
private lateinit var bluetoothAutoOnRepository: BluetoothAutoOnRepository
@Before
fun setUp() {
+ whenever(localBluetoothManager.eventManager).thenReturn(eventManager)
bluetoothAutoOnRepository =
BluetoothAutoOnRepository(
- secureSettings,
- userRepository,
+ localBluetoothManager,
+ bluetoothAdapter,
testScope.backgroundScope,
- testDispatcher
+ testDispatcher,
)
-
- userRepository.setUserInfos(listOf(SECONDARY_USER, SYSTEM_USER))
}
@Test
- fun testGetValue_valueUnset() {
+ fun testIsAutoOn_returnFalse() {
testScope.runTest {
- userRepository.setSelectedUserInfo(SYSTEM_USER)
+ whenever(bluetoothAdapter.isAutoOnEnabled).thenReturn(false)
val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
runCurrent()
- assertThat(actualValue).isEqualTo(UNSET)
- assertThat(bluetoothAutoOnRepository.isValuePresent()).isFalse()
+ assertThat(actualValue).isEqualTo(false)
}
}
@Test
- fun testGetValue_valueFalse() {
+ fun testIsAutoOn_returnTrue() {
testScope.runTest {
- userRepository.setSelectedUserInfo(SYSTEM_USER)
+ whenever(bluetoothAdapter.isAutoOnEnabled).thenReturn(true)
val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
- secureSettings.putIntForUser(SETTING_NAME, DISABLED, UserHandle.USER_SYSTEM)
runCurrent()
- assertThat(actualValue).isEqualTo(DISABLED)
+ assertThat(actualValue).isEqualTo(true)
}
}
@Test
- fun testGetValue_valueTrue() {
+ fun testIsAutoOnSupported_returnTrue() {
testScope.runTest {
- userRepository.setSelectedUserInfo(SYSTEM_USER)
- val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
+ whenever(bluetoothAdapter.isAutoOnSupported).thenReturn(true)
+ val actualValue = bluetoothAutoOnRepository.isAutoOnSupported()
- secureSettings.putIntForUser(SETTING_NAME, ENABLED, UserHandle.USER_SYSTEM)
runCurrent()
- assertThat(actualValue).isEqualTo(ENABLED)
+ assertThat(actualValue).isEqualTo(true)
}
}
-
- @Test
- fun testGetValue_valueTrue_secondaryUser_returnTrue() {
- testScope.runTest {
- userRepository.setSelectedUserInfo(SECONDARY_USER)
- val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
-
- secureSettings.putIntForUser(SETTING_NAME, DISABLED, SYSTEM_USER_ID)
- secureSettings.putIntForUser(SETTING_NAME, ENABLED, SECONDARY_USER_ID)
- runCurrent()
-
- assertThat(actualValue).isEqualTo(ENABLED)
- }
- }
-
- companion object {
- private const val SYSTEM_USER_ID = 0
- private const val SECONDARY_USER_ID = 1
- private val SYSTEM_USER =
- UserInfo(/* id= */ SYSTEM_USER_ID, /* name= */ "system user", /* flags= */ 0)
- private val SECONDARY_USER =
- UserInfo(/* id= */ SECONDARY_USER_ID, /* name= */ "secondary user", /* flags= */ 0)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegateTest.kt
index 8ecb953..17b6127 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogDelegateTest.kt
@@ -109,7 +109,6 @@
mBluetoothTileDialogDelegate =
BluetoothTileDialogDelegate(
- mContext,
uiProperties,
CONTENT_HEIGHT,
ENABLED,
@@ -119,14 +118,12 @@
fakeSystemClock,
uiEventLogger,
logger,
- sysuiDialogFactory,
- LayoutInflater.from(mContext)
+ sysuiDialogFactory
)
whenever(
sysuiDialogFactory.create(
- any(SystemUIDialog.Delegate::class.java),
- any(Context::class.java)
+ any(SystemUIDialog.Delegate::class.java)
)
)
.thenAnswer {
@@ -216,7 +213,6 @@
LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false)
val viewHolder =
BluetoothTileDialogDelegate(
- mContext,
uiProperties,
CONTENT_HEIGHT,
ENABLED,
@@ -227,7 +223,6 @@
uiEventLogger,
logger,
sysuiDialogFactory,
- LayoutInflater.from(mContext)
)
.Adapter(bluetoothTileDialogCallback)
.DeviceItemViewHolder(view)
@@ -273,7 +268,6 @@
val cachedHeight = Int.MAX_VALUE
val dialog =
BluetoothTileDialogDelegate(
- mContext,
BluetoothTileDialogViewModel.UiProperties.build(ENABLED, ENABLED),
cachedHeight,
ENABLED,
@@ -284,7 +278,6 @@
uiEventLogger,
logger,
sysuiDialogFactory,
- LayoutInflater.from(mContext)
)
.createDialog()
dialog.show()
@@ -298,7 +291,6 @@
testScope.runTest {
val dialog =
BluetoothTileDialogDelegate(
- mContext,
BluetoothTileDialogViewModel.UiProperties.build(ENABLED, ENABLED),
MATCH_PARENT,
ENABLED,
@@ -309,7 +301,6 @@
uiEventLogger,
logger,
sysuiDialogFactory,
- LayoutInflater.from(mContext)
)
.createDialog()
dialog.show()
@@ -323,7 +314,6 @@
testScope.runTest {
val dialog =
BluetoothTileDialogDelegate(
- mContext,
BluetoothTileDialogViewModel.UiProperties.build(ENABLED, ENABLED),
MATCH_PARENT,
ENABLED,
@@ -334,7 +324,6 @@
uiEventLogger,
logger,
sysuiDialogFactory,
- LayoutInflater.from(mContext)
)
.createDialog()
dialog.show()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index 39e2413..c8a2aa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
-import android.content.pm.UserInfo
+import android.bluetooth.BluetoothAdapter
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -26,19 +26,18 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.flags.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.kotlin.getMutableStateFlow
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
@@ -75,6 +74,8 @@
@Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
+ @Mock private lateinit var bluetoothAutoOnInteractor: BluetoothAutoOnInteractor
+
@Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
@Mock private lateinit var activityStarter: ActivityStarter
@@ -87,6 +88,10 @@
@Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var bluetoothAdapter: BluetoothAdapter
+
+ @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+
@Mock
private lateinit var mBluetoothTileDialogDelegateDelegateFactory:
BluetoothTileDialogDelegate.Factory
@@ -100,8 +105,6 @@
private lateinit var scheduler: TestCoroutineScheduler
private lateinit var dispatcher: CoroutineDispatcher
private lateinit var testScope: TestScope
- private lateinit var secureSettings: FakeSettings
- private lateinit var userRepository: FakeUserRepository
@Before
fun setUp() {
@@ -109,14 +112,6 @@
scheduler = TestCoroutineScheduler()
dispatcher = UnconfinedTestDispatcher(scheduler)
testScope = TestScope(dispatcher)
- secureSettings = FakeSettings()
- userRepository = FakeUserRepository()
- userRepository.setUserInfos(listOf(SYSTEM_USER))
- secureSettings.putIntForUser(
- BluetoothAutoOnRepository.SETTING_NAME,
- BluetoothAutoOnInteractor.ENABLED,
- SYSTEM_USER_ID
- )
bluetoothTileDialogViewModel =
BluetoothTileDialogViewModel(
deviceItemInteractor,
@@ -124,8 +119,8 @@
// TODO(b/316822488): Create FakeBluetoothAutoOnInteractor.
BluetoothAutoOnInteractor(
BluetoothAutoOnRepository(
- secureSettings,
- userRepository,
+ localBluetoothManager,
+ bluetoothAdapter,
testScope.backgroundScope,
dispatcher
)
@@ -148,7 +143,6 @@
whenever(
mBluetoothTileDialogDelegateDelegateFactory.create(
any(),
- any(),
anyInt(),
ArgumentMatchers.anyBoolean(),
any(),
@@ -157,6 +151,7 @@
)
.thenReturn(bluetoothTileDialogDelegate)
whenever(bluetoothTileDialogDelegate.createDialog()).thenReturn(sysuiDialog)
+ whenever(sysuiDialog.context).thenReturn(mContext)
whenever(bluetoothTileDialogDelegate.bluetoothStateToggle)
.thenReturn(getMutableStateFlow(false))
whenever(bluetoothTileDialogDelegate.deviceItemClick)
@@ -169,7 +164,7 @@
@Test
fun testShowDialog_noAnimation() {
testScope.runTest {
- bluetoothTileDialogViewModel.showDialog(context, null)
+ bluetoothTileDialogViewModel.showDialog(null)
verify(mDialogTransitionAnimator, never()).showFromView(any(), any(), any(), any())
}
@@ -178,7 +173,7 @@
@Test
fun testShowDialog_animated() {
testScope.runTest {
- bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
+ bluetoothTileDialogViewModel.showDialog(LinearLayout(mContext))
verify(mDialogTransitionAnimator).showFromView(any(), any(), nullable(), anyBoolean())
}
@@ -188,7 +183,7 @@
fun testShowDialog_animated_callInBackgroundThread() {
testScope.runTest {
backgroundExecutor.execute {
- bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
+ bluetoothTileDialogViewModel.showDialog(LinearLayout(mContext))
verify(mDialogTransitionAnimator)
.showFromView(any(), any(), nullable(), anyBoolean())
@@ -199,7 +194,7 @@
@Test
fun testShowDialog_fetchDeviceItem() {
testScope.runTest {
- bluetoothTileDialogViewModel.showDialog(context, null)
+ bluetoothTileDialogViewModel.showDialog(null)
verify(deviceItemInteractor).deviceItemUpdate
}
@@ -208,7 +203,7 @@
@Test
fun testShowDialog_withBluetoothStateValue() {
testScope.runTest {
- bluetoothTileDialogViewModel.showDialog(context, null)
+ bluetoothTileDialogViewModel.showDialog(null)
verify(bluetoothStateInteractor).bluetoothStateUpdate
}
@@ -218,7 +213,7 @@
fun testStartSettingsActivity_activityLaunched_dialogDismissed() {
testScope.runTest {
whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
- bluetoothTileDialogViewModel.showDialog(context, null)
+ bluetoothTileDialogViewModel.showDialog(null)
val clickedView = View(context)
bluetoothTileDialogViewModel.onPairNewDeviceClicked(clickedView)
@@ -265,26 +260,22 @@
}
@Test
- fun testIsAutoOnToggleFeatureAvailable_flagOn_settingValueSet_returnTrue() {
+ fun testIsAutoOnToggleFeatureAvailable_returnTrue() {
testScope.runTest {
+ whenever(bluetoothAdapter.isAutoOnSupported).thenReturn(true)
+
val actual = bluetoothTileDialogViewModel.isAutoOnToggleFeatureAvailable()
assertThat(actual).isTrue()
}
}
@Test
- fun testIsAutoOnToggleFeatureAvailable_flagOff_settingValueSet_returnFalse() {
+ fun testIsAutoOnToggleFeatureAvailable_returnFalse() {
testScope.runTest {
- mSetFlagsRule.disableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
+ whenever(bluetoothAdapter.isAutoOnSupported).thenReturn(false)
val actual = bluetoothTileDialogViewModel.isAutoOnToggleFeatureAvailable()
assertThat(actual).isFalse()
}
}
-
- companion object {
- private const val SYSTEM_USER_ID = 0
- private val SYSTEM_USER =
- UserInfo(/* id= */ SYSTEM_USER_ID, /* name= */ "system user", /* flags= */ 0)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ee2eb80..0e89d80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -845,7 +845,6 @@
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATION_ROW_USER_CONTEXT)
public void imageResolver_differentNotificationUser_createsUserContext() throws Exception {
UserHandle user = new UserHandle(33);
Context userContext = new SysuiTestableContext(mContext);
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 6549193..fe0d9d0 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
@@ -23,6 +23,7 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static com.android.systemui.concurrency.FakeExecutorKosmosKt.getFakeExecutor;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static junit.framework.Assert.assertNotNull;
@@ -124,12 +125,11 @@
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
- private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
- private TestScope mTestScope = mKosmos.getTestScope();
- private JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
- private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
- private TestableLooper mTestableLooper;
- private Handler mHandler;
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+ private final TestScope mTestScope = mKosmos.getTestScope();
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+ private final FakeExecutor mExecutor = mKosmos.getFakeExecutor();
+ private final Handler mHandler = mKosmos.getFakeExecutorHandler();
private NotificationTestHelper mHelper;
private NotificationGutsManager mGutsManager;
@@ -171,10 +171,8 @@
@Before
public void setUp() {
- mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- mHandler = Handler.createAsync(mTestableLooper.getLooper());
- mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
+ mHelper = new NotificationTestHelper(mContext, mDependency);
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mWindowRootViewVisibilityInteractor = new WindowRootViewVisibilityInteractor(
@@ -248,7 +246,7 @@
assertTrue(mGutsManager.openGutsInternal(row, 0, 0, menuItem));
assertEquals(View.INVISIBLE, guts.getVisibility());
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
verify(guts).openControls(
anyInt(),
anyInt(),
@@ -261,7 +259,7 @@
verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
verify(row, times(1)).setGutsView(any());
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), false);
}
@@ -352,7 +350,7 @@
when(entry.getGuts()).thenReturn(guts);
assertTrue(mGutsManager.openGutsInternal(row, 0, 0, menuItem));
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
verify(guts).openControls(
anyInt(),
anyInt(),
@@ -365,7 +363,7 @@
row.onDensityOrFontScaleChanged();
mGutsManager.onDensityOrFontScaleChanged(entry);
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 012ff2e..65a960b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -27,12 +27,11 @@
import android.content.pm.launcherApps
import android.graphics.Color
import android.os.Binder
-import android.os.Handler
+import android.os.fakeExecutorHandler
import android.os.userManager
import android.provider.Settings
import android.service.notification.NotificationListenerService.Ranking
import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.util.ArraySet
import android.view.View
@@ -45,6 +44,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.statusBarService
import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
@@ -71,9 +71,7 @@
import com.android.systemui.statusbar.policy.deviceProvisionedController
import com.android.systemui.statusbar.policy.headsUpManager
import com.android.systemui.testKosmos
-import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.kotlin.JavaAdapter
-import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.wmshell.BubblesManager
import java.util.Optional
import junit.framework.Assert
@@ -106,9 +104,8 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val javaAdapter = JavaAdapter(testScope.backgroundScope)
- private val executor = FakeExecutor(FakeSystemClock())
- private lateinit var testableLooper: TestableLooper
- private lateinit var handler: Handler
+ private val executor = kosmos.fakeExecutor
+ private val handler = kosmos.fakeExecutorHandler
private lateinit var helper: NotificationTestHelper
private lateinit var gutsManager: NotificationGutsManager
private lateinit var windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
@@ -148,10 +145,8 @@
MockitoAnnotations.initMocks(this)
val sceneContainerFlags = kosmos.fakeSceneContainerFlags
sceneContainerFlags.enabled = true
- testableLooper = TestableLooper.get(this)
allowTestableLooperAsMainThread()
- handler = Handler.createAsync(testableLooper.getLooper())
- helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ helper = NotificationTestHelper(mContext, mDependency)
Mockito.`when`(accessibilityManager.isTouchExplorationEnabled).thenReturn(false)
windowRootViewVisibilityInteractor =
WindowRootViewVisibilityInteractor(
@@ -227,7 +222,7 @@
Mockito.`when`(row.guts).thenReturn(guts)
Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong())
- testableLooper.processAllMessages()
+ executor.runAllReady()
verify(guts)
.openControls(
ArgumentMatchers.anyInt(),
@@ -247,7 +242,7 @@
ArgumentMatchers.anyBoolean()
)
verify(row, Mockito.times(1)).setGutsView(ArgumentMatchers.any())
- testableLooper.processAllMessages()
+ executor.runAllReady()
verify(headsUpManager).setGutsShown(realRow.entry, false)
}
@@ -343,7 +338,7 @@
Mockito.`when`(entry.row).thenReturn(row)
Mockito.`when`(entry.getGuts()).thenReturn(guts)
Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
- testableLooper.processAllMessages()
+ executor.runAllReady()
verify(guts)
.openControls(
ArgumentMatchers.anyInt(),
@@ -356,7 +351,7 @@
verify(row).setGutsView(ArgumentMatchers.any())
row.onDensityOrFontScaleChanged()
gutsManager.onDensityOrFontScaleChanged(entry)
- testableLooper.processAllMessages()
+ executor.runAllReady()
gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
verify(guts)
.closeControls(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 09a3eb48..954335e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -615,10 +615,8 @@
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- if (com.android.systemui.Flags.notificationRowUserContext()) {
- inflater.setFactory2(new RowInflaterTask.RowAsyncLayoutInflater(entry, mSystemClock,
- mRowInflaterTaskLogger));
- }
+ inflater.setFactory2(new RowInflaterTask.RowAsyncLayoutInflater(entry, mSystemClock,
+ mRowInflaterTaskLogger));
mRow = (ExpandableNotificationRow) inflater.inflate(
R.layout.status_bar_notification_row,
null /* root */,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
index 8f88501..a15b4cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
@@ -25,8 +25,6 @@
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import android.view.View;
@@ -44,7 +42,6 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
-@RunWithLooper
public class NotificationBigPictureTemplateViewWrapperTest extends SysuiTestCase {
private View mView;
@@ -53,11 +50,7 @@
@Before
public void setup() throws Exception {
- allowTestableLooperAsMainThread();
- NotificationTestHelper helper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
+ NotificationTestHelper helper = new NotificationTestHelper(mContext, mDependency);
mView = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.notification_template_material_big_picture, null);
mRow = helper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
index 3fa68bb..fe2971c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
@@ -18,8 +18,6 @@
import android.graphics.drawable.AnimatedImageDrawable
import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.R
@@ -41,7 +39,6 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
-@RunWithLooper
class NotificationConversationTemplateViewWrapperTest : SysuiTestCase() {
private lateinit var mRow: ExpandableNotificationRow
@@ -49,8 +46,7 @@
@Before
fun setUp() {
- allowTestableLooperAsMainThread()
- helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ helper = NotificationTestHelper(mContext, mDependency)
mRow = helper.createRow()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 45f7c5a..2d72c7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.notification.row.wrapper;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.RemoteViews;
@@ -36,18 +34,13 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
public class NotificationCustomViewWrapperTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
@Before
public void setUp() throws Exception {
- allowTestableLooperAsMainThread();
- NotificationTestHelper helper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
+ NotificationTestHelper helper = new NotificationTestHelper(mContext, mDependency);
mRow = helper.createRow();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
index c0444b5..f26c18b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
@@ -18,8 +18,6 @@
import android.graphics.drawable.AnimatedImageDrawable
import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.widget.MessagingGroup
@@ -39,7 +37,6 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
-@RunWithLooper
class NotificationMessagingTemplateViewWrapperTest : SysuiTestCase() {
private lateinit var mRow: ExpandableNotificationRow
@@ -47,8 +44,7 @@
@Before
fun setUp() {
- allowTestableLooperAsMainThread()
- helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ helper = NotificationTestHelper(mContext, mDependency)
mRow = helper.createRow()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
index f7632aa..54eed26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
@@ -69,7 +69,7 @@
TestUiOffloadThread(looper.looper)
)
- helper = NotificationTestHelper(mContext, mDependency, looper)
+ helper = NotificationTestHelper(mContext, mDependency)
row = helper.createRow()
// Some code in the view iterates through parents so we need some extra containers around
// it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 93a9e59..e3a77d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -39,7 +39,6 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
-@RunWithLooper
public class NotificationViewWrapperTest extends SysuiTestCase {
private View mView;
@@ -48,13 +47,9 @@
@Before
public void setup() throws Exception {
- allowTestableLooperAsMainThread();
mView = mock(View.class);
when(mView.getContext()).thenReturn(mContext);
- NotificationTestHelper helper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
+ NotificationTestHelper helper = new NotificationTestHelper(mContext, mDependency);
mRow = helper.createRow();
mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 138e1fa..c308a98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -130,35 +130,35 @@
}
@Test
- fun testShouldShowEmptyShadeView_trueWhenNoNotifs() =
+ fun testShouldIncludeEmptyShadeView_trueWhenNoNotifs() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
runCurrent()
// THEN empty shade is visible
- assertThat(shouldShow).isTrue()
+ assertThat(shouldInclude).isTrue()
}
@Test
- fun testShouldShowEmptyShadeView_falseWhenNotifs() =
+ fun testShouldIncludeEmptyShadeView_falseWhenNotifs() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldShow).isFalse()
+ assertThat(shouldInclude).isFalse()
}
@Test
- fun testShouldShowEmptyShadeView_falseWhenQsExpandedDefault() =
+ fun testShouldIncludeEmptyShadeView_falseWhenQsExpandedDefault() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -167,13 +167,13 @@
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldShow).isFalse()
+ assertThat(shouldInclude).isFalse()
}
@Test
- fun testShouldShowEmptyShadeView_trueWhenQsExpandedInSplitShade() =
+ fun testShouldIncludeEmptyShadeView_trueWhenQsExpandedInSplitShade() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -185,13 +185,13 @@
runCurrent()
// THEN empty shade is visible
- assertThat(shouldShow).isTrue()
+ assertThat(shouldInclude).isTrue()
}
@Test
- fun testShouldShowEmptyShadeView_trueWhenLockedShade() =
+ fun testShouldIncludeEmptyShadeView_trueWhenLockedShade() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -200,13 +200,13 @@
runCurrent()
// THEN empty shade is visible
- assertThat(shouldShow).isTrue()
+ assertThat(shouldInclude).isTrue()
}
@Test
- fun testShouldShowEmptyShadeView_falseWhenKeyguard() =
+ fun testShouldIncludeEmptyShadeView_falseWhenKeyguard() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -215,13 +215,13 @@
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldShow).isFalse()
+ assertThat(shouldInclude).isFalse()
}
@Test
- fun testShouldShowEmptyShadeView_falseWhenStartingToSleep() =
+ fun testShouldIncludeEmptyShadeView_falseWhenStartingToSleep() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -232,7 +232,7 @@
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldShow).isFalse()
+ assertThat(shouldInclude).isFalse()
}
@Test
@@ -282,9 +282,9 @@
}
@Test
- fun testShouldShowFooterView_trueWhenShade() =
+ fun testShouldIncludeFooterView_trueWhenShade() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -294,13 +294,13 @@
runCurrent()
// THEN footer is visible
- assertThat(shouldShow?.value).isTrue()
+ assertThat(shouldInclude?.value).isTrue()
}
@Test
- fun testShouldShowFooterView_trueWhenLockedShade() =
+ fun testShouldIncludeFooterView_trueWhenLockedShade() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -310,13 +310,13 @@
runCurrent()
// THEN footer is visible
- assertThat(shouldShow?.value).isTrue()
+ assertThat(shouldInclude?.value).isTrue()
}
@Test
- fun testShouldShowFooterView_falseWhenKeyguard() =
+ fun testShouldIncludeFooterView_falseWhenKeyguard() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -325,13 +325,13 @@
runCurrent()
// THEN footer is not visible
- assertThat(shouldShow?.value).isFalse()
+ assertThat(shouldInclude?.value).isFalse()
}
@Test
- fun testShouldShowFooterView_falseWhenUserNotSetUp() =
+ fun testShouldIncludeFooterView_falseWhenUserNotSetUp() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -343,13 +343,13 @@
runCurrent()
// THEN footer is not visible
- assertThat(shouldShow?.value).isFalse()
+ assertThat(shouldInclude?.value).isFalse()
}
@Test
- fun testShouldShowFooterView_falseWhenStartingToSleep() =
+ fun testShouldIncludeFooterView_falseWhenStartingToSleep() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -361,13 +361,13 @@
runCurrent()
// THEN footer is not visible
- assertThat(shouldShow?.value).isFalse()
+ assertThat(shouldInclude?.value).isFalse()
}
@Test
- fun testShouldShowFooterView_falseWhenQsExpandedDefault() =
+ fun testShouldIncludeFooterView_falseWhenQsExpandedDefault() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -380,13 +380,13 @@
runCurrent()
// THEN footer is not visible
- assertThat(shouldShow?.value).isFalse()
+ assertThat(shouldInclude?.value).isFalse()
}
@Test
- fun testShouldShowFooterView_trueWhenQsExpandedSplitShade() =
+ fun testShouldIncludeFooterView_trueWhenQsExpandedSplitShade() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -401,13 +401,13 @@
runCurrent()
// THEN footer is visible
- assertThat(shouldShow?.value).isTrue()
+ assertThat(shouldInclude?.value).isTrue()
}
@Test
- fun testShouldShowFooterView_falseWhenRemoteInputActive() =
+ fun testShouldIncludeFooterView_falseWhenRemoteInputActive() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -419,29 +419,13 @@
runCurrent()
// THEN footer is not visible
- assertThat(shouldShow?.value).isFalse()
+ assertThat(shouldInclude?.value).isFalse()
}
@Test
- fun testShouldShowFooterView_falseWhenShadeIsClosed() =
+ fun testShouldIncludeFooterView_animatesWhenShade() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
-
- // WHEN has notifs
- activeNotificationListRepository.setActiveNotifs(count = 2)
- // AND shade is closed
- fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(0f)
- runCurrent()
-
- // THEN footer is not visible
- assertThat(shouldShow?.value).isFalse()
- }
-
- @Test
- fun testShouldShowFooterView_animatesWhenShade() =
- testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -451,13 +435,13 @@
runCurrent()
// THEN footer visibility animates
- assertThat(shouldShow?.isAnimating).isTrue()
+ assertThat(shouldInclude?.isAnimating).isTrue()
}
@Test
- fun testShouldShowFooterView_notAnimatingOnKeyguard() =
+ fun testShouldIncludeFooterView_notAnimatingOnKeyguard() =
testScope.runTest {
- val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+ val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -467,7 +451,35 @@
runCurrent()
// THEN footer visibility does not animate
- assertThat(shouldShow?.isAnimating).isFalse()
+ assertThat(shouldInclude?.isAnimating).isFalse()
+ }
+
+ @Test
+ fun testShouldHideFooterView_trueWhenShadeIsClosed() =
+ testScope.runTest {
+ val shouldHide by collectLastValue(underTest.shouldHideFooterView)
+
+ // WHEN shade is closed
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN footer is hidden
+ assertThat(shouldHide).isTrue()
+ }
+
+ @Test
+ fun testShouldHideFooterView_falseWhenShadeIsOpen() =
+ testScope.runTest {
+ val shouldHide by collectLastValue(underTest.shouldHideFooterView)
+
+ // WHEN shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN footer is hidden
+ assertThat(shouldHide).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 9855651..f761bcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -868,6 +868,24 @@
}
@Test
+ fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers() =
+ testScope.runTest {
+ // Use the [StateFlow.value] getter so we can prove that the collection happens
+ // even when there is no [Job]
+
+ // Starts out default
+ assertThat(underTest.networkName.value).isEqualTo(DEFAULT_NAME_MODEL)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.value!!.onReceive(context, intent)
+
+ // The value is still there despite no active subscribers
+ assertThat(underTest.networkName.value).isEqualTo(intent.toNetworkNameModel(SEP))
+ }
+
+ @Test
fun operatorAlphaShort_tracked() =
testScope.runTest {
var latest: String? = null
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index e861892..c879588 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -19,6 +19,7 @@
package com.android.systemui.kosmos
import android.content.applicationContext
+import android.os.fakeExecutorHandler
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.bouncerRepository
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
@@ -27,6 +28,7 @@
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -65,6 +67,8 @@
val testScope by lazy { kosmos.testScope }
val fakeFeatureFlags by lazy { kosmos.fakeFeatureFlagsClassic }
val fakeSceneContainerFlags by lazy { kosmos.fakeSceneContainerFlags }
+ val fakeExecutor by lazy { kosmos.fakeExecutor }
+ val fakeExecutorHandler by lazy { kosmos.fakeExecutorHandler }
val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
val configurationInteractor by lazy { kosmos.configurationInteractor }
val bouncerRepository by lazy { kosmos.bouncerRepository }
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 532db12..c130cee 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -16,6 +16,7 @@
flag {
name: "autofill_credman_dev_integration"
+ is_exported: true
namespace: "autofill"
description: "Guards against Autofill-Credman Phase1 developer integration via new APIs"
bug: "320730001"
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 53c0f58..0f951746 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -12360,7 +12360,8 @@
}
private boolean callerHasPermission(String permission) {
- return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
}
/** @return true if projection is a valid MediaProjection that can project audio. */
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 04e7f77..851d197 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -894,6 +894,7 @@
@Nullable
private HdrBrightnessData mHdrBrightnessData;
+ // Null if low brightness mode is disabled - in config or by flag.
@Nullable
public LowBrightnessData mLowBrightnessData;
@@ -1063,6 +1064,9 @@
* @return The brightness mapping nits array.
*/
public float[] getNits() {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mNits;
+ }
return mNits;
}
@@ -1071,7 +1075,11 @@
*
* @return The backlight mapping value array.
*/
+ @VisibleForTesting
public float[] getBacklight() {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mBacklight;
+ }
return mBacklight;
}
@@ -1083,9 +1091,26 @@
* @return backlight value on the HAL scale of 0-1
*/
public float getBacklightFromBrightness(float brightness) {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mBrightnessToBacklight.interpolate(brightness);
+ }
return mBrightnessToBacklightSpline.interpolate(brightness);
}
+ private float getBrightnessFromBacklight(float brightness) {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mBacklightToBrightness.interpolate(brightness);
+ }
+ return mBacklightToBrightnessSpline.interpolate(brightness);
+ }
+
+ private Spline getBacklightToBrightnessSpline() {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mBacklightToBrightness;
+ }
+ return mBacklightToBrightnessSpline;
+ }
+
/**
* Calculates the nits value for the specified backlight value if a mapping exists.
*
@@ -1093,6 +1118,14 @@
* exits.
*/
public float getNitsFromBacklight(float backlight) {
+ if (mLowBrightnessData != null) {
+ if (mLowBrightnessData.mBacklightToNits == null) {
+ return INVALID_NITS;
+ }
+ backlight = Math.max(backlight, mBacklightMinimum);
+ return mLowBrightnessData.mBacklightToNits.interpolate(backlight);
+ }
+
if (mBacklightToNitsSpline == null) {
return INVALID_NITS;
}
@@ -1100,6 +1133,20 @@
return mBacklightToNitsSpline.interpolate(backlight);
}
+ private float getBacklightFromNits(float nits) {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mNitsToBacklight.interpolate(nits);
+ }
+ return mNitsToBacklightSpline.interpolate(nits);
+ }
+
+ private Spline getNitsToBacklightSpline() {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mNitsToBacklight;
+ }
+ return mNitsToBacklightSpline;
+ }
+
/**
* @return true if there is sdrHdrRatioMap, false otherwise.
*/
@@ -1126,13 +1173,13 @@
float ratio = Math.min(mSdrToHdrRatioSpline.interpolate(nits), maxDesiredHdrSdrRatio);
float hdrNits = nits * ratio;
- if (mNitsToBacklightSpline == null) {
+ if (getNitsToBacklightSpline() == null) {
return PowerManager.BRIGHTNESS_INVALID;
}
- float hdrBacklight = mNitsToBacklightSpline.interpolate(hdrNits);
+ float hdrBacklight = getBacklightFromNits(hdrNits);
hdrBacklight = Math.max(mBacklightMinimum, Math.min(mBacklightMaximum, hdrBacklight));
- float hdrBrightness = mBacklightToBrightnessSpline.interpolate(hdrBacklight);
+ float hdrBrightness = getBrightnessFromBacklight(hdrBacklight);
if (DEBUG) {
Slog.d(TAG, "getHdrBrightnessFromSdr: sdr brightness " + brightness
@@ -1154,6 +1201,9 @@
* @return brightness array
*/
public float[] getBrightness() {
+ if (mLowBrightnessData != null) {
+ return mLowBrightnessData.mBrightness;
+ }
return mBrightness;
}
@@ -1987,7 +2037,8 @@
+ "mHdrBrightnessData= " + mHdrBrightnessData + "\n"
+ "mBrightnessCapForWearBedtimeMode= " + mBrightnessCapForWearBedtimeMode
+ "\n"
- + (mLowBrightnessData != null ? mLowBrightnessData.toString() : "")
+ + "mLowBrightnessData:" + (mLowBrightnessData != null
+ ? mLowBrightnessData.toString() : "null")
+ "}";
}
@@ -2588,9 +2639,9 @@
// A negative value means that there's no threshold
mLowDisplayBrightnessThresholds[i] = thresholdNits;
} else {
- float thresholdBacklight = mNitsToBacklightSpline.interpolate(thresholdNits);
+ float thresholdBacklight = getBacklightFromNits(thresholdNits);
mLowDisplayBrightnessThresholds[i] =
- mBacklightToBrightnessSpline.interpolate(thresholdBacklight);
+ getBrightnessFromBacklight(thresholdBacklight);
}
mLowAmbientBrightnessThresholds[i] = lowerThresholdDisplayBrightnessPoints
@@ -2639,9 +2690,9 @@
// A negative value means that there's no threshold
mHighDisplayBrightnessThresholds[i] = thresholdNits;
} else {
- float thresholdBacklight = mNitsToBacklightSpline.interpolate(thresholdNits);
+ float thresholdBacklight = getBacklightFromNits(thresholdNits);
mHighDisplayBrightnessThresholds[i] =
- mBacklightToBrightnessSpline.interpolate(thresholdBacklight);
+ getBrightnessFromBacklight(thresholdBacklight);
}
mHighAmbientBrightnessThresholds[i] = higherThresholdDisplayBrightnessPoints
@@ -2658,7 +2709,7 @@
loadAutoBrightnessBrighteningLightDebounceIdle(autoBrightness);
loadAutoBrightnessDarkeningLightDebounceIdle(autoBrightness);
mDisplayBrightnessMapping = new DisplayBrightnessMappingConfig(mContext, mFlags,
- autoBrightness, mBacklightToBrightnessSpline);
+ autoBrightness, getBacklightToBrightnessSpline());
loadEnableAutoBrightness(autoBrightness);
}
@@ -2832,17 +2883,10 @@
// These splines are used to convert from the system brightness value to the HAL backlight
// value
private void createBacklightConversionSplines() {
- if (mLowBrightnessData != null) {
- mBrightnessToBacklightSpline = mLowBrightnessData.mBrightnessToBacklight;
- mBacklightToBrightnessSpline = mLowBrightnessData.mBacklightToBrightness;
- mBacklightToNitsSpline = mLowBrightnessData.mBacklightToNits;
- mNitsToBacklightSpline = mLowBrightnessData.mNitsToBacklight;
- mNits = mLowBrightnessData.mNits;
- mBrightness = mLowBrightnessData.mBrightness;
- mBacklight = mLowBrightnessData.mBacklight;
- return;
- }
+
+ // Create original brightness splines - not using low brightness mode arrays - this is
+ // so that we can continue to log the original brightness splines.
mBrightness = new float[mBacklight.length];
for (int i = 0; i < mBrightness.length; i++) {
@@ -2884,7 +2928,7 @@
+ mBacklightMaximum);
}
mHbmData.transitionPoint =
- mBacklightToBrightnessSpline.interpolate(transitionPointBacklightScale);
+ getBrightnessFromBacklight(transitionPointBacklightScale);
final HbmTiming hbmTiming = hbm.getTiming_all();
mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
@@ -2953,7 +2997,7 @@
continue;
}
luxToTransitionPointMap.put(lux,
- mBacklightToBrightnessSpline.interpolate(maxBrightness));
+ getBrightnessFromBacklight(maxBrightness));
}
if (!luxToTransitionPointMap.isEmpty()) {
mLuxThrottlingData.put(mappedType, luxToTransitionPointMap);
@@ -3048,7 +3092,7 @@
private void loadAutoBrightnessConfigsFromConfigXml() {
mDisplayBrightnessMapping = new DisplayBrightnessMappingConfig(mContext, mFlags,
- /* autoBrightnessConfig= */ null, mBacklightToBrightnessSpline);
+ /* autoBrightnessConfig= */ null, getBacklightToBrightnessSpline());
}
private void loadBrightnessChangeThresholdsFromXml() {
diff --git a/services/core/java/com/android/server/feature/dropbox_flags.aconfig b/services/core/java/com/android/server/feature/dropbox_flags.aconfig
index fee4bf3..14e964b 100644
--- a/services/core/java/com/android/server/feature/dropbox_flags.aconfig
+++ b/services/core/java/com/android/server/feature/dropbox_flags.aconfig
@@ -2,6 +2,7 @@
flag{
name: "enable_read_dropbox_permission"
+ is_exported: true
namespace: "preload_safety"
description: "Feature flag for permission to Read dropbox data"
bug: "287512663"
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index b2e01c5..c42ccea 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "power_monitor_api"
+ is_exported: true
namespace: "backstage_power"
description: "Feature flag for ODPM API"
bug: "295027807"
diff --git a/services/core/java/com/android/server/webkit/flags.aconfig b/services/core/java/com/android/server/webkit/flags.aconfig
index 1411acc..2afbcd6 100644
--- a/services/core/java/com/android/server/webkit/flags.aconfig
+++ b/services/core/java/com/android/server/webkit/flags.aconfig
@@ -2,6 +2,7 @@
flag {
name: "update_service_v2"
+ is_exported: true
namespace: "webview"
description: "Using a new version of the WebView update service"
bug: "308907090"
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
index 5460e4e87..64dbe71 100644
--- a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
@@ -43,6 +43,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -392,6 +393,7 @@
}
@Test
+ @Ignore("Can be enabled only after b/330536267 is ready")
public void testChoreographerDivisorRefreshRate() {
for (int divisor : new int[]{2, 3}) {
CountDownLatch continueLatch = new CountDownLatch(1);
@@ -420,6 +422,7 @@
}
@Test
+ @Ignore("Can be enabled only after b/330536267 is ready")
public void testChoreographerAttachedAfterSetFrameRate() {
Log.i(TAG, "starting testChoreographerAttachedAfterSetFrameRate");
diff --git a/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java b/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java
index df003b6..fb7a6ab 100644
--- a/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java
+++ b/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java
@@ -18,6 +18,7 @@
import com.android.asllib.AndroidSafetyLabel;
import com.android.asllib.AndroidSafetyLabel.Format;
+import com.android.asllib.util.MalformedXmlException;
import org.xml.sax.SAXException;
@@ -32,7 +33,11 @@
/** Takes the options to make file conversion. */
public static void main(String[] args)
- throws IOException, ParserConfigurationException, SAXException, TransformerException {
+ throws IOException,
+ ParserConfigurationException,
+ SAXException,
+ TransformerException,
+ MalformedXmlException {
String inFile = null;
String outFile = null;
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java
index 0f7ce68..bc8063e 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java
@@ -16,6 +16,8 @@
package com.android.asllib;
+import com.android.asllib.util.MalformedXmlException;
+
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
@@ -53,7 +55,7 @@
/** Reads a {@link AndroidSafetyLabel} from an {@link InputStream}. */
// TODO(b/329902686): Support parsing from on-device.
public static AndroidSafetyLabel readFromStream(InputStream in, Format format)
- throws IOException, ParserConfigurationException, SAXException {
+ throws IOException, ParserConfigurationException, SAXException, MalformedXmlException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Document document = factory.newDocumentBuilder().parse(in);
@@ -65,9 +67,9 @@
return new AndroidSafetyLabelFactory()
.createFromHrElements(
- XmlUtils.asElementList(
- document.getElementsByTagName(
- XmlUtils.HR_TAG_APP_METADATA_BUNDLES)));
+ List.of(
+ XmlUtils.getSingleElement(
+ document, XmlUtils.HR_TAG_APP_METADATA_BUNDLES)));
case ON_DEVICE:
throw new IllegalArgumentException(
"Parsing from on-device format is not supported at this time.");
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java
index 9b0f05b..7e7fcf9 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java
@@ -16,6 +16,8 @@
package com.android.asllib;
+import com.android.asllib.util.MalformedXmlException;
+
import org.w3c.dom.Element;
import java.util.List;
@@ -24,7 +26,8 @@
/** Creates an {@link AndroidSafetyLabel} from human-readable DOM element */
@Override
- public AndroidSafetyLabel createFromHrElements(List<Element> appMetadataBundles) {
+ public AndroidSafetyLabel createFromHrElements(List<Element> appMetadataBundles)
+ throws MalformedXmlException {
Element appMetadataBundlesEle = XmlUtils.getSingleElement(appMetadataBundles);
Element safetyLabelsEle =
XmlUtils.getSingleChildElement(
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java
index b607353..b8f9f0e 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java
@@ -16,6 +16,8 @@
package com.android.asllib;
+import com.android.asllib.util.MalformedXmlException;
+
import org.w3c.dom.Element;
import java.util.List;
@@ -23,5 +25,5 @@
public interface AslMarshallableFactory<T extends AslMarshallable> {
/** Creates an {@link AslMarshallableFactory} from human-readable DOM element */
- T createFromHrElements(List<Element> elements);
+ T createFromHrElements(List<Element> elements) throws MalformedXmlException;
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java
index 5a52591..d946345 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java
@@ -16,6 +16,8 @@
package com.android.asllib;
+import com.android.asllib.util.MalformedXmlException;
+
import org.w3c.dom.Element;
import java.util.HashMap;
@@ -24,12 +26,16 @@
public class DataCategoryFactory implements AslMarshallableFactory<DataCategory> {
@Override
- public DataCategory createFromHrElements(List<Element> elements) {
+ public DataCategory createFromHrElements(List<Element> elements) throws MalformedXmlException {
String categoryName = null;
Map<String, DataType> dataTypeMap = new HashMap<String, DataType>();
for (Element ele : elements) {
categoryName = ele.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY);
String dataTypeName = ele.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+ if (!DataTypeConstants.getValidDataTypes().contains(dataTypeName)) {
+ throw new MalformedXmlException(
+ String.format("Unrecognized data type name: %s", dataTypeName));
+ }
dataTypeMap.put(dataTypeName, new DataTypeFactory().createFromHrElements(List.of(ele)));
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java
index c758ab9..1adb140 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java
@@ -16,6 +16,8 @@
package com.android.asllib;
+import com.android.asllib.util.MalformedXmlException;
+
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -29,7 +31,7 @@
/** Creates a {@link DataLabels} from the human-readable DOM element. */
@Override
- public DataLabels createFromHrElements(List<Element> elements) {
+ public DataLabels createFromHrElements(List<Element> elements) throws MalformedXmlException {
Element ele = XmlUtils.getSingleElement(elements);
Map<String, DataCategory> dataAccessed =
getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_ACCESSED);
@@ -37,13 +39,54 @@
getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED);
Map<String, DataCategory> dataShared =
getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED);
+
+ // Validate booleans such as isCollectionOptional, isSharingOptional.
+ for (DataCategory dataCategory : dataAccessed.values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsSharingOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isSharingOptional was unexpectedly defined on a DataType"
+ + " belonging to data accessed: %s",
+ dataType.getDataTypeName()));
+ }
+ if (dataType.getIsCollectionOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isCollectionOptional was unexpectedly defined on a DataType"
+ + " belonging to data accessed: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+ for (DataCategory dataCategory : dataCollected.values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsSharingOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isSharingOptional was unexpectedly defined on a DataType"
+ + " belonging to data collected: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+ for (DataCategory dataCategory : dataShared.values()) {
+ for (DataType dataType : dataCategory.getDataTypes().values()) {
+ if (dataType.getIsCollectionOptional() != null) {
+ throw new MalformedXmlException(
+ String.format(
+ "isCollectionOptional was unexpectedly defined on a DataType"
+ + " belonging to data shared: %s",
+ dataType.getDataTypeName()));
+ }
+ }
+ }
+
return new DataLabels(dataAccessed, dataCollected, dataShared);
}
private static Map<String, DataCategory> getDataCategoriesWithTag(
- Element dataLabelsEle, String dataCategoryUsageTypeTag) {
- Map<String, Map<String, DataType>> dataTypeMap =
- new HashMap<String, Map<String, DataType>>();
+ Element dataLabelsEle, String dataCategoryUsageTypeTag) throws MalformedXmlException {
NodeList dataUsedNodeList = dataLabelsEle.getElementsByTagName(dataCategoryUsageTypeTag);
Map<String, DataCategory> dataCategoryMap = new HashMap<String, DataCategory>();
@@ -51,6 +94,10 @@
for (int i = 0; i < dataUsedNodeList.getLength(); i++) {
Element dataUsedEle = (Element) dataUsedNodeList.item(i);
String dataCategoryName = dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY);
+ if (!DataCategoryConstants.getValidDataCategories().contains(dataCategoryName)) {
+ throw new MalformedXmlException(
+ String.format("Unrecognized category name: %s", dataCategoryName));
+ }
dataCategoryNames.add(dataCategoryName);
}
for (String dataCategoryName : dataCategoryNames) {
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java
index 99f8a8b..e3d1587 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java
@@ -35,10 +35,10 @@
.collect(Collectors.toUnmodifiableSet());
Boolean isCollectionOptional =
XmlUtils.fromString(
- hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL));
+ hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL));
Boolean isSharingOptional =
XmlUtils.fromString(
- hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL));
+ hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL));
Boolean ephemeral =
XmlUtils.fromString(hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_EPHEMERAL));
return new DataType(
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java
index 68e83fe..80b9f57 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java
@@ -16,6 +16,8 @@
package com.android.asllib;
+import com.android.asllib.util.MalformedXmlException;
+
import org.w3c.dom.Element;
import java.util.List;
@@ -24,7 +26,7 @@
/** Creates a {@link SafetyLabels} from the human-readable DOM element. */
@Override
- public SafetyLabels createFromHrElements(List<Element> elements) {
+ public SafetyLabels createFromHrElements(List<Element> elements) throws MalformedXmlException {
Element safetyLabelsEle = XmlUtils.getSingleElement(elements);
Long version;
try {
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java
index 3c89a30..3bc9ccc 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java
@@ -16,6 +16,8 @@
package com.android.asllib;
+import com.android.asllib.util.MalformedXmlException;
+
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -61,30 +63,34 @@
public static final String FALSE_STR = "false";
/** Gets the single top-level {@link Element} having the {@param tagName}. */
- public static Element getSingleElement(Document doc, String tagName) {
+ public static Element getSingleElement(Document doc, String tagName)
+ throws MalformedXmlException {
var elements = doc.getElementsByTagName(tagName);
- return getSingleElement(elements);
+ return getSingleElement(elements, tagName);
}
/**
* Gets the single {@link Element} within {@param parentEle} and having the {@param tagName}.
*/
- public static Element getSingleChildElement(Element parentEle, String tagName) {
+ public static Element getSingleChildElement(Element parentEle, String tagName)
+ throws MalformedXmlException {
var elements = parentEle.getElementsByTagName(tagName);
- return getSingleElement(elements);
+ return getSingleElement(elements, tagName);
}
/** Gets the single {@link Element} from {@param elements} */
- public static Element getSingleElement(NodeList elements) {
+ public static Element getSingleElement(NodeList elements, String tagName)
+ throws MalformedXmlException {
if (elements.getLength() != 1) {
- throw new IllegalArgumentException(
+ throw new MalformedXmlException(
String.format(
- "Expected 1 element in NodeList but got %s.", elements.getLength()));
+ "Expected 1 element \"%s\" in NodeList but got %s.",
+ tagName, elements.getLength()));
}
var elementAsNode = elements.item(0);
if (!(elementAsNode instanceof Element)) {
- throw new IllegalStateException(
- String.format("%s was not an element.", elementAsNode.getNodeName()));
+ throw new MalformedXmlException(
+ String.format("%s was not a valid XML element.", tagName));
}
return ((Element) elementAsNode);
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java
new file mode 100644
index 0000000..216df56
--- /dev/null
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asllib.util;
+
+public class MalformedXmlException extends Exception {
+ /** Constructs an {@code MalformedXmlException} with no detail message. */
+ public MalformedXmlException() {
+ super();
+ }
+
+ /**
+ * Constructs an {@code MalformedXmlException} with the specified detail message.
+ *
+ * @param s the detail message.
+ */
+ public MalformedXmlException(String s) {
+ super(s);
+ }
+}
diff --git a/wifi/wifi.aconfig b/wifi/wifi.aconfig
index 6ac986e..6c4e4c3 100644
--- a/wifi/wifi.aconfig
+++ b/wifi/wifi.aconfig
@@ -2,6 +2,7 @@
flag {
name: "get_device_cross_akm_roaming_support"
+ is_exported: true
namespace: "wifi"
description: "Add new API to get the device support for CROSS-AKM roaming"
bug: "313038031"