diff options
88 files changed, 1446 insertions, 527 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d24b677b1d72..03a97ba1d977 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -997,6 +997,8 @@ public class Activity extends ContextThemeWrapper private ComponentCallbacksController mCallbacksController; + @Nullable private IVoiceInteractionManagerService mVoiceInteractionManagerService; + private final WindowControllerCallback mWindowControllerCallback = new WindowControllerCallback() { /** @@ -1606,18 +1608,17 @@ public class Activity extends ContextThemeWrapper private void notifyVoiceInteractionManagerServiceActivityEvent( @VoiceInteractionSession.VoiceInteractionActivityEventType int type) { - - final IVoiceInteractionManagerService service = - IVoiceInteractionManagerService.Stub.asInterface( - ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); - if (service == null) { - Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get " - + "VoiceInteractionManagerService"); - return; + if (mVoiceInteractionManagerService == null) { + mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface( + ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); + if (mVoiceInteractionManagerService == null) { + Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get " + + "VoiceInteractionManagerService"); + return; + } } - try { - service.notifyActivityEventChanged(mToken, type); + mVoiceInteractionManagerService.notifyActivityEventChanged(mToken, type); } catch (RemoteException e) { // Empty } diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index a51b9d3956df..27f9f54d9522 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -1406,6 +1406,17 @@ public class PropertyInvalidatedCache<Query, Result> { } /** + * Return the number of entries in the cache. This is used for testing and has package-only + * visibility. + * @hide + */ + public int size() { + synchronized (mLock) { + return mCache.size(); + } + } + + /** * Returns a list of caches alive at the current time. */ @GuardedBy("sGlobalLock") @@ -1612,8 +1623,12 @@ public class PropertyInvalidatedCache<Query, Result> { * @hide */ public static void onTrimMemory() { - for (PropertyInvalidatedCache pic : getActiveCaches()) { - pic.clear(); + ArrayList<PropertyInvalidatedCache> activeCaches; + synchronized (sGlobalLock) { + activeCaches = getActiveCaches(); + } + for (int i = 0; i < activeCaches.size(); i++) { + activeCaches.get(i).clear(); } } } diff --git a/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java index 41f2f9c28349..b86b97c76740 100644 --- a/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java +++ b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java @@ -17,7 +17,7 @@ package android.database.sqlite; /** - * Thrown if the the bind or column parameter index is out of range + * Thrown if the bind or column parameter index is out of range. */ public class SQLiteBindOrColumnIndexOutOfRangeException extends SQLiteException { public SQLiteBindOrColumnIndexOutOfRangeException() {} diff --git a/core/java/android/database/sqlite/SQLiteDiskIOException.java b/core/java/android/database/sqlite/SQLiteDiskIOException.java index 01b2069c23db..152d90a76ba6 100644 --- a/core/java/android/database/sqlite/SQLiteDiskIOException.java +++ b/core/java/android/database/sqlite/SQLiteDiskIOException.java @@ -17,7 +17,7 @@ package android.database.sqlite; /** - * An exception that indicates that an IO error occured while accessing the + * Indicates that an IO error occurred while accessing the * SQLite database file. */ public class SQLiteDiskIOException extends SQLiteException { diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 5e2b40cbaf75..4dc6e9310220 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -392,6 +392,23 @@ public final class CameraManager { * except that it uses {@link java.util.concurrent.Executor} as an argument * instead of {@link android.os.Handler}.</p> * + * <p>Note: If the order between some availability callbacks matters, the implementation of the + * executor should handle those callbacks in the same thread to maintain the callbacks' order. + * Some examples are:</p> + * + * <ul> + * + * <li>{@link AvailabilityCallback#onCameraAvailable} and + * {@link AvailabilityCallback#onCameraUnavailable} of the same camera ID.</li> + * + * <li>{@link AvailabilityCallback#onCameraAvailable} or + * {@link AvailabilityCallback#onCameraUnavailable} of a logical multi-camera, and {@link + * AvailabilityCallback#onPhysicalCameraUnavailable} or + * {@link AvailabilityCallback#onPhysicalCameraAvailable} of its physical + * cameras.</li> + * + * </ul> + * * @param executor The executor which will be used to invoke the callback. * @param callback the new callback to send camera availability notices to * @@ -2336,15 +2353,6 @@ public final class CameraManager { final AvailabilityCallback callback = mCallbackMap.keyAt(i); postSingleUpdate(callback, executor, id, null /*physicalId*/, status); - - // Send the NOT_PRESENT state for unavailable physical cameras - if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) { - ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id); - for (String unavailableId : unavailableIds) { - postSingleUpdate(callback, executor, id, unavailableId, - ICameraServiceListener.STATUS_NOT_PRESENT); - } - } } } // onStatusChangedLocked diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java index 0fb9f57f5f57..b0e847cd53f9 100644 --- a/core/java/android/service/autofill/FillEventHistory.java +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -166,7 +166,7 @@ public final class FillEventHistory implements Parcelable { } /** - * Description of an event that occured after the latest call to + * Description of an event that occurred after the latest call to * {@link FillCallback#onSuccess(FillResponse)}. */ public static final class Event { diff --git a/core/java/android/service/resumeonreboot/OWNERS b/core/java/android/service/resumeonreboot/OWNERS index 721fbaf2d4ed..8ffb76a69923 100644 --- a/core/java/android/service/resumeonreboot/OWNERS +++ b/core/java/android/service/resumeonreboot/OWNERS @@ -1 +1,2 @@ +aveena@google.com ejyzhang@google.com
\ No newline at end of file diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4988362f38db..1834d19a8e67 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5045,7 +5045,7 @@ public final class ViewRootImpl implements ViewParent, } void reportKeepClearAreasChanged() { - if (!mHasPendingKeepClearAreaChange) { + if (!mHasPendingKeepClearAreaChange || mView == null) { return; } mHasPendingKeepClearAreaChange = false; diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 017bf3f3dd6c..04fc4a6fd81d 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -338,4 +338,11 @@ oneway interface IStatusBar * @param leftOrTop indicates where the stage split is. */ void enterStageSplitFromRunningApp(boolean leftOrTop); + + /** + * Shows the media output switcher dialog. + * + * @param packageName of the session for which the output switcher is shown. + */ + void showMediaOutputSwitcher(String packageName); } diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index 869da1ffcb6e..058c6ec4d13c 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -106,7 +106,9 @@ public final class RotationPolicy { * Enables or disables rotation lock from the system UI toggle. */ public static void setRotationLock(Context context, final boolean enabled) { - final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION; + final int rotation = areAllRotationsAllowed(context) + || useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION + : NATURAL_ROTATION; setRotationLockAtAngle(context, enabled, rotation); } @@ -139,6 +141,11 @@ public final class RotationPolicy { return context.getResources().getBoolean(R.bool.config_allowAllRotations); } + private static boolean useCurrentRotationOnRotationLockChange(Context context) { + return context.getResources().getBoolean( + R.bool.config_useCurrentRotationOnRotationLockChange); + } + private static void setRotationLock(final boolean enabled, final int rotation) { AsyncTask.execute(new Runnable() { @Override diff --git a/core/res/OWNERS b/core/res/OWNERS index 3c143668489d..8c041b20fc81 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -28,8 +28,8 @@ yamasani@google.com # WindowManager team # TODO(262451702): Move WindowManager configs out of config.xml in a separate file -per-file core/res/res/values/config.xml = file:/services/core/java/com/android/server/wm/OWNERS -per-file core/res/res/values/symbols.xml = file:/services/core/java/com/android/server/wm/OWNERS +per-file res/values/config.xml = file:/services/core/java/com/android/server/wm/OWNERS +per-file res/values/symbols.xml = file:/services/core/java/com/android/server/wm/OWNERS # Resources finalization per-file res/xml/public-staging.xml = file:/tools/aapt2/OWNERS diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index e60a96e3a4b7..ce02af221d0f 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -78,7 +78,7 @@ <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string> <string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string> <string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string> - <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ez dago mugikorreko datu-zerbitzurik"</string> + <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ez dago mugikorretarako datu-zerbitzurik"</string> <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Ezin da egin larrialdi-deirik"</string> <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ez dago ahots-deien zerbitzurik"</string> <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Ez dago ahozko zerbitzurik eta ezin da egin larrialdi-deirik"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 3db4e9bded7d..27d932aac8fa 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -264,7 +264,7 @@ <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Учак режими"</string> <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"Учак режими КҮЙҮК"</string> <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"Учак режими ӨЧҮК"</string> - <string name="global_action_settings" msgid="4671878836947494217">"Жөндөөлөр"</string> + <string name="global_action_settings" msgid="4671878836947494217">"Параметрлер"</string> <string name="global_action_assist" msgid="2517047220311505805">"Жардам"</string> <string name="global_action_voice_assist" msgid="6655788068555086695">"Үн жардамчысы"</string> <string name="global_action_lockdown" msgid="2475471405907902963">"Бекем кулпулоо"</string> @@ -631,7 +631,7 @@ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Жүзүңүздүн үлгүсүн өчүрүү үчүн басып, жаңы үлгүнү кошуңуз"</string> <string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну тууралоо"</string> <string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string> - <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр > Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string> + <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Параметрлер > Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string> <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string> <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Манжа изин кошуу үчүн басыңыз"</string> <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Кулпуланган түзмөктү манжа изи менен ачуу"</string> @@ -1624,7 +1624,7 @@ <string name="media_route_chooser_title" msgid="6646594924991269208">"Түзмөккө туташуу"</string> <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Тышкы экранга чыгаруу"</string> <string name="media_route_chooser_searching" msgid="6119673534251329535">"Түзмөктөр изделүүдө..."</string> - <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Жөндөөлөр"</string> + <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Параметрлер"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Ажыратуу"</string> <string name="media_route_status_scanning" msgid="8045156315309594482">"Скандоодо..."</string> <string name="media_route_status_connecting" msgid="5845597961412010540">"Туташууда..."</string> @@ -1679,10 +1679,10 @@ <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."</string> <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Атайын мүмкүнчүлүктөрдүн ыкчам баскычын иштетесизби?"</string> - <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string> + <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн Параметрлер > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string> <string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string> <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> ыкчам баскычын иштетесизби?"</string> - <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string> + <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн үндү катуулатуу/акырындатуу баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Параметрлер > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string> <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ооба"</string> <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Жок"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"КҮЙҮК"</string> @@ -2069,7 +2069,7 @@ <string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Кийинчерээк эскертүү"</string> <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Жабуу"</string> <string name="notification_app_name_system" msgid="3045196791746735601">"Тутум"</string> - <string name="notification_app_name_settings" msgid="9088548800899952531">"Жөндөөлөр"</string> + <string name="notification_app_name_settings" msgid="9088548800899952531">"Параметрлер"</string> <string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string> <string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string> <string name="notification_appops_overlay_active" msgid="5571732753262836481">"экрандагы башка терезелердин үстүнөн көрсөтүлүүдө"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 42a477b4b1a8..9768208e5171 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1534,7 +1534,7 @@ <string name="add_account_button_label" msgid="322390749416414097">"Adicionar conta"</string> <string name="number_picker_increment_button" msgid="7621013714795186298">"Aumentar"</string> <string name="number_picker_decrement_button" msgid="5116948444762708204">"Diminuir"</string> - <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e mantenha pressionado."</string> + <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e pressione."</string> <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Deslize para cima para aumentar e para baixo para diminuir."</string> <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Aumentar minuto"</string> <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Diminuir minuto"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 42a477b4b1a8..9768208e5171 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1534,7 +1534,7 @@ <string name="add_account_button_label" msgid="322390749416414097">"Adicionar conta"</string> <string name="number_picker_increment_button" msgid="7621013714795186298">"Aumentar"</string> <string name="number_picker_decrement_button" msgid="5116948444762708204">"Diminuir"</string> - <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e mantenha pressionado."</string> + <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> toque e pressione."</string> <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Deslize para cima para aumentar e para baixo para diminuir."</string> <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Aumentar minuto"</string> <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Diminuir minuto"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 93d907ecfd1a..3b3fa06fd93c 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -796,7 +796,7 @@ <string-array name="phoneTypes"> <item msgid="8996339953292723951">"Nhà riêng"</item> <item msgid="7740243458912727194">"Di động"</item> - <item msgid="8526146065496663766">"Cơ quan"</item> + <item msgid="8526146065496663766">"Nơi làm việc"</item> <item msgid="8150904584178569699">"Số fax cơ quan"</item> <item msgid="4537253139152229577">"Số fax nhà riêng"</item> <item msgid="6751245029698664340">"Số máy nhắn tin"</item> @@ -805,24 +805,24 @@ </string-array> <string-array name="emailAddressTypes"> <item msgid="7786349763648997741">"Nhà riêng"</item> - <item msgid="435564470865989199">"Cơ quan"</item> + <item msgid="435564470865989199">"Nơi làm việc"</item> <item msgid="4199433197875490373">"Khác"</item> <item msgid="3233938986670468328">"Tùy chỉnh"</item> </string-array> <string-array name="postalAddressTypes"> <item msgid="3861463339764243038">"Nhà riêng"</item> - <item msgid="5472578890164979109">"Cơ quan"</item> + <item msgid="5472578890164979109">"Nơi làm việc"</item> <item msgid="5718921296646594739">"Khác"</item> <item msgid="5523122236731783179">"Tùy chỉnh"</item> </string-array> <string-array name="imAddressTypes"> <item msgid="588088543406993772">"Nhà riêng"</item> - <item msgid="5503060422020476757">"Cơ quan"</item> + <item msgid="5503060422020476757">"Nơi làm việc"</item> <item msgid="2530391194653760297">"Khác"</item> <item msgid="7640927178025203330">"Tùy chỉnh"</item> </string-array> <string-array name="organizationTypes"> - <item msgid="6144047813304847762">"Cơ quan"</item> + <item msgid="6144047813304847762">"Nơi làm việc"</item> <item msgid="7402720230065674193">"Khác"</item> <item msgid="808230403067569648">"Tùy chỉnh"</item> </string-array> @@ -839,7 +839,7 @@ <string name="phoneTypeCustom" msgid="5120365721260686814">"Tùy chỉnh"</string> <string name="phoneTypeHome" msgid="3880132427643623588">"Nhà riêng"</string> <string name="phoneTypeMobile" msgid="1178852541462086735">"Di động"</string> - <string name="phoneTypeWork" msgid="6604967163358864607">"Cơ quan"</string> + <string name="phoneTypeWork" msgid="6604967163358864607">"Nơi làm việc"</string> <string name="phoneTypeFaxWork" msgid="6757519896109439123">"Số fax cơ quan"</string> <string name="phoneTypeFaxHome" msgid="6678559953115904345">"Số fax nhà riêng"</string> <string name="phoneTypePager" msgid="576402072263522767">"Số máy nhắn tin"</string> @@ -863,16 +863,16 @@ <string name="eventTypeOther" msgid="530671238533887997">"Khác"</string> <string name="emailTypeCustom" msgid="1809435350482181786">"Tùy chỉnh"</string> <string name="emailTypeHome" msgid="1597116303154775999">"Nhà riêng"</string> - <string name="emailTypeWork" msgid="2020095414401882111">"Cơ quan"</string> + <string name="emailTypeWork" msgid="2020095414401882111">"Nơi làm việc"</string> <string name="emailTypeOther" msgid="5131130857030897465">"Khác"</string> <string name="emailTypeMobile" msgid="787155077375364230">"Di Động"</string> <string name="postalTypeCustom" msgid="5645590470242939129">"Tùy chỉnh"</string> <string name="postalTypeHome" msgid="7562272480949727912">"Nhà riêng"</string> - <string name="postalTypeWork" msgid="8553425424652012826">"Cơ quan"</string> + <string name="postalTypeWork" msgid="8553425424652012826">"Nơi làm việc"</string> <string name="postalTypeOther" msgid="7094245413678857420">"Khác"</string> <string name="imTypeCustom" msgid="5653384545085765570">"Tùy chỉnh"</string> <string name="imTypeHome" msgid="6996507981044278216">"Nhà riêng"</string> - <string name="imTypeWork" msgid="2099668940169903123">"Cơ quan"</string> + <string name="imTypeWork" msgid="2099668940169903123">"Nơi làm việc"</string> <string name="imTypeOther" msgid="8068447383276219810">"Khác"</string> <string name="imProtocolCustom" msgid="4437878287653764692">"Tùy chỉnh"</string> <string name="imProtocolAim" msgid="4050198236506604378">"AIM"</string> @@ -884,7 +884,7 @@ <string name="imProtocolIcq" msgid="2410325380427389521">"ICQ"</string> <string name="imProtocolJabber" msgid="7919269388889582015">"Jabber"</string> <string name="imProtocolNetMeeting" msgid="4985002408136148256">"NetMeeting"</string> - <string name="orgTypeWork" msgid="8684458700669564172">"Cơ quan"</string> + <string name="orgTypeWork" msgid="8684458700669564172">"Nơi làm việc"</string> <string name="orgTypeOther" msgid="5450675258408005553">"Khác"</string> <string name="orgTypeCustom" msgid="1126322047677329218">"Tùy chỉnh"</string> <string name="relationTypeCustom" msgid="282938315217441351">"Tùy chỉnh"</string> @@ -904,7 +904,7 @@ <string name="relationTypeSpouse" msgid="6916682664436031703">"Vợ/chồng"</string> <string name="sipAddressTypeCustom" msgid="6283889809842649336">"Tùy chỉnh"</string> <string name="sipAddressTypeHome" msgid="5918441930656878367">"Nhà riêng"</string> - <string name="sipAddressTypeWork" msgid="7873967986701216770">"Cơ quan"</string> + <string name="sipAddressTypeWork" msgid="7873967986701216770">"Nơi làm việc"</string> <string name="sipAddressTypeOther" msgid="6317012577345187275">"Khác"</string> <string name="quick_contacts_not_available" msgid="1262709196045052223">"Không tìm thấy ứng dụng nào để xem liên hệ này."</string> <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Nhập mã PIN"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 2f5efd12a2ba..c759239bd5bd 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -576,6 +576,10 @@ rotations as the default behavior. --> <bool name="config_allowAllRotations">false</bool> + <!-- If false and config_allowAllRotations is false, the screen will rotate to the natural + orientation of the device when the auto-rotate policy is toggled. --> + <bool name="config_useCurrentRotationOnRotationLockChange">false</bool> + <!-- If true, the direction rotation is applied to get to an application's requested orientation is reversed. Normally, the model is that landscape is clockwise from portrait; thus on a portrait device an app requesting diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 41281fa0d40f..7bf0dfc7f45d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1732,6 +1732,7 @@ <java-symbol type="attr" name="dialogTitleDecorLayout" /> <java-symbol type="attr" name="dialogTitleIconsDecorLayout" /> <java-symbol type="bool" name="config_allowAllRotations" /> + <java-symbol type="bool" name="config_useCurrentRotationOnRotationLockChange"/> <java-symbol type="bool" name="config_annoy_dianne" /> <java-symbol type="bool" name="config_startDreamImmediatelyOnDock" /> <java-symbol type="bool" name="config_carDockEnablesAccelerometer" /> diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index ed2b10117308..3768063f2a91 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -368,4 +368,20 @@ public class PropertyInvalidatedCacheTests { PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState"); assertEquals(n1, "cache_key.bluetooth.get_state"); } + + @Test + public void testOnTrimMemory() { + TestCache cache = new TestCache(MODULE, "trimMemoryTest"); + // The cache is not active until it has been invalidated once. + cache.invalidateCache(); + // Populate the cache with six entries. + for (int i = 0; i < 6; i++) { + cache.query(i); + } + // The maximum number of entries in TestCache is 4, so even though six entries were + // created, only four are retained. + assertEquals(4, cache.size()); + PropertyInvalidatedCache.onTrimMemory(); + assertEquals(0, cache.size()); + } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java index ff1256b47429..a288fd6a3067 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java @@ -77,6 +77,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; +import androidx.window.extensions.core.util.function.Function; import androidx.window.extensions.layout.WindowLayoutComponentImpl; import androidx.window.extensions.layout.WindowLayoutInfo; @@ -562,10 +563,10 @@ public class SplitPresenterTest { SplitAttributes.SplitType.RatioSplitType.splitEqually() ) ).build(); + final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator = + params -> splitAttributes; - mController.setSplitAttributesCalculator(params -> { - return splitAttributes; - }); + mController.setSplitAttributesCalculator(calculator); assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties, splitPairRule, null /* minDimensionsPair */)); diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar Binary files differindex 5de5365e4d03..cddbf469b3c7 100644 --- a/libs/WindowManager/Jetpack/window-extensions-release.aar +++ b/libs/WindowManager/Jetpack/window-extensions-release.aar diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 7d39c45d2530..800728b1e1e4 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -19,7 +19,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="pip_phone_close" msgid="5783752637260411309">"Жабуу"</string> <string name="pip_phone_expand" msgid="2579292903468287504">"Жайып көрсөтүү"</string> - <string name="pip_phone_settings" msgid="5468987116750491918">"Жөндөөлөр"</string> + <string name="pip_phone_settings" msgid="5468987116750491918">"Параметрлер"</string> <string name="pip_phone_enter_split" msgid="7042877263880641911">"Экранды бөлүү режимине өтүү"</string> <string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string> <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index a9d3c9f154cd..abb357c5b653 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -78,6 +78,7 @@ public class SplitDecorManager extends WindowlessWindowManager { private final Rect mResizingBounds = new Rect(); private final Rect mTempRect = new Rect(); private ValueAnimator mFadeAnimator; + private ValueAnimator mScreenshotAnimator; private int mIconSize; private int mOffsetX; @@ -135,8 +136,17 @@ public class SplitDecorManager extends WindowlessWindowManager { /** Releases the surfaces for split decor. */ public void release(SurfaceControl.Transaction t) { - if (mFadeAnimator != null && mFadeAnimator.isRunning()) { - mFadeAnimator.cancel(); + if (mFadeAnimator != null) { + if (mFadeAnimator.isRunning()) { + mFadeAnimator.cancel(); + } + mFadeAnimator = null; + } + if (mScreenshotAnimator != null) { + if (mScreenshotAnimator.isRunning()) { + mScreenshotAnimator.cancel(); + } + mScreenshotAnimator = null; } if (mViewHost != null) { mViewHost.release(); @@ -238,16 +248,20 @@ public class SplitDecorManager extends WindowlessWindowManager { /** Stops showing resizing hint. */ public void onResized(SurfaceControl.Transaction t, Runnable animFinishedCallback) { if (mScreenshot != null) { + if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) { + mScreenshotAnimator.cancel(); + } + t.setPosition(mScreenshot, mOffsetX, mOffsetY); final SurfaceControl.Transaction animT = new SurfaceControl.Transaction(); - final ValueAnimator va = ValueAnimator.ofFloat(1, 0); - va.addUpdateListener(valueAnimator -> { + mScreenshotAnimator = ValueAnimator.ofFloat(1, 0); + mScreenshotAnimator.addUpdateListener(valueAnimator -> { final float progress = (float) valueAnimator.getAnimatedValue(); animT.setAlpha(mScreenshot, progress); animT.apply(); }); - va.addListener(new AnimatorListenerAdapter() { + mScreenshotAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mRunningAnimationCount++; @@ -266,7 +280,7 @@ public class SplitDecorManager extends WindowlessWindowManager { } } }); - va.start(); + mScreenshotAnimator.start(); } if (mResizingIconView == null) { @@ -292,9 +306,6 @@ public class SplitDecorManager extends WindowlessWindowManager { }); return; } - - // If fade-in animation is running, cancel it and re-run fade-out one. - mFadeAnimator.cancel(); } if (mShown) { fadeOutDecor(animFinishedCallback); @@ -332,6 +343,11 @@ public class SplitDecorManager extends WindowlessWindowManager { * directly. */ public void fadeOutDecor(Runnable finishedCallback) { if (mShown) { + // If previous animation is running, just cancel it. + if (mFadeAnimator != null && mFadeAnimator.isRunning()) { + mFadeAnimator.cancel(); + } + startFadeAnimation(false /* show */, true, finishedCallback); mShown = false; } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 63b03ab76aac..a839a230ea6f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -36,6 +36,7 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.ArraySet; @@ -261,6 +262,11 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll } } + /** Get number of tasks that are marked as visible */ + int getVisibleTaskCount() { + return mDesktopModeTaskRepository.getVisibleTaskCount(); + } + @NonNull private WindowContainerTransaction bringDesktopAppsToFront(boolean force) { final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -438,5 +444,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll executeRemoteCallWithTaskPermission(mController, "showDesktopApps", DesktopModeController::showDesktopApps); } + + @Override + public int getVisibleTaskCount() throws RemoteException { + int[] result = new int[1]; + executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount", + controller -> result[0] = controller.getVisibleTaskCount(), + true /* blocking */ + ); + return result[0]; + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 600ccc17ecaa..47342c9f21ee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -143,6 +143,13 @@ class DesktopModeTaskRepository { } /** + * Get number of tasks that are marked as visible + */ + fun getVisibleTaskCount(): Int { + return visibleTasks.size + } + + /** * Add (or move if it already exists) the task to the top of the ordered list. */ fun addOrMoveFreeformTaskToTop(taskId: Int) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 9165f7012bb5..23033253eaf9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -96,6 +96,11 @@ class DesktopTasksController( } } + /** Get number of tasks that are marked as visible */ + fun getVisibleTaskCount(): Int { + return desktopModeTaskRepository.getVisibleTaskCount() + } + /** Move a task with given `taskId` to desktop */ fun moveToDesktop(taskId: Int) { shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task) } @@ -309,5 +314,16 @@ class DesktopTasksController( Consumer(DesktopTasksController::showDesktopApps) ) } + + override fun getVisibleTaskCount(): Int { + val result = IntArray(1) + ExecutorUtils.executeRemoteCallWithTaskPermission( + controller, + "getVisibleTaskCount", + { controller -> result[0] = controller.getVisibleTaskCount() }, + true /* blocking */ + ) + return result[0] + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index 5042bd6f2d65..d0739e14675f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -23,4 +23,7 @@ interface IDesktopMode { /** Show apps on the desktop */ void showDesktopApps(); + + /** Get count of visible desktop tasks */ + int getVisibleTaskCount(); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index 35cc16852b9f..7997a7ed7f7f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -334,6 +334,41 @@ public class DesktopModeControllerTest extends ShellTestCase { } @Test + public void testGetVisibleTaskCount_noTasks_returnsZero() { + assertThat(mController.getVisibleTaskCount()).isEqualTo(0); + } + + @Test + public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() { + RunningTaskInfo task1 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */); + + RunningTaskInfo task2 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + + assertThat(mController.getVisibleTaskCount()).isEqualTo(2); + } + + @Test + public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() { + RunningTaskInfo task1 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */); + + RunningTaskInfo task2 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, false /* visible */); + + assertThat(mController.getVisibleTaskCount()).isEqualTo(1); + } + + @Test public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() { when(DesktopModeStatus.isActive(any())).thenReturn(false); WindowContainerTransaction wct = mController.handleRequest( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt index 1e43a5983821..45cb3a062cc5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt @@ -141,6 +141,36 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { } @Test + fun getVisibleTaskCount() { + // No tasks, count is 0 + assertThat(repo.getVisibleTaskCount()).isEqualTo(0) + + // New task increments count to 1 + repo.updateVisibleFreeformTasks(taskId = 1, visible = true) + assertThat(repo.getVisibleTaskCount()).isEqualTo(1) + + // Visibility update to same task does not increase count + repo.updateVisibleFreeformTasks(taskId = 1, visible = true) + assertThat(repo.getVisibleTaskCount()).isEqualTo(1) + + // Second task visible increments count + repo.updateVisibleFreeformTasks(taskId = 2, visible = true) + assertThat(repo.getVisibleTaskCount()).isEqualTo(2) + + // Hiding a task decrements count + repo.updateVisibleFreeformTasks(taskId = 1, visible = false) + assertThat(repo.getVisibleTaskCount()).isEqualTo(1) + + // Hiding all tasks leaves count at 0 + repo.updateVisibleFreeformTasks(taskId = 2, visible = false) + assertThat(repo.getVisibleTaskCount()).isEqualTo(0) + + // Hiding a not existing task, count remains at 0 + repo.updateVisibleFreeformTasks(taskId = 999, visible = false) + assertThat(repo.getVisibleTaskCount()).isEqualTo(0) + } + + @Test fun addOrMoveFreeformTaskToTop_didNotExist_addsToTop() { repo.addOrMoveFreeformTaskToTop(5) repo.addOrMoveFreeformTaskToTop(6) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 4011d080564d..f16beeed57a3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -197,6 +197,27 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun getVisibleTaskCount_noTasks_returnsZero() { + assertThat(controller.getVisibleTaskCount()).isEqualTo(0) + } + + @Test + fun getVisibleTaskCount_twoTasks_bothVisible_returnsTwo() { + setUpHomeTask() + setUpFreeformTask().also(::markTaskVisible) + setUpFreeformTask().also(::markTaskVisible) + assertThat(controller.getVisibleTaskCount()).isEqualTo(2) + } + + @Test + fun getVisibleTaskCount_twoTasks_oneVisible_returnsOne() { + setUpHomeTask() + setUpFreeformTask().also(::markTaskVisible) + setUpFreeformTask().also(::markTaskHidden) + assertThat(controller.getVisibleTaskCount()).isEqualTo(1) + } + + @Test fun moveToDesktop() { val task = setUpFullscreenTask() controller.moveToDesktop(task) @@ -224,7 +245,7 @@ class DesktopTasksControllerTest : ShellTestCase() { assertThat(hierarchyOps).hasSize(3) assertReorderSequence(homeTask, freeformTask, fullscreenTask) assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode) - .isEqualTo(WINDOWING_MODE_FREEFORM) + .isEqualTo(WINDOWING_MODE_FREEFORM) } } diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml index 2684fd969880..0f19fe81b379 100644 --- a/packages/PackageInstaller/res/values-ky/strings.xml +++ b/packages/PackageInstaller/res/values-ky/strings.xml @@ -88,7 +88,7 @@ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Планшетиңиз жана жеке дайын-даректериңиз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам планшетиңизге кандайдыр бир зыян келтирилсе же дайын-даректериңизды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string> <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Сыналгыңыз жана жеке дайын-даректериңиз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам сыналгыңызга кандайдыр бир зыян келтирилсе же дайын-даректериңизды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string> <string name="anonymous_source_continue" msgid="4375745439457209366">"Улантуу"</string> - <string name="external_sources_settings" msgid="4046964413071713807">"Жөндөөлөр"</string> + <string name="external_sources_settings" msgid="4046964413071713807">"Параметрлер"</string> <string name="wear_app_channel" msgid="1960809674709107850">"Тагынма колдонмолорду орнотуу/чыгаруу"</string> <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Колдонмолорду орноткучтун билдирмелери"</string> <string name="notification_installation_success_message" msgid="6450467996056038442">"Ийгиликтүү орнотулду"</string> diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml index f4a893a1cd83..bcd6784bc8f8 100644 --- a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml +++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="settings_label" msgid="5948970810295631236">"Жөндөөлөр"</string> + <string name="settings_label" msgid="5948970810295631236">"Параметрлер"</string> </resources> diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt index 259f0ed5c7a1..4e96ddabfda6 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt @@ -41,6 +41,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCompositionContext @@ -73,6 +74,7 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewTreeLifecycleOwner import androidx.lifecycle.ViewTreeViewModelStoreOwner +import com.android.compose.runtime.movableContentOf import com.android.systemui.animation.Expandable import com.android.systemui.animation.LaunchAnimator import kotlin.math.max @@ -170,25 +172,25 @@ fun Expandable( val contentColor = controller.contentColor val shape = controller.shape - // TODO(b/230830644): Use movableContentOf to preserve the content state instead once the - // Compose libraries have been updated and include aosp/2163631. val wrappedContent = - @Composable { controller: ExpandableController -> - CompositionLocalProvider( - LocalContentColor provides contentColor, - ) { - // We make sure that the content itself (wrapped by the background) is at least - // 40.dp, which is the same as the M3 buttons. This applies even if onClick is - // null, to make it easier to write expandables that are sometimes clickable and - // sometimes not. There shouldn't be any Expandable smaller than 40dp because if - // the expandable is not clickable directly, then something in its content should - // be (and with a size >= 40dp). - val minSize = 40.dp - Box( - Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize), - contentAlignment = Alignment.Center, + remember(content) { + movableContentOf { expandable: Expandable -> + CompositionLocalProvider( + LocalContentColor provides contentColor, ) { - content(controller.expandable) + // We make sure that the content itself (wrapped by the background) is at least + // 40.dp, which is the same as the M3 buttons. This applies even if onClick is + // null, to make it easier to write expandables that are sometimes clickable and + // sometimes not. There shouldn't be any Expandable smaller than 40dp because if + // the expandable is not clickable directly, then something in its content + // should be (and with a size >= 40dp). + val minSize = 40.dp + Box( + Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize), + contentAlignment = Alignment.Center, + ) { + content(expandable) + } } } } @@ -270,7 +272,7 @@ fun Expandable( .onGloballyPositioned { controller.boundsInComposeViewRoot.value = it.boundsInRoot() } - ) { wrappedContent(controller) } + ) { wrappedContent(controller.expandable) } } else -> { val clickModifier = @@ -301,7 +303,7 @@ fun Expandable( controller.boundsInComposeViewRoot.value = it.boundsInRoot() }, ) { - wrappedContent(controller) + wrappedContent(controller.expandable) } } } @@ -315,7 +317,7 @@ private fun AnimatedContentInOverlay( animatorState: State<LaunchAnimator.State?>, overlay: ViewGroupOverlay, controller: ExpandableControllerImpl, - content: @Composable (ExpandableController) -> Unit, + content: @Composable (Expandable) -> Unit, composeViewRoot: View, onOverlayComposeViewChanged: (View?) -> Unit, density: Density, @@ -370,7 +372,7 @@ private fun AnimatedContentInOverlay( // We center the content in the expanding container. contentAlignment = Alignment.Center, ) { - Box(contentModifier) { content(controller) } + Box(contentModifier) { content(controller.expandable) } } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt index 7f2933e44b32..c9e57b45612c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt @@ -15,21 +15,51 @@ package com.android.systemui.unfold.system import android.app.ActivityManager +import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration +import android.os.Trace +import com.android.systemui.shared.system.TaskStackChangeListener +import com.android.systemui.shared.system.TaskStackChangeListeners import com.android.systemui.unfold.util.CurrentActivityTypeProvider import javax.inject.Inject import javax.inject.Singleton @Singleton -class ActivityManagerActivityTypeProvider @Inject constructor( - private val activityManager: ActivityManager -) : CurrentActivityTypeProvider { +class ActivityManagerActivityTypeProvider +@Inject +constructor(private val activityManager: ActivityManager) : CurrentActivityTypeProvider { override val isHomeActivity: Boolean? - get() { - val activityType = activityManager.getRunningTasks(/* maxNum= */ 1) - ?.getOrNull(0)?.topActivityType ?: return null + get() = _isHomeActivity - return activityType == WindowConfiguration.ACTIVITY_TYPE_HOME + private var _isHomeActivity: Boolean? = null + + + override fun init() { + _isHomeActivity = activityManager.isOnHomeActivity() + TaskStackChangeListeners.getInstance().registerTaskStackListener(taskStackChangeListener) + } + + override fun uninit() { + TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener) + } + + private val taskStackChangeListener = + object : TaskStackChangeListener { + override fun onTaskMovedToFront(taskInfo: RunningTaskInfo) { + _isHomeActivity = taskInfo.isHomeActivity() + } + } + + private fun RunningTaskInfo.isHomeActivity(): Boolean = + topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME + + private fun ActivityManager.isOnHomeActivity(): Boolean? { + try { + Trace.beginSection("isOnHomeActivity") + return getRunningTasks(/* maxNum= */ 1)?.firstOrNull()?.isHomeActivity() + } finally { + Trace.endSection() } + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt index 24ae42ae4db2..fe607e16661c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt @@ -19,7 +19,7 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig import com.android.systemui.unfold.config.UnfoldTransitionConfig -import com.android.systemui.unfold.dagger.UnfoldBackground +import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.util.CurrentActivityTypeProvider @@ -56,6 +56,6 @@ abstract class SystemUnfoldSharedModule { abstract fun mainHandler(@Main handler: Handler): Handler @Binds - @UnfoldBackground + @UnfoldSingleThreadBg abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index d4ca8e34fb32..ea84438bf4ba 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -29,6 +29,9 @@ import android.view.View; import android.view.View.OnKeyListener; import android.view.ViewTreeObserver; import android.widget.FrameLayout; +import android.window.OnBackAnimationCallback; + +import androidx.annotation.NonNull; import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; @@ -394,6 +397,14 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> } /** + * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture. + */ + @NonNull + public OnBackAnimationCallback getBackCallback() { + return mKeyguardSecurityContainerController.getBackCallback(); + } + + /** * Allows the media keys to work when the keyguard is showing. * The media keys should be of no interest to the actual keyguard view(s), * so intercepting them here should not be of any harm. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 5d7a6f122e69..e4f85db3971e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -32,6 +32,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.START; import static androidx.constraintlayout.widget.ConstraintSet.TOP; import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT; +import static com.android.systemui.animation.InterpolatorsAndroidX.DECELERATE_QUINT; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; import static java.lang.Integer.max; @@ -73,6 +74,8 @@ import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import androidx.annotation.IntDef; import androidx.annotation.NonNull; @@ -135,7 +138,9 @@ public class KeyguardSecurityContainer extends ConstraintLayout { private static final float MIN_DRAG_SIZE = 10; // How much to scale the default slop by, to avoid accidental drags. private static final float SLOP_SCALE = 4f; - + @VisibleForTesting + // How much the view scales down to during back gestures. + static final float MIN_BACK_SCALE = 0.9f; @VisibleForTesting KeyguardSecurityViewFlipper mSecurityViewFlipper; private GlobalSettings mGlobalSettings; @@ -240,6 +245,33 @@ public class KeyguardSecurityContainer extends ConstraintLayout { } }; + private final OnBackAnimationCallback mBackCallback = new OnBackAnimationCallback() { + @Override + public void onBackCancelled() { + // TODO(b/259608500): Remove once back API auto animates progress to 0 on cancel. + resetScale(); + } + + @Override + public void onBackInvoked() { } + + @Override + public void onBackProgressed(BackEvent event) { + float progress = event.getProgress(); + // TODO(b/263819310): Update the interpolator to match spec. + float scale = MIN_BACK_SCALE + + (1 - MIN_BACK_SCALE) * (1 - DECELERATE_QUINT.getInterpolation(progress)); + setScale(scale); + } + }; + /** + * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture. + */ + @NonNull + OnBackAnimationCallback getBackCallback() { + return mBackCallback; + } + // Used to notify the container when something interesting happens. public interface SecurityCallback { /** @@ -736,6 +768,15 @@ public class KeyguardSecurityContainer extends ConstraintLayout { mViewMode.onDensityOrFontScaleChanged(); } + void resetScale() { + setScale(1); + } + + private void setScale(float scale) { + setScaleX(scale); + setScaleY(scale); + } + /** * Enscapsulates the differences between bouncer modes for the container. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index a72a484fb6f1..57bfe5421049 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -40,7 +40,9 @@ import android.util.Log; import android.util.Slog; import android.view.MotionEvent; import android.view.View; +import android.window.OnBackAnimationCallback; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; @@ -479,6 +481,9 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard /** Called when the bouncer changes visibility. */ public void onBouncerVisibilityChanged(@View.Visibility int visibility) { setBouncerVisible(visibility == View.VISIBLE); + if (visibility == View.INVISIBLE) { + mView.resetScale(); + } } private void setBouncerVisible(boolean visible) { @@ -588,6 +593,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } /** + * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture. + */ + @NonNull + OnBackAnimationCallback getBackCallback() { + return mView.getBackCallback(); + } + + /** * Switches to the given security view unless it's already being shown, in which case * this is a no-op. * diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 8012dea643fb..8e9992fdd296 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -31,6 +31,7 @@ import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.keyboard.KeyboardUI import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.log.SessionTracker +import com.android.systemui.media.dialog.MediaOutputSwitcherDialogUI import com.android.systemui.media.RingtonePlayer import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver @@ -217,6 +218,12 @@ abstract class SystemUICoreStartableModule { @ClassKey(ToastUI::class) abstract fun bindToastUI(service: ToastUI): CoreStartable + /** Inject into MediaOutputSwitcherDialogUI. */ + @Binds + @IntoMap + @ClassKey(MediaOutputSwitcherDialogUI::class) + abstract fun MediaOutputSwitcherDialogUI(sysui: MediaOutputSwitcherDialogUI): CoreStartable + /** Inject into VolumeUI. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 37183e89df29..2ef1262fe55e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -116,6 +116,7 @@ public class DozeSensors { private boolean mListening; private boolean mListeningTouchScreenSensors; private boolean mListeningProxSensors; + private boolean mListeningAodOnlySensors; private boolean mUdfpsEnrolled; @DevicePostureController.DevicePostureInt @@ -184,7 +185,8 @@ public class DozeSensors { dozeParameters.getPulseOnSigMotion(), DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */, - false /* touchscreen */), + false /* touchscreen */ + ), new TriggerSensor( mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE), Settings.Secure.DOZE_PICK_UP_GESTURE, @@ -195,14 +197,17 @@ public class DozeSensors { false /* touchscreen */, false /* ignoresSetting */, false /* requires prox */, - true /* immediatelyReRegister */), + true /* immediatelyReRegister */, + false /* requiresAod */ + ), new TriggerSensor( findSensor(config.doubleTapSensorType()), Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, true /* configured */, DozeLog.REASON_SENSOR_DOUBLE_TAP, dozeParameters.doubleTapReportsTouchCoordinates(), - true /* touchscreen */), + true /* touchscreen */ + ), new TriggerSensor( findSensors(config.tapSensorTypeMapping()), Settings.Secure.DOZE_TAP_SCREEN_GESTURE, @@ -214,7 +219,9 @@ public class DozeSensors { false /* ignoresSetting */, dozeParameters.singleTapUsesProx(mDevicePosture) /* requiresProx */, true /* immediatelyReRegister */, - mDevicePosture), + mDevicePosture, + false + ), new TriggerSensor( findSensor(config.longPressSensorType()), Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, @@ -225,7 +232,9 @@ public class DozeSensors { true /* touchscreen */, false /* ignoresSetting */, dozeParameters.longPressUsesProx() /* requiresProx */, - true /* immediatelyReRegister */), + true /* immediatelyReRegister */, + false /* requiresAod */ + ), new TriggerSensor( findSensor(config.udfpsLongPressSensorType()), "doze_pulse_on_auth", @@ -236,7 +245,9 @@ public class DozeSensors { true /* touchscreen */, false /* ignoresSetting */, dozeParameters.longPressUsesProx(), - false /* immediatelyReRegister */), + false /* immediatelyReRegister */, + true /* requiresAod */ + ), new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY), Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, @@ -244,7 +255,8 @@ public class DozeSensors { && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT), DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE, false /* reports touch coordinates */, - false /* touchscreen */), + false /* touchscreen */ + ), new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN), Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, @@ -252,7 +264,8 @@ public class DozeSensors { DozeLog.PULSE_REASON_SENSOR_WAKE_REACH, false /* reports touch coordinates */, false /* touchscreen */, - mConfig.getWakeLockScreenDebounce()), + mConfig.getWakeLockScreenDebounce() + ), new TriggerSensor( findSensor(config.quickPickupSensorType()), Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, @@ -263,7 +276,9 @@ public class DozeSensors { false /* requiresTouchscreen */, false /* ignoresSetting */, false /* requiresProx */, - true /* immediatelyReRegister */), + true /* immediatelyReRegister */, + false /* requiresAod */ + ), }; setProxListening(false); // Don't immediately start listening when we register. mProximitySensor.register( @@ -357,29 +372,36 @@ public class DozeSensors { /** * If sensors should be registered and sending signals. */ - public void setListening(boolean listen, boolean includeTouchScreenSensors) { - if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors) { + public void setListening(boolean listen, boolean includeTouchScreenSensors, + boolean includeAodOnlySensors) { + if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors + && mListeningAodOnlySensors == includeAodOnlySensors) { return; } mListening = listen; mListeningTouchScreenSensors = includeTouchScreenSensors; + mListeningAodOnlySensors = includeAodOnlySensors; updateListening(); } /** * If sensors should be registered and sending signals. */ - public void setListening(boolean listen, boolean includeTouchScreenSensors, - boolean lowPowerStateOrOff) { + public void setListeningWithPowerState(boolean listen, boolean includeTouchScreenSensors, + boolean includeAodRequiringSensors, boolean lowPowerStateOrOff) { final boolean shouldRegisterProxSensors = !mSelectivelyRegisterProxSensors || lowPowerStateOrOff; - if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors - && mListeningProxSensors == shouldRegisterProxSensors) { + if (mListening == listen + && mListeningTouchScreenSensors == includeTouchScreenSensors + && mListeningProxSensors == shouldRegisterProxSensors + && mListeningAodOnlySensors == includeAodRequiringSensors + ) { return; } mListening = listen; mListeningTouchScreenSensors = includeTouchScreenSensors; mListeningProxSensors = shouldRegisterProxSensors; + mListeningAodOnlySensors = includeAodRequiringSensors; updateListening(); } @@ -391,7 +413,8 @@ public class DozeSensors { for (TriggerSensor s : mTriggerSensors) { boolean listen = mListening && (!s.mRequiresTouchscreen || mListeningTouchScreenSensors) - && (!s.mRequiresProx || mListeningProxSensors); + && (!s.mRequiresProx || mListeningProxSensors) + && (!s.mRequiresAod || mListeningAodOnlySensors); s.setListening(listen); if (listen) { anyListening = true; @@ -499,6 +522,9 @@ public class DozeSensors { private final boolean mRequiresTouchscreen; private final boolean mRequiresProx; + // Whether the sensor should only register if the device is in AOD + private final boolean mRequiresAod; + // Whether to immediately re-register this sensor after the sensor is triggered. // If false, the sensor registration will be updated on the next AOD state transition. private final boolean mImmediatelyReRegister; @@ -527,7 +553,8 @@ public class DozeSensors { requiresTouchscreen, false /* ignoresSetting */, false /* requiresProx */, - true /* immediatelyReRegister */ + true /* immediatelyReRegister */, + false ); } @@ -541,7 +568,8 @@ public class DozeSensors { boolean requiresTouchscreen, boolean ignoresSetting, boolean requiresProx, - boolean immediatelyReRegister + boolean immediatelyReRegister, + boolean requiresAod ) { this( new Sensor[]{ sensor }, @@ -554,7 +582,8 @@ public class DozeSensors { ignoresSetting, requiresProx, immediatelyReRegister, - DevicePostureController.DEVICE_POSTURE_UNKNOWN + DevicePostureController.DEVICE_POSTURE_UNKNOWN, + requiresAod ); } @@ -569,7 +598,8 @@ public class DozeSensors { boolean ignoresSetting, boolean requiresProx, boolean immediatelyReRegister, - @DevicePostureController.DevicePostureInt int posture + @DevicePostureController.DevicePostureInt int posture, + boolean requiresAod ) { mSensors = sensors; mSetting = setting; @@ -580,6 +610,7 @@ public class DozeSensors { mRequiresTouchscreen = requiresTouchscreen; mIgnoresSetting = ignoresSetting; mRequiresProx = requiresProx; + mRequiresAod = requiresAod; mPosture = posture; mImmediatelyReRegister = immediatelyReRegister; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index b95c3f3c0ee7..b70960832d32 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -111,6 +111,7 @@ public class DozeTriggers implements DozeMachine.Part { private boolean mWantProxSensor; private boolean mWantTouchScreenSensors; private boolean mWantSensors; + private boolean mInAod; private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @@ -460,12 +461,19 @@ public class DozeTriggers implements DozeMachine.Part { mDozeSensors.requestTemporaryDisable(); break; case DOZE: + mAodInterruptRunnable = null; + mWantProxSensor = false; + mWantSensors = true; + mWantTouchScreenSensors = true; + mInAod = false; + break; case DOZE_AOD: mAodInterruptRunnable = null; - mWantProxSensor = newState != DozeMachine.State.DOZE; + mWantProxSensor = true; mWantSensors = true; mWantTouchScreenSensors = true; - if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) { + mInAod = true; + if (!sWakeDisplaySensorState) { onWakeScreen(false, newState, DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE); } break; @@ -491,7 +499,7 @@ public class DozeTriggers implements DozeMachine.Part { break; default: } - mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors); + mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, mInAod); } private void registerCallbacks() { @@ -510,11 +518,12 @@ public class DozeTriggers implements DozeMachine.Part { private void stopListeningToAllTriggers() { unregisterCallbacks(); - mDozeSensors.setListening(false, false); + mDozeSensors.setListening(false, false, false); mDozeSensors.setProxListening(false); mWantSensors = false; mWantProxSensor = false; mWantTouchScreenSensors = false; + mInAod = false; } @Override @@ -523,7 +532,8 @@ public class DozeTriggers implements DozeMachine.Part { final boolean lowPowerStateOrOff = state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND || state == Display.STATE_OFF; mDozeSensors.setProxListening(mWantProxSensor && lowPowerStateOrOff); - mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, lowPowerStateOrOff); + mDozeSensors.setListeningWithPowerState(mWantSensors, mWantTouchScreenSensors, + mInAod, lowPowerStateOrOff); if (mAodInterruptRunnable != null && state == Display.STATE_ON) { mAodInterruptRunnable.run(); diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 82934d10a927..42e7e93f4825 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -77,12 +77,6 @@ object Flags { // TODO(b/254512731): Tracking Bug @JvmField val NOTIFICATION_DISMISSAL_FADE = releasedFlag(113, "notification_dismissal_fade") - // TODO(b/259558771): Tracking Bug - val STABILITY_INDEX_FIX = releasedFlag(114, "stability_index_fix") - - // TODO(b/259559750): Tracking Bug - val SEMI_STABLE_SORT = releasedFlag(115, "semi_stable_sort") - @JvmField val USE_ROUNDNESS_SOURCETYPES = releasedFlag(116, "use_roundness_sourcetype") // TODO(b/259217907) @@ -413,6 +407,17 @@ object Flags { val WM_DESKTOP_WINDOWING_2 = sysPropBooleanFlag(1112, "persist.wm.debug.desktop_mode_2", default = false) + // TODO(b/254513207): Tracking Bug to delete + @Keep + @JvmField + val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES = + unreleasedFlag( + 1113, + name = "screen_record_enterprise_policies", + namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER, + teamfood = false + ) + // 1200 - predictive back @Keep @JvmField @@ -476,8 +481,7 @@ object Flags { // 1800 - shade container @JvmField - val LEAVE_SHADE_OPEN_FOR_BUGREPORT = - unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true) + val LEAVE_SHADE_OPEN_FOR_BUGREPORT = releasedFlag(1800, "leave_shade_open_for_bugreport") // 1900 @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag") @@ -521,6 +525,11 @@ object Flags { @JvmField val OUTPUT_SWITCHER_DEVICE_STATUS = unreleasedFlag(2502, "output_switcher_device_status") + // TODO(b/20911786): Tracking Bug + @JvmField + val OUTPUT_SWITCHER_SHOW_API_ENABLED = + unreleasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true) + // TODO(b259590361): Tracking bug val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release") } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt index 80c6130955c5..faeb48526ae4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data import android.view.KeyEvent +import android.window.OnBackAnimationCallback import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.ActivityStarter import java.lang.ref.WeakReference @@ -51,4 +52,6 @@ interface BouncerViewDelegate { cancelAction: Runnable?, ) fun willDismissWithActions(): Boolean + /** @return the {@link OnBackAnimationCallback} to animate Bouncer during a back gesture. */ + fun getBackCallback(): OnBackAnimationCallback } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index 49d3748dcf3d..5e46c5d1f67a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.binder import android.view.KeyEvent import android.view.View import android.view.ViewGroup +import android.window.OnBackAnimationCallback import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.internal.policy.SystemBarUtils @@ -55,6 +56,10 @@ object KeyguardBouncerViewBinder { mode == KeyguardSecurityModel.SecurityMode.SimPuk } + override fun getBackCallback(): OnBackAnimationCallback { + return hostViewController.backCallback + } + override fun shouldDismissOnMenuPressed(): Boolean { return hostViewController.shouldEnableMenuKey() } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt index ceb48458a1d3..a692ad74615f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt @@ -18,6 +18,7 @@ package com.android.systemui.media import android.app.ActivityOptions import android.content.Intent import android.content.res.Configuration +import android.content.res.Resources import android.media.projection.IMediaProjection import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION import android.os.Binder @@ -27,6 +28,7 @@ import android.os.ResultReceiver import android.os.UserHandle import android.view.ViewGroup import com.android.internal.annotations.VisibleForTesting +import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider import com.android.internal.app.ChooserActivity import com.android.internal.app.ResolverListController import com.android.internal.app.chooser.NotSelectableTargetInfo @@ -59,16 +61,12 @@ class MediaProjectionAppSelectorActivity( private lateinit var configurationController: ConfigurationController private lateinit var controller: MediaProjectionAppSelectorController private lateinit var recentsViewController: MediaProjectionRecentsViewController + private lateinit var component: MediaProjectionAppSelectorComponent override fun getLayoutResource() = R.layout.media_projection_app_selector public override fun onCreate(bundle: Bundle?) { - val component = - componentFactory.create( - activity = this, - view = this, - resultHandler = this - ) + component = componentFactory.create(activity = this, view = this, resultHandler = this) // Create a separate configuration controller for this activity as the configuration // might be different from the global one @@ -76,11 +74,12 @@ class MediaProjectionAppSelectorActivity( controller = component.controller recentsViewController = component.recentsViewController - val queryIntent = Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) } - intent.putExtra(Intent.EXTRA_INTENT, queryIntent) + intent.configureChooserIntent( + resources, + component.hostUserHandle, + component.personalProfileUserHandle + ) - val title = getString(R.string.media_projection_permission_app_selector_title) - intent.putExtra(Intent.EXTRA_TITLE, title) super.onCreate(bundle) controller.init() } @@ -183,6 +182,13 @@ class MediaProjectionAppSelectorActivity( override fun shouldShowContentPreview() = true + override fun shouldShowContentPreviewWhenEmpty(): Boolean = true + + override fun createMyUserIdProvider(): MyUserIdProvider = + object : MyUserIdProvider() { + override fun getMyUserId(): Int = component.hostUserHandle.identifier + } + override fun createContentPreviewView(parent: ViewGroup): ViewGroup = recentsViewController.createView(parent) @@ -193,6 +199,34 @@ class MediaProjectionAppSelectorActivity( * instance through activity result. */ const val EXTRA_CAPTURE_REGION_RESULT_RECEIVER = "capture_region_result_receiver" + + /** UID of the app that originally launched the media projection flow (host app user) */ + const val EXTRA_HOST_APP_USER_HANDLE = "launched_from_user_handle" const val KEY_CAPTURE_TARGET = "capture_region" + + /** Set up intent for the [ChooserActivity] */ + private fun Intent.configureChooserIntent( + resources: Resources, + hostUserHandle: UserHandle, + personalProfileUserHandle: UserHandle + ) { + // Specify the query intent to show icons for all apps on the chooser screen + val queryIntent = + Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) } + putExtra(Intent.EXTRA_INTENT, queryIntent) + + // Update the title of the chooser + val title = resources.getString(R.string.media_projection_permission_app_selector_title) + putExtra(Intent.EXTRA_TITLE, title) + + // Select host app's profile tab by default + val selectedProfile = + if (hostUserHandle == personalProfileUserHandle) { + PROFILE_PERSONAL + } else { + PROFILE_WORK + } + putExtra(EXTRA_SELECTED_PROFILE, selectedProfile) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java index bfa67a89baca..d830fc4a5fb5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java @@ -22,6 +22,7 @@ import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREE import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; @@ -35,6 +36,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.text.BidiFormatter; import android.text.SpannableString; import android.text.TextPaint; @@ -208,8 +210,14 @@ public class MediaProjectionPermissionActivity extends Activity final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class); intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder()); + intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE, + UserHandle.getUserHandleForUid(getLaunchedFromUid())); intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); - startActivity(intent); + + // Start activity from the current foreground user to avoid creating a separate + // SystemUI process without access to recent tasks because it won't have + // WM Shell running inside. + startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser())); } } catch (RemoteException e) { Log.e(TAG, "Error granting projection permission", e); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java new file mode 100644 index 000000000000..e35575bfc184 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.dialog; + +import android.annotation.MainThread; +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; + +import com.android.systemui.CoreStartable; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.statusbar.CommandQueue; + +import javax.inject.Inject; + +/** Controls display of media output switcher. */ +@SysUISingleton +public class MediaOutputSwitcherDialogUI implements CoreStartable, CommandQueue.Callbacks { + + private static final String TAG = "MediaOutputSwitcherDialogUI"; + + private final CommandQueue mCommandQueue; + private final MediaOutputDialogFactory mMediaOutputDialogFactory; + private final FeatureFlags mFeatureFlags; + + @Inject + public MediaOutputSwitcherDialogUI( + Context context, + CommandQueue commandQueue, + MediaOutputDialogFactory mediaOutputDialogFactory, + FeatureFlags featureFlags) { + mCommandQueue = commandQueue; + mMediaOutputDialogFactory = mediaOutputDialogFactory; + mFeatureFlags = featureFlags; + } + + @Override + public void start() { + if (mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_SHOW_API_ENABLED)) { + mCommandQueue.addCallback(this); + } else { + Log.w(TAG, "Show media output switcher is not enabled."); + } + } + + @Override + @MainThread + public void showMediaOutputSwitcher(String packageName) { + if (!TextUtils.isEmpty(packageName)) { + mMediaOutputDialogFactory.create(packageName, false, null); + } else { + Log.e(TAG, "Unable to launch media output dialog. Package name is empty."); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt index 6c41caab38a8..1d863435fa6e 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt @@ -19,9 +19,11 @@ package com.android.systemui.mediaprojection.appselector import android.app.Activity import android.content.ComponentName import android.content.Context +import android.os.UserHandle import com.android.launcher3.icons.IconFactory import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.MediaProjectionAppSelectorActivity +import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader import com.android.systemui.mediaprojection.appselector.data.AppIconLoader import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader @@ -30,6 +32,8 @@ import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnail import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider +import com.android.systemui.settings.UserTracker +import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.policy.ConfigurationController import dagger.Binds @@ -39,6 +43,7 @@ import dagger.Provides import dagger.Subcomponent import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap +import java.lang.IllegalArgumentException import javax.inject.Qualifier import javax.inject.Scope import kotlinx.coroutines.CoroutineScope @@ -46,6 +51,12 @@ import kotlinx.coroutines.SupervisorJob @Qualifier @Retention(AnnotationRetention.BINARY) annotation class MediaProjectionAppSelector +@Qualifier @Retention(AnnotationRetention.BINARY) annotation class HostUserHandle + +@Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile + +@Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile + @Retention(AnnotationRetention.RUNTIME) @Scope annotation class MediaProjectionAppSelectorScope @Module(subcomponents = [MediaProjectionAppSelectorComponent::class]) @@ -83,7 +94,7 @@ interface MediaProjectionAppSelectorModule { @MediaProjectionAppSelector @MediaProjectionAppSelectorScope fun provideAppSelectorComponentName(context: Context): ComponentName = - ComponentName(context, MediaProjectionAppSelectorActivity::class.java) + ComponentName(context, MediaProjectionAppSelectorActivity::class.java) @Provides @MediaProjectionAppSelector @@ -93,9 +104,32 @@ interface MediaProjectionAppSelectorModule { ): ConfigurationController = ConfigurationControllerImpl(activity) @Provides - fun bindIconFactory( - context: Context - ): IconFactory = IconFactory.obtain(context) + @PersonalProfile + @MediaProjectionAppSelectorScope + fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle { + // Current foreground user is the 'personal' profile + return UserHandle.of(activityManagerWrapper.currentUserId) + } + + @Provides + @WorkProfile + @MediaProjectionAppSelectorScope + fun workProfileUserHandle(userTracker: UserTracker): UserHandle? = + userTracker.userProfiles.find { it.isManagedProfile }?.userHandle + + @Provides + @HostUserHandle + @MediaProjectionAppSelectorScope + fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle { + val extras = + activity.intent.extras + ?: error("MediaProjectionAppSelectorActivity should be launched with extras") + return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE) + ?: error("MediaProjectionAppSelectorActivity should be provided with " + + "$EXTRA_HOST_APP_USER_HANDLE extra") + } + + @Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context) @Provides @MediaProjectionAppSelector @@ -124,6 +158,8 @@ interface MediaProjectionAppSelectorComponent { val controller: MediaProjectionAppSelectorController val recentsViewController: MediaProjectionRecentsViewController + @get:HostUserHandle val hostUserHandle: UserHandle + @get:PersonalProfile val personalProfileUserHandle: UserHandle @MediaProjectionAppSelector val configurationController: ConfigurationController } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt index d744a40b60d8..52c7ca3bb3d4 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt @@ -17,24 +17,36 @@ package com.android.systemui.mediaprojection.appselector import android.content.ComponentName +import android.os.UserHandle +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import javax.inject.Inject @MediaProjectionAppSelectorScope -class MediaProjectionAppSelectorController @Inject constructor( +class MediaProjectionAppSelectorController +@Inject +constructor( private val recentTaskListProvider: RecentTaskListProvider, private val view: MediaProjectionAppSelectorView, + private val flags: FeatureFlags, + @HostUserHandle private val hostUserHandle: UserHandle, @MediaProjectionAppSelector private val scope: CoroutineScope, @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName ) { fun init() { scope.launch { - val tasks = recentTaskListProvider.loadRecentTasks().sortTasks() + val recentTasks = recentTaskListProvider.loadRecentTasks() + + val tasks = recentTasks + .filterDevicePolicyRestrictedTasks() + .sortedTasks() + view.bind(tasks) } } @@ -43,9 +55,20 @@ class MediaProjectionAppSelectorController @Inject constructor( scope.cancel() } - private fun List<RecentTask>.sortTasks(): List<RecentTask> = - sortedBy { - // Show normal tasks first and only then tasks with opened app selector - it.topActivityComponent == appSelectorComponentName + /** + * Removes all recent tasks that are different from the profile of the host app to avoid any + * cross-profile sharing + */ + private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = + if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) { + // TODO(b/263950746): filter tasks based on the enterprise policies + this + } else { + filter { UserHandle.of(it.userId) == hostUserHandle } } + + private fun List<RecentTask>.sortedTasks(): List<RecentTask> = sortedBy { + // Show normal tasks first and only then tasks with opened app selector + it.topActivityComponent == appSelectorComponentName + } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt index cd994b857e95..41e22860d0ad 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt @@ -17,11 +17,12 @@ package com.android.systemui.mediaprojection.appselector.data import android.annotation.ColorInt +import android.annotation.UserIdInt import android.content.ComponentName data class RecentTask( val taskId: Int, - val userId: Int, + @UserIdInt val userId: Int, val topActivityComponent: ComponentName?, val baseIntentComponent: ComponentName?, @ColorInt val colorBackground: Int? diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 13c5b48906c5..c7bad634db77 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -273,7 +273,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Override public void triggerBack() { // Notify FalsingManager that an intentional gesture has occurred. - // TODO(b/186519446): use a different method than isFalseTouch mFalsingManager.isFalseTouch(BACK_GESTURE); // Only inject back keycodes when ahead-of-time back dispatching is disabled. if (mBackAnimation == null) { @@ -919,6 +918,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mThresholdCrossed = true; // Capture inputs mInputMonitor.pilferPointers(); + if (mBackAnimation != null) { + // Notify FalsingManager that an intentional gesture has occurred. + mFalsingManager.isFalseTouch(BACK_GESTURE); + } mInputEventReceiver.setBatchingEnabled(true); } else { logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt index 44b18ec4639b..68e3dcdb63ea 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt @@ -23,6 +23,7 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.os.ResultReceiver +import android.os.UserHandle import android.view.View import android.view.View.GONE import android.view.View.VISIBLE @@ -77,6 +78,14 @@ class ScreenRecordPermissionDialog( MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER, CaptureTargetResultReceiver() ) + + // Send SystemUI's user handle as the host app user handle because SystemUI + // is the 'host app' (the app that receives screen capture data) + intent.putExtra( + MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE, + UserHandle.of(UserHandle.myUserId()) + ) + val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!) if (animationController == null) { dismiss() diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 10130b0a0882..c0017bee053b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -715,7 +715,7 @@ public final class NotificationPanelViewController implements Dumpable { updatePanelExpansionAndVisibility(); }; private final Runnable mMaybeHideExpandedRunnable = () -> { - if (getExpansionFraction() == 0.0f) { + if (getExpandedFraction() == 0.0f) { postToView(mHideExpandedRunnable); } }; @@ -5451,10 +5451,6 @@ public final class NotificationPanelViewController implements Dumpable { InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); } - private float getExpansionFraction() { - return mExpandedFraction; - } - private ShadeExpansionStateManager getShadeExpansionStateManager() { return mShadeExpansionStateManager; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 584a382184f3..cd423dc13c9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -45,12 +45,14 @@ import android.hardware.fingerprint.IUdfpsHbmListener; import android.inputmethodservice.InputMethodService.BackDispositionMode; import android.media.INearbyMediaDevicesProvider; import android.media.MediaRoute2Info; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.RemoteException; import android.util.Pair; import android.util.SparseArray; @@ -168,6 +170,7 @@ public class CommandQueue extends IStatusBar.Stub implements private static final int MSG_SHOW_REAR_DISPLAY_DIALOG = 69 << MSG_SHIFT; private static final int MSG_GO_TO_FULLSCREEN_FROM_SPLIT = 70 << MSG_SHIFT; private static final int MSG_ENTER_STAGE_SPLIT_FROM_RUNNING_APP = 71 << MSG_SHIFT; + private static final int MSG_SHOW_MEDIA_OUTPUT_SWITCHER = 72 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -492,6 +495,11 @@ public class CommandQueue extends IStatusBar.Stub implements * @see IStatusBar#enterStageSplitFromRunningApp */ default void enterStageSplitFromRunningApp(boolean leftOrTop) {} + + /** + * @see IStatusBar#showMediaOutputSwitcher + */ + default void showMediaOutputSwitcher(String packageName) {} } public CommandQueue(Context context) { @@ -1262,6 +1270,19 @@ public class CommandQueue extends IStatusBar.Stub implements } @Override + public void showMediaOutputSwitcher(String packageName) { + int callingUid = Binder.getCallingUid(); + if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { + throw new SecurityException("Call only allowed from system server."); + } + synchronized (mLock) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = packageName; + mHandler.obtainMessage(MSG_SHOW_MEDIA_OUTPUT_SWITCHER, args).sendToTarget(); + } + } + + @Override public void requestAddTile( @NonNull ComponentName componentName, @NonNull CharSequence appName, @@ -1777,6 +1798,13 @@ public class CommandQueue extends IStatusBar.Stub implements mCallbacks.get(i).enterStageSplitFromRunningApp((Boolean) msg.obj); } break; + case MSG_SHOW_MEDIA_OUTPUT_SWITCHER: + args = (SomeArgs) msg.obj; + String clientPackageName = (String) args.arg1; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).showMediaOutputSwitcher(clientPackageName); + } + break; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt index 3072c810d31b..05a9a427870e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt @@ -35,14 +35,6 @@ class NotifPipelineFlags @Inject constructor( fun fsiOnDNDUpdate(): Boolean = featureFlags.isEnabled(Flags.FSI_ON_DND_UPDATE) - val isStabilityIndexFixEnabled: Boolean by lazy { - featureFlags.isEnabled(Flags.STABILITY_INDEX_FIX) - } - - val isSemiStableSortEnabled: Boolean by lazy { - featureFlags.isEnabled(Flags.SEMI_STABLE_SORT) - } - val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy { featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt index 84ab0d1190f0..b5fce4163bb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt @@ -98,13 +98,11 @@ data class ListAttachState private constructor( * This can happen if the entry is removed from a group that was broken up or if the entry was * filtered out during any of the filtering steps. */ - fun detach(includingStableIndex: Boolean) { + fun detach() { parent = null section = null promoter = null - if (includingStableIndex) { - stableIndex = -1 - } + stableIndex = -1 } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index 65a21a4e2190..4065b98ab0c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -965,8 +965,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable { * filtered out during any of the filtering steps. */ private void annulAddition(ListEntry entry) { - // NOTE(b/241229236): Don't clear stableIndex until we fix stability fragility - entry.getAttachState().detach(/* includingStableIndex= */ mFlags.isSemiStableSortEnabled()); + entry.getAttachState().detach(); } private void assignSections() { @@ -986,50 +985,10 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable { private void sortListAndGroups() { Trace.beginSection("ShadeListBuilder.sortListAndGroups"); - if (mFlags.isSemiStableSortEnabled()) { - sortWithSemiStableSort(); - } else { - sortWithLegacyStability(); - } + sortWithSemiStableSort(); Trace.endSection(); } - private void sortWithLegacyStability() { - // Sort all groups and the top level list - for (ListEntry entry : mNotifList) { - if (entry instanceof GroupEntry) { - GroupEntry parent = (GroupEntry) entry; - parent.sortChildren(mGroupChildrenComparator); - } - } - mNotifList.sort(mTopLevelComparator); - assignIndexes(mNotifList); - - // Check for suppressed order changes - if (!getStabilityManager().isEveryChangeAllowed()) { - mForceReorderable = true; - boolean isSorted = isShadeSortedLegacy(); - mForceReorderable = false; - if (!isSorted) { - getStabilityManager().onEntryReorderSuppressed(); - } - } - } - - private boolean isShadeSortedLegacy() { - if (!isSorted(mNotifList, mTopLevelComparator)) { - return false; - } - for (ListEntry entry : mNotifList) { - if (entry instanceof GroupEntry) { - if (!isSorted(((GroupEntry) entry).getChildren(), mGroupChildrenComparator)) { - return false; - } - } - } - return true; - } - private void sortWithSemiStableSort() { // Sort each group's children boolean allSorted = true; @@ -1100,29 +1059,16 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable { sectionMemberIndex = 0; currentSection = section; } - if (mFlags.isStabilityIndexFixEnabled()) { - entry.getAttachState().setStableIndex(sectionMemberIndex++); - if (entry instanceof GroupEntry) { - final GroupEntry parent = (GroupEntry) entry; - final NotificationEntry summary = parent.getSummary(); - if (summary != null) { - summary.getAttachState().setStableIndex(sectionMemberIndex++); - } - for (NotificationEntry child : parent.getChildren()) { - child.getAttachState().setStableIndex(sectionMemberIndex++); - } + entry.getAttachState().setStableIndex(sectionMemberIndex++); + if (entry instanceof GroupEntry) { + final GroupEntry parent = (GroupEntry) entry; + final NotificationEntry summary = parent.getSummary(); + if (summary != null) { + summary.getAttachState().setStableIndex(sectionMemberIndex++); } - } else { - // This old implementation uses the same index number for the group as the first - // child, and fails to assign an index to the summary. Remove once tested. - entry.getAttachState().setStableIndex(sectionMemberIndex); - if (entry instanceof GroupEntry) { - final GroupEntry parent = (GroupEntry) entry; - for (NotificationEntry child : parent.getChildren()) { - child.getAttachState().setStableIndex(sectionMemberIndex++); - } + for (NotificationEntry child : parent.getChildren()) { + child.getAttachState().setStableIndex(sectionMemberIndex++); } - sectionMemberIndex++; } } } @@ -1272,11 +1218,6 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable { o2.getSectionIndex()); if (cmp != 0) return cmp; - cmp = mFlags.isSemiStableSortEnabled() ? 0 : Integer.compare( - getStableOrderIndex(o1), - getStableOrderIndex(o2)); - if (cmp != 0) return cmp; - NotifComparator sectionComparator = getSectionComparator(o1, o2); if (sectionComparator != null) { cmp = sectionComparator.compare(o1, o2); @@ -1301,12 +1242,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable { private final Comparator<NotificationEntry> mGroupChildrenComparator = (o1, o2) -> { - int cmp = mFlags.isSemiStableSortEnabled() ? 0 : Integer.compare( - getStableOrderIndex(o1), - getStableOrderIndex(o2)); - if (cmp != 0) return cmp; - - cmp = Integer.compare( + int cmp = Integer.compare( o1.getRepresentativeEntry().getRanking().getRank(), o2.getRepresentativeEntry().getRanking().getRank()); if (cmp != 0) return cmp; @@ -1317,25 +1253,6 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable { return cmp; }; - /** - * A flag that is set to true when we want to run the comparators as if all reordering is - * allowed. This is used to check if the list is "out of order" after the sort is complete. - */ - private boolean mForceReorderable = false; - - private int getStableOrderIndex(ListEntry entry) { - if (mForceReorderable) { - // this is used to determine if the list is correctly sorted - return -1; - } - if (getStabilityManager().isEntryReorderingAllowed(entry)) { - // let the stability manager constrain or allow reordering - return -1; - } - // NOTE(b/241229236): Can't use cleared section index until we fix stability fragility - return entry.getPreviousAttachState().getStableIndex(); - } - @Nullable private Integer getStableOrderRank(ListEntry entry) { if (getStabilityManager().isEntryReorderingAllowed(entry)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index f1e1f426b467..8110b4747b63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -312,6 +312,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final Context mContext; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; + private final DeviceStateManager mDeviceStateManager; private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks; private float mTransitionToFullShadeProgress = 0f; private NotificationListContainer mNotifListContainer; @@ -879,8 +880,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT, id -> onLaunchTransitionTimeout()); - deviceStateManager.registerCallback(mMainExecutor, - new FoldStateListener(mContext, this::onFoldedStateChanged)); + mDeviceStateManager = deviceStateManager; wiredChargingRippleController.registerCallbacks(); mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy; @@ -1069,6 +1069,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } }); + registerCallbacks(); + mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener); mPluginManager.addPluginListener( @@ -1124,6 +1126,14 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @VisibleForTesting + /** Registers listeners/callbacks with external dependencies. */ + void registerCallbacks() { + //TODO(b/264502026) move the rest of the listeners here. + mDeviceStateManager.registerCallback(mMainExecutor, + new FoldStateListener(mContext, this::onFoldedStateChanged)); + } + + @VisibleForTesting void initShadeVisibilityListener() { mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index aef25e3f0932..1dc34fad532a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -37,7 +37,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; -import android.window.OnBackInvokedCallback; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedDispatcher; import androidx.annotation.NonNull; @@ -198,11 +199,38 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } }; - private final OnBackInvokedCallback mOnBackInvokedCallback = () -> { - if (DEBUG) { - Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()"); + private final OnBackAnimationCallback mOnBackInvokedCallback = new OnBackAnimationCallback() { + @Override + public void onBackInvoked() { + if (DEBUG) { + Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()"); + } + onBackPressed(); + if (shouldPlayBackAnimation()) { + mPrimaryBouncerView.getDelegate().getBackCallback().onBackInvoked(); + } + } + + @Override + public void onBackProgressed(BackEvent event) { + if (shouldPlayBackAnimation()) { + mPrimaryBouncerView.getDelegate().getBackCallback().onBackProgressed(event); + } + } + + @Override + public void onBackCancelled() { + if (shouldPlayBackAnimation()) { + mPrimaryBouncerView.getDelegate().getBackCallback().onBackCancelled(); + } + } + + @Override + public void onBackStarted(BackEvent event) { + if (shouldPlayBackAnimation()) { + mPrimaryBouncerView.getDelegate().getBackCallback().onBackStarted(event); + } } - onBackPressed(); }; private boolean mIsBackCallbackRegistered = false; @@ -256,6 +284,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mIsModernBouncerEnabled; private boolean mIsUnoccludeTransitionFlagEnabled; private boolean mIsModernAlternateBouncerEnabled; + private boolean mIsBackAnimationEnabled; private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; @@ -337,6 +366,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER); mAlternateBouncerInteractor = alternateBouncerInteractor; + mIsBackAnimationEnabled = + featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM); } @Override @@ -472,6 +503,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } + private boolean shouldPlayBackAnimation() { + // Suppress back animation when bouncer shouldn't be dismissed on back invocation. + return !needsFullscreenBouncer() && mIsBackAnimationEnabled; + } + @Override public void onDensityOrFontScaleChanged() { hideBouncer(true /* destroyView */); diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt index 7726d09cf971..8214822f0335 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt @@ -3,26 +3,43 @@ package com.android.systemui.unfold import android.os.SystemProperties import android.os.VibrationEffect import android.os.Vibrator +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback +import java.util.concurrent.Executor import javax.inject.Inject -/** - * Class that plays a haptics effect during unfolding a foldable device - */ +/** Class that plays a haptics effect during unfolding a foldable device */ @SysUIUnfoldScope class UnfoldHapticsPlayer @Inject constructor( unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider, + foldProvider: FoldProvider, + @Main private val mainExecutor: Executor, private val vibrator: Vibrator? ) : TransitionProgressListener { + private var isFirstAnimationAfterUnfold = false + init { if (vibrator != null) { // We don't need to remove the callback because we should listen to it // the whole time when SystemUI process is alive unfoldTransitionProgressProvider.addCallback(this) } + + foldProvider.registerCallback( + object : FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + if (isFolded) { + isFirstAnimationAfterUnfold = true + } + } + }, + mainExecutor + ) } private var lastTransitionProgress = TRANSITION_PROGRESS_FULL_OPEN @@ -36,6 +53,13 @@ constructor( } override fun onTransitionFinishing() { + // Run haptics only when unfolding the device (first animation after unfolding) + if (!isFirstAnimationAfterUnfold) { + return + } + + isFirstAnimationAfterUnfold = false + // Run haptics only if the animation is long enough to notice if (lastTransitionProgress < TRANSITION_NOTICEABLE_THRESHOLD) { playHaptics() diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 84f6d913b310..075ef9df9664 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -405,6 +405,13 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } @Test + public void onBouncerVisibilityChanged_resetsScale() { + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE); + + verify(mView).resetScale(); + } + + @Test public void onStartingToHide_sideFpsHintShown_sideFpsHintHidden() { setupGetSecurityView(); setupConditionsToEnableSideFpsHint(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 36ed669e299c..1bbc19931c21 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -49,6 +49,8 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import androidx.constraintlayout.widget.ConstraintSet; import androidx.test.filters.SmallTest; @@ -357,6 +359,27 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID); } + @Test + public void testPlayBackAnimation() { + OnBackAnimationCallback backCallback = mKeyguardSecurityContainer.getBackCallback(); + backCallback.onBackStarted(createBackEvent(0, 0)); + mKeyguardSecurityContainer.getBackCallback().onBackProgressed( + createBackEvent(0, 1)); + assertThat(mKeyguardSecurityContainer.getScaleX()).isEqualTo( + KeyguardSecurityContainer.MIN_BACK_SCALE); + assertThat(mKeyguardSecurityContainer.getScaleY()).isEqualTo( + KeyguardSecurityContainer.MIN_BACK_SCALE); + + // reset scale + mKeyguardSecurityContainer.resetScale(); + assertThat(mKeyguardSecurityContainer.getScaleX()).isEqualTo(1); + assertThat(mKeyguardSecurityContainer.getScaleY()).isEqualTo(1); + } + + private BackEvent createBackEvent(float touchX, float progress) { + return new BackEvent(0, 0, progress, BackEvent.EDGE_LEFT); + } + private Configuration configuration(@Configuration.Orientation int orientation) { Configuration config = new Configuration(); config.orientation = orientation; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index d910a275967b..e87f1042927f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -144,7 +144,7 @@ public class DozeSensorsTest extends SysuiTestCase { @Test public void testSensorDebounce() { - mDozeSensors.setListening(true, true); + mDozeSensors.setListening(true, true, true); mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class)); mTestableLooper.processAllMessages(); @@ -162,7 +162,7 @@ public class DozeSensorsTest extends SysuiTestCase { @Test public void testSetListening_firstTrue_registerSettingsObserver() { verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt()); - mDozeSensors.setListening(true, true); + mDozeSensors.setListening(true, true, true); verify(mTriggerSensor).registerSettingsObserver(any(ContentObserver.class)); } @@ -170,8 +170,8 @@ public class DozeSensorsTest extends SysuiTestCase { @Test public void testSetListening_twiceTrue_onlyRegisterSettingsObserverOnce() { verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt()); - mDozeSensors.setListening(true, true); - mDozeSensors.setListening(true, true); + mDozeSensors.setListening(true, true, true); + mDozeSensors.setListening(true, true, true); verify(mTriggerSensor, times(1)).registerSettingsObserver(any(ContentObserver.class)); } @@ -196,7 +196,7 @@ public class DozeSensorsTest extends SysuiTestCase { assertFalse(mSensorTap.mRequested); // WHEN we're now in a low powered state - dozeSensors.setListening(true, true, true); + dozeSensors.setListeningWithPowerState(true, true, true, true); // THEN the tap sensor is registered assertTrue(mSensorTap.mRequested); @@ -207,12 +207,12 @@ public class DozeSensorsTest extends SysuiTestCase { // GIVEN doze sensors enabled when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); - // GIVEN a trigger sensor + // GIVEN a trigger sensor that's enabled by settings Sensor mockSensor = mock(Sensor.class); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled( mockSensor, - /* settingEnabled */ true, - /* requiresTouchScreen */ true); + /* settingEnabled */ true + ); when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) .thenReturn(true); @@ -228,12 +228,12 @@ public class DozeSensorsTest extends SysuiTestCase { // GIVEN doze sensors enabled when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); - // GIVEN a trigger sensor + // GIVEN a trigger sensor that's not enabled by settings Sensor mockSensor = mock(Sensor.class); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled( mockSensor, - /* settingEnabled*/ false, - /* requiresTouchScreen */ true); + /* settingEnabled*/ false + ); when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) .thenReturn(true); @@ -249,12 +249,12 @@ public class DozeSensorsTest extends SysuiTestCase { // GIVEN doze sensors enabled when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); - // GIVEN a trigger sensor that's + // GIVEN a trigger sensor that's not enabled by settings Sensor mockSensor = mock(Sensor.class); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled( mockSensor, - /* settingEnabled*/ false, - /* requiresTouchScreen */ true); + /* settingEnabled*/ false + ); when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) .thenReturn(true); @@ -264,7 +264,7 @@ public class DozeSensorsTest extends SysuiTestCase { // WHEN ignoreSetting is called triggerSensor.ignoreSetting(true); - // THEN the sensor is registered + // THEN the sensor is still registered since the setting is ignore assertTrue(triggerSensor.mRegistered); } @@ -275,10 +275,10 @@ public class DozeSensorsTest extends SysuiTestCase { // GIVEN a trigger sensor Sensor mockSensor = mock(Sensor.class); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorWithSettingEnabled( mockSensor, - /* settingEnabled*/ true, - /* requiresTouchScreen */ true); + /* settingEnabled*/ true + ); when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) .thenReturn(true); @@ -295,7 +295,7 @@ public class DozeSensorsTest extends SysuiTestCase { // GIVEN doze sensor that supports postures Sensor closedSensor = createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); Sensor openedSensor = createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_LIGHT); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture( new Sensor[] { null /* unknown */, closedSensor, @@ -316,7 +316,7 @@ public class DozeSensorsTest extends SysuiTestCase { // GIVEN doze sensor that supports postures Sensor closedSensor = createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); Sensor openedSensor = createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_LIGHT); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture( new Sensor[] { null /* unknown */, closedSensor, @@ -345,7 +345,7 @@ public class DozeSensorsTest extends SysuiTestCase { // GIVEN doze sensor that supports postures Sensor closedSensor = createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); Sensor openedSensor = createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_LIGHT); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture( new Sensor[] { null /* unknown */, closedSensor, @@ -400,7 +400,7 @@ public class DozeSensorsTest extends SysuiTestCase { public void testUdfpsEnrollmentChanged() throws Exception { // GIVEN a UDFPS_LONG_PRESS trigger sensor that's not configured Sensor mockSensor = mock(Sensor.class); - TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + TriggerSensor triggerSensor = mDozeSensors.createDozeSensorForPosture( mockSensor, REASON_SENSOR_UDFPS_LONG_PRESS, /* configured */ false); @@ -409,7 +409,7 @@ public class DozeSensorsTest extends SysuiTestCase { .thenReturn(true); // WHEN listening state is set to TRUE - mDozeSensors.setListening(true, true); + mDozeSensors.setListening(true, true, true); // THEN mRegistered is still false b/c !mConfigured assertFalse(triggerSensor.mConfigured); @@ -439,6 +439,35 @@ public class DozeSensorsTest extends SysuiTestCase { } @Test + public void aodOnlySensor_onlyRegisteredWhenAodSensorsIncluded() { + // GIVEN doze sensors enabled + when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); + + // GIVEN a trigger sensor that requires aod + Sensor mockSensor = mock(Sensor.class); + TriggerSensor aodOnlyTriggerSensor = mDozeSensors.createDozeSensorRequiringAod(mockSensor); + when(mSensorManager.requestTriggerSensor(eq(aodOnlyTriggerSensor), eq(mockSensor))) + .thenReturn(true); + mDozeSensors.addSensor(aodOnlyTriggerSensor); + + // WHEN aod only sensors aren't included + mDozeSensors.setListening(/* listen */ true, /* includeTouchScreenSensors */true, + /* includeAodOnlySensors */false); + + // THEN the sensor is not registered or requested + assertFalse(aodOnlyTriggerSensor.mRequested); + assertFalse(aodOnlyTriggerSensor.mRegistered); + + // WHEN aod only sensors ARE included + mDozeSensors.setListening(/* listen */ true, /* includeTouchScreenSensors */true, + /* includeAodOnlySensors */true); + + // THEN the sensor is registered and requested + assertTrue(aodOnlyTriggerSensor.mRequested); + assertTrue(aodOnlyTriggerSensor.mRegistered); + } + + @Test public void liftToWake_defaultSetting_configDefaultFalse() { // WHEN the default lift to wake gesture setting is false when(mResources.getBoolean( @@ -494,8 +523,8 @@ public class DozeSensorsTest extends SysuiTestCase { mTriggerSensors = new TriggerSensor[] {mTriggerSensor, mSensorTap}; } - public TriggerSensor createDozeSensor(Sensor sensor, boolean settingEnabled, - boolean requiresTouchScreen) { + public TriggerSensor createDozeSensorWithSettingEnabled(Sensor sensor, + boolean settingEnabled) { return new TriggerSensor(/* sensor */ sensor, /* setting name */ "test_setting", /* settingDefault */ settingEnabled, @@ -504,11 +533,13 @@ public class DozeSensorsTest extends SysuiTestCase { /* reportsTouchCoordinate*/ false, /* requiresTouchscreen */ false, /* ignoresSetting */ false, - requiresTouchScreen, - /* immediatelyReRegister */ true); + /* requiresProx */ false, + /* immediatelyReRegister */ true, + /* requiresAod */false + ); } - public TriggerSensor createDozeSensor( + public TriggerSensor createDozeSensorForPosture( Sensor sensor, int pulseReason, boolean configured @@ -522,15 +553,35 @@ public class DozeSensorsTest extends SysuiTestCase { /* requiresTouchscreen */ false, /* ignoresSetting */ false, /* requiresTouchScreen */ false, - /* immediatelyReRegister*/ true); + /* immediatelyReRegister*/ true, + false + ); } /** - * create a doze sensor that supports postures and is enabled + * Create a doze sensor that requires Aod */ - public TriggerSensor createDozeSensor(Sensor[] sensors, int posture) { + public TriggerSensor createDozeSensorRequiringAod(Sensor sensor) { + return new TriggerSensor(/* sensor */ sensor, + /* setting name */ "aod_requiring_sensor", + /* settingDefault */ true, + /* configured */ true, + /* pulseReason*/ 0, + /* reportsTouchCoordinate*/ false, + /* requiresTouchscreen */ false, + /* ignoresSetting */ false, + /* requiresProx */ false, + /* immediatelyReRegister */ true, + /* requiresAoD */ true + ); + } + + /** + * Create a doze sensor that supports postures and is enabled + */ + public TriggerSensor createDozeSensorForPosture(Sensor[] sensors, int posture) { return new TriggerSensor(/* sensor */ sensors, - /* setting name */ "test_setting", + /* setting name */ "posture_test_setting", /* settingDefault */ true, /* configured */ true, /* pulseReason*/ 0, @@ -539,7 +590,9 @@ public class DozeSensorsTest extends SysuiTestCase { /* ignoresSetting */ true, /* requiresProx */ false, /* immediatelyReRegister */ true, - posture); + posture, + /* requiresUi */ false + ); } public void addSensor(TriggerSensor sensor) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index b66a454638ac..3552399586a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -395,6 +395,14 @@ public class DozeTriggersTest extends SysuiTestCase { verify(mAuthController).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); } + + @Test + public void udfpsLongPress_dozeState_notRegistered() { + // GIVEN device is DOZE_AOD_PAUSED + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + // beverlyt + } + private void waitForSensorManager() { mExecutor.runAllReady(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt index 19d2d334b884..1042ea714936 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt @@ -1,12 +1,16 @@ package com.android.systemui.mediaprojection.appselector import android.content.ComponentName +import android.os.UserHandle import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import org.junit.Test @@ -21,11 +25,17 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { private val scope = CoroutineScope(Dispatchers.Unconfined) private val appSelectorComponentName = ComponentName("com.test", "AppSelector") + private val hostUserHandle = UserHandle.of(123) + private val otherUserHandle = UserHandle.of(456) + private val view: MediaProjectionAppSelectorView = mock() + private val featureFlags: FeatureFlags = mock() private val controller = MediaProjectionAppSelectorController( taskListProvider, view, + featureFlags, + hostUserHandle, scope, appSelectorComponentName ) @@ -98,15 +108,72 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { ) } + @Test + fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsOnlyTasksWithHostProfile() { + givenEnterprisePoliciesFeatureFlag(enabled = false) + + val tasks = listOf( + createRecentTask(taskId = 1, userId = hostUserHandle.identifier), + createRecentTask(taskId = 2, userId = otherUserHandle.identifier), + createRecentTask(taskId = 3, userId = hostUserHandle.identifier), + createRecentTask(taskId = 4, userId = otherUserHandle.identifier), + createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + ) + taskListProvider.tasks = tasks + + controller.init() + + verify(view).bind( + listOf( + createRecentTask(taskId = 1, userId = hostUserHandle.identifier), + createRecentTask(taskId = 3, userId = hostUserHandle.identifier), + createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + ) + ) + } + + @Test + fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesEnabled_bindsAllTasks() { + givenEnterprisePoliciesFeatureFlag(enabled = true) + + val tasks = listOf( + createRecentTask(taskId = 1, userId = hostUserHandle.identifier), + createRecentTask(taskId = 2, userId = otherUserHandle.identifier), + createRecentTask(taskId = 3, userId = hostUserHandle.identifier), + createRecentTask(taskId = 4, userId = otherUserHandle.identifier), + createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + ) + taskListProvider.tasks = tasks + + controller.init() + + // TODO(b/233348916) should filter depending on the policies + verify(view).bind( + listOf( + createRecentTask(taskId = 1, userId = hostUserHandle.identifier), + createRecentTask(taskId = 2, userId = otherUserHandle.identifier), + createRecentTask(taskId = 3, userId = hostUserHandle.identifier), + createRecentTask(taskId = 4, userId = otherUserHandle.identifier), + createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + ) + ) + } + + private fun givenEnterprisePoliciesFeatureFlag(enabled: Boolean) { + whenever(featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) + .thenReturn(enabled) + } + private fun createRecentTask( taskId: Int, - topActivityComponent: ComponentName? = null + topActivityComponent: ComponentName? = null, + userId: Int = hostUserHandle.identifier ): RecentTask { return RecentTask( taskId = taskId, topActivityComponent = topActivityComponent, baseIntentComponent = ComponentName("com", "Test"), - userId = 0, + userId = userId, colorBackground = 0 ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index 09f8a10f88c7..a8690388c3e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -39,7 +39,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -137,7 +136,6 @@ public class ShadeListBuilderTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); allowTestableLooperAsMainThread(); - when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(true); mListBuilder = new ShadeListBuilder( mDumpManager, @@ -1998,29 +1996,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { } @Test - public void testActiveOrdering_withLegacyStability() { - when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(false); - assertOrder("ABCDEFG", "ABCDEFG", "ABCDEFG", true); // no change - assertOrder("ABCDEFG", "ACDEFXBG", "ACDEFXBG", true); // X - assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG", true); // no change - assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG", true); // Z and X - assertOrder("ABCDEFG", "AXCDEZFBG", "AXCDEZFBG", true); // Z and X + gap - } - - @Test - public void testStableOrdering_withLegacyStability() { - when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(false); - mStabilityManager.setAllowEntryReordering(false); - assertOrder("ABCDEFG", "ABCDEFG", "ABCDEFG", true); // no change - assertOrder("ABCDEFG", "ACDEFXBG", "XABCDEFG", false); // X - assertOrder("ABCDEFG", "ACDEFBG", "ABCDEFG", false); // no change - assertOrder("ABCDEFG", "ACDEFBXZG", "XZABCDEFG", false); // Z and X - assertOrder("ABCDEFG", "AXCDEZFBG", "XZABCDEFG", false); // Z and X + gap - } - - @Test public void testStableOrdering() { - when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(true); mStabilityManager.setAllowEntryReordering(false); // No input or output assertOrder("", "", "", true); @@ -2076,7 +2052,6 @@ public class ShadeListBuilderTest extends SysuiTestCase { @Test public void testActiveOrdering() { - when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(true); assertOrder("ABCDEFG", "ACDEFXBG", "ACDEFXBG", true); // X assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG", true); // no change assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG", true); // Z and X @@ -2133,7 +2108,6 @@ public class ShadeListBuilderTest extends SysuiTestCase { @Test public void stableOrderingDisregardedWithSectionChange() { - when(mNotifPipelineFlags.isSemiStableSortEnabled()).thenReturn(true); // GIVEN the first sectioner's packages can be changed from run-to-run List<String> mutableSectionerPackages = new ArrayList<>(); mutableSectionerPackages.add(PACKAGE_1); @@ -2229,49 +2203,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { } @Test - public void groupRevertingToSummaryDoesNotRetainStablePositionWithLegacyIndexLogic() { - when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(false); - - // GIVEN a notification group is on screen - mStabilityManager.setAllowEntryReordering(false); - - // WHEN the list is originally built with reordering disabled (and section changes allowed) - addNotif(0, PACKAGE_1).setRank(2); - addNotif(1, PACKAGE_1).setRank(3); - addGroupSummary(2, PACKAGE_1, "group").setRank(4); - addGroupChild(3, PACKAGE_1, "group").setRank(5); - addGroupChild(4, PACKAGE_1, "group").setRank(6); - dispatchBuild(); - - verifyBuiltList( - notif(0), - notif(1), - group( - summary(2), - child(3), - child(4) - ) - ); - - // WHEN the notification summary rank increases and children removed - setNewRank(notif(2).entry, 1); - mEntrySet.remove(4); - mEntrySet.remove(3); - dispatchBuild(); - - // VERIFY the summary (incorrectly) moves to the top of the section where it is ranked, - // despite visual stability being active - verifyBuiltList( - notif(2), - notif(0), - notif(1) - ); - } - - @Test public void groupRevertingToSummaryRetainsStablePosition() { - when(mNotifPipelineFlags.isStabilityIndexFixEnabled()).thenReturn(true); - // GIVEN a notification group is on screen mStabilityManager.setAllowEntryReordering(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index c8157ccc8a9a..4c1b219af843 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -544,6 +544,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mCentralSurfaces.startKeyguard(); mInitController.executePostInitTasks(); notificationLogger.setUpWithContainer(mNotificationListContainer); + mCentralSurfaces.registerCallbacks(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index e3e648a8f716..dea4a7d19dcb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -39,6 +39,8 @@ import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; import android.window.WindowOnBackInvokedDispatcher; @@ -56,6 +58,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.data.BouncerView; import com.android.systemui.keyguard.data.BouncerViewDelegate; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; @@ -120,16 +123,19 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private BouncerView mBouncerView; @Mock private BouncerViewDelegate mBouncerViewDelegate; + @Mock private OnBackAnimationCallback mBouncerViewDelegateBackCallback; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback; private FakeKeyguardStateController mKeyguardStateController = spy(new FakeKeyguardStateController()); - @Mock private ViewRootImpl mViewRootImpl; - @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher; + @Mock + private ViewRootImpl mViewRootImpl; + @Mock + private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher; @Captor - private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback; + private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor; @Before @@ -140,6 +146,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class))) .thenReturn(mKeyguardMessageAreaController); when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate); + when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback); + when(mFeatureFlags + .isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM)) + .thenReturn(true); when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(true); @@ -193,7 +203,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test public void dismissWithAction_AfterKeyguardGoneSetToFalse() { OnDismissAction action = () -> false; - Runnable cancelAction = () -> {}; + Runnable cancelAction = () -> { + }; mStatusBarKeyguardViewManager.dismissWithAction( action, cancelAction, false /* afterKeyguardGone */); verify(mPrimaryBouncerInteractor).setDismissAction(eq(action), eq(cancelAction)); @@ -541,12 +552,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mBouncerExpansionCallback.onVisibilityChanged(true); verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), - mOnBackInvokedCallback.capture()); + mBackCallbackCaptor.capture()); /* verify that the same callback is unregistered when the bouncer becomes invisible */ mBouncerExpansionCallback.onVisibilityChanged(false); verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback( - eq(mOnBackInvokedCallback.getValue())); + eq(mBackCallbackCaptor.getValue())); } @Test @@ -555,18 +566,63 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { /* capture the predictive back callback during registration */ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), - mOnBackInvokedCallback.capture()); + mBackCallbackCaptor.capture()); when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true); when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true); /* invoke the back callback directly */ - mOnBackInvokedCallback.getValue().onBackInvoked(); + mBackCallbackCaptor.getValue().onBackInvoked(); /* verify that the bouncer will be hidden as a result of the invocation */ verify(mCentralSurfaces).setBouncerShowing(eq(false)); } @Test + public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() { + when(mKeyguardSecurityModel.getSecurityMode(anyInt())) + .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin); + mBouncerExpansionCallback.onVisibilityChanged(true); + /* capture the predictive back callback during registration */ + verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + mBackCallbackCaptor.capture()); + assertTrue(mBackCallbackCaptor.getValue() instanceof OnBackAnimationCallback); + + OnBackAnimationCallback backCallback = + (OnBackAnimationCallback) mBackCallbackCaptor.getValue(); + + BackEvent event = new BackEvent(0, 0, 0, BackEvent.EDGE_LEFT); + backCallback.onBackStarted(event); + verify(mBouncerViewDelegateBackCallback, never()).onBackStarted(any()); + } + + @Test + public void testPredictiveBackCallback_forwardsBackDispatches() { + mBouncerExpansionCallback.onVisibilityChanged(true); + /* capture the predictive back callback during registration */ + verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY), + mBackCallbackCaptor.capture()); + assertTrue(mBackCallbackCaptor.getValue() instanceof OnBackAnimationCallback); + + OnBackAnimationCallback backCallback = + (OnBackAnimationCallback) mBackCallbackCaptor.getValue(); + + BackEvent event = new BackEvent(0, 0, 0, BackEvent.EDGE_LEFT); + backCallback.onBackStarted(event); + verify(mBouncerViewDelegateBackCallback).onBackStarted(eq(event)); + + backCallback.onBackProgressed(event); + verify(mBouncerViewDelegateBackCallback).onBackProgressed(eq(event)); + + backCallback.onBackInvoked(); + verify(mBouncerViewDelegateBackCallback).onBackInvoked(); + + backCallback.onBackCancelled(); + verify(mBouncerViewDelegateBackCallback).onBackCancelled(); + } + + @Test public void testReportBouncerOnDreamWhenVisible() { mBouncerExpansionCallback.onVisibilityChanged(true); verify(mCentralSurfaces).setBouncerShowingOverDream(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt index 30c4f97e5503..f4226bcd71c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt @@ -175,8 +175,10 @@ class FoldAodAnimationControllerTest : SysuiTestCase() { fold() underTest.onScreenTurningOn({}) - underTest.onStartedWakingUp() + // The body of onScreenTurningOn is executed on fakeExecutor, + // run all pending tasks before calling the next method fakeExecutor.runAllReady() + underTest.onStartedWakingUp() verify(latencyTracker).onActionStart(any()) verify(latencyTracker).onActionCancel(any()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt index c31640279305..4a28cd1de255 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt @@ -26,6 +26,10 @@ class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, Transitio listeners.forEach { it.onTransitionFinished() } } + override fun onTransitionFinishing() { + listeners.forEach { it.onTransitionFinishing() } + } + override fun onTransitionProgress(progress: Float) { listeners.forEach { it.onTransitionProgress(progress) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt new file mode 100644 index 000000000000..d3fdbd94a5ac --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.unfold + +import android.os.VibrationEffect +import android.os.Vibrator +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import java.util.concurrent.Executor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class UnfoldHapticsPlayerTest : SysuiTestCase() { + + private val progressProvider = TestUnfoldTransitionProvider() + private val vibrator: Vibrator = mock() + private val testFoldProvider = TestFoldProvider() + + private lateinit var player: UnfoldHapticsPlayer + + @Before + fun before() { + player = UnfoldHapticsPlayer(progressProvider, testFoldProvider, Runnable::run, vibrator) + } + + @Test + fun testUnfoldingTransitionFinishingEarly_playsHaptics() { + testFoldProvider.onFoldUpdate(isFolded = true) + testFoldProvider.onFoldUpdate(isFolded = false) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.5f) + progressProvider.onTransitionFinishing() + + verify(vibrator).vibrate(any<VibrationEffect>()) + } + + @Test + fun testUnfoldingTransitionFinishingLate_doesNotPlayHaptics() { + testFoldProvider.onFoldUpdate(isFolded = true) + testFoldProvider.onFoldUpdate(isFolded = false) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.99f) + progressProvider.onTransitionFinishing() + + verify(vibrator, never()).vibrate(any<VibrationEffect>()) + } + + @Test + fun testFoldingAfterUnfolding_doesNotPlayHaptics() { + // Unfold + testFoldProvider.onFoldUpdate(isFolded = true) + testFoldProvider.onFoldUpdate(isFolded = false) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.5f) + progressProvider.onTransitionFinishing() + progressProvider.onTransitionFinished() + clearInvocations(vibrator) + + // Fold + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.5f) + progressProvider.onTransitionFinished() + testFoldProvider.onFoldUpdate(isFolded = true) + + verify(vibrator, never()).vibrate(any<VibrationEffect>()) + } + + @Test + fun testUnfoldingAfterFoldingAndUnfolding_playsHaptics() { + // Unfold + testFoldProvider.onFoldUpdate(isFolded = true) + testFoldProvider.onFoldUpdate(isFolded = false) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.5f) + progressProvider.onTransitionFinishing() + progressProvider.onTransitionFinished() + + // Fold + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.5f) + progressProvider.onTransitionFinished() + testFoldProvider.onFoldUpdate(isFolded = true) + clearInvocations(vibrator) + + // Unfold again + testFoldProvider.onFoldUpdate(isFolded = true) + testFoldProvider.onFoldUpdate(isFolded = false) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0.5f) + progressProvider.onTransitionFinishing() + progressProvider.onTransitionFinished() + + verify(vibrator).vibrate(any<VibrationEffect>()) + } + + private class TestFoldProvider : FoldProvider { + private val listeners = arrayListOf<FoldProvider.FoldCallback>() + + override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) { + listeners += callback + } + + override fun unregisterCallback(callback: FoldProvider.FoldCallback) { + listeners -= callback + } + + fun onFoldUpdate(isFolded: Boolean) { + listeners.forEach { it.onFoldUpdated(isFolded) } + } + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt index 5a868a4df354..cfb959e51d4e 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt @@ -22,8 +22,8 @@ import android.hardware.SensorManager import android.os.Handler import android.view.IWindowManager import com.android.systemui.unfold.config.UnfoldTransitionConfig -import com.android.systemui.unfold.dagger.UnfoldBackground import com.android.systemui.unfold.dagger.UnfoldMain +import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.RotationChangeProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider @@ -58,7 +58,7 @@ interface UnfoldSharedComponent { @BindsInstance sensorManager: SensorManager, @BindsInstance @UnfoldMain handler: Handler, @BindsInstance @UnfoldMain executor: Executor, - @BindsInstance @UnfoldBackground backgroundExecutor: Executor, + @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor, @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String, @BindsInstance windowManager: IWindowManager, @BindsInstance contentResolver: ContentResolver = context.contentResolver diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt index 3fa546914d3a..31616fa54bf4 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt @@ -16,9 +16,7 @@ package com.android.systemui.unfold -import android.hardware.SensorManager import com.android.systemui.unfold.config.UnfoldTransitionConfig -import com.android.systemui.unfold.dagger.UnfoldBackground import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider import com.android.systemui.unfold.updates.DeviceFoldStateProvider @@ -34,55 +32,18 @@ import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider import dagger.Module import dagger.Provides import java.util.Optional -import java.util.concurrent.Executor +import javax.inject.Provider import javax.inject.Singleton -@Module +@Module(includes = [UnfoldSharedInternalModule::class]) class UnfoldSharedModule { @Provides @Singleton - fun unfoldTransitionProgressProvider( - config: UnfoldTransitionConfig, - scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory, - tracingListener: ATraceLoggerTransitionProgressListener, - foldStateProvider: FoldStateProvider - ): Optional<UnfoldTransitionProgressProvider> = - if (!config.isEnabled) { - Optional.empty() - } else { - val baseProgressProvider = - if (config.isHingeAngleEnabled) { - PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider) - } else { - FixedTimingTransitionProgressProvider(foldStateProvider) - } - Optional.of( - scaleAwareProviderFactory.wrap(baseProgressProvider).apply { - // Always present callback that logs animation beginning and end. - addCallback(tracingListener) - } - ) - } - - @Provides - @Singleton fun provideFoldStateProvider( deviceFoldStateProvider: DeviceFoldStateProvider ): FoldStateProvider = deviceFoldStateProvider @Provides - fun hingeAngleProvider( - config: UnfoldTransitionConfig, - sensorManager: SensorManager, - @UnfoldBackground executor: Executor - ): HingeAngleProvider = - if (config.isHingeAngleEnabled) { - HingeSensorAngleProvider(sensorManager, executor) - } else { - EmptyHingeAngleProvider - } - - @Provides @Singleton fun unfoldKeyguardVisibilityProvider( impl: UnfoldKeyguardVisibilityManagerImpl @@ -94,3 +55,51 @@ class UnfoldSharedModule { impl: UnfoldKeyguardVisibilityManagerImpl ): UnfoldKeyguardVisibilityManager = impl } + +/** + * Needed as methods inside must be public, but their parameters can be internal (and, a public + * method can't have internal parameters). Making the module internal and included in a public one + * fixes the issue. + */ +@Module +internal class UnfoldSharedInternalModule { + @Provides + @Singleton + fun unfoldTransitionProgressProvider( + config: UnfoldTransitionConfig, + scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory, + tracingListener: ATraceLoggerTransitionProgressListener, + physicsBasedUnfoldTransitionProgressProvider: + Provider<PhysicsBasedUnfoldTransitionProgressProvider>, + fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>, + ): Optional<UnfoldTransitionProgressProvider> { + if (!config.isEnabled) { + return Optional.empty() + } + val baseProgressProvider = + if (config.isHingeAngleEnabled) { + physicsBasedUnfoldTransitionProgressProvider.get() + } else { + fixedTimingTransitionProgressProvider.get() + } + + return Optional.of( + scaleAwareProviderFactory.wrap(baseProgressProvider).apply { + // Always present callback that logs animation beginning and end. + addCallback(tracingListener) + } + ) + } + + @Provides + fun hingeAngleProvider( + config: UnfoldTransitionConfig, + hingeAngleSensorProvider: Provider<HingeSensorAngleProvider> + ): HingeAngleProvider { + return if (config.isHingeAngleEnabled) { + hingeAngleSensorProvider.get() + } else { + EmptyHingeAngleProvider + } + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index a1ed17844e8e..aa93c6290145 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -37,29 +37,29 @@ import java.util.concurrent.Executor * This should **never** be called from sysui, as the object is already provided in that process. */ fun createUnfoldSharedComponent( - context: Context, - config: UnfoldTransitionConfig, - screenStatusProvider: ScreenStatusProvider, - foldProvider: FoldProvider, - activityTypeProvider: CurrentActivityTypeProvider, - sensorManager: SensorManager, - mainHandler: Handler, - mainExecutor: Executor, - backgroundExecutor: Executor, - tracingTagPrefix: String, - windowManager: IWindowManager, + context: Context, + config: UnfoldTransitionConfig, + screenStatusProvider: ScreenStatusProvider, + foldProvider: FoldProvider, + activityTypeProvider: CurrentActivityTypeProvider, + sensorManager: SensorManager, + mainHandler: Handler, + mainExecutor: Executor, + singleThreadBgExecutor: Executor, + tracingTagPrefix: String, + windowManager: IWindowManager, ): UnfoldSharedComponent = - DaggerUnfoldSharedComponent.factory() - .create( - context, - config, - screenStatusProvider, - foldProvider, - activityTypeProvider, - sensorManager, - mainHandler, - mainExecutor, - backgroundExecutor, - tracingTagPrefix, - windowManager, - ) + DaggerUnfoldSharedComponent.factory() + .create( + context, + config, + screenStatusProvider, + foldProvider, + activityTypeProvider, + sensorManager, + mainHandler, + mainExecutor, + singleThreadBgExecutor, + tracingTagPrefix, + windowManager, + ) diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt index 60747954dac3..dcac531485e3 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldSingleThreadBg.kt @@ -18,8 +18,7 @@ import javax.inject.Qualifier /** * Alternative to [UiBackground] qualifier annotation in unfold module. + * * It is needed as we can't depend on SystemUI code in this module. */ -@Qualifier -@Retention(AnnotationRetention.RUNTIME) -annotation class UnfoldBackground +@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldSingleThreadBg diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt index fa59cb4d12be..4622464b204d 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt @@ -24,11 +24,13 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate +import javax.inject.Inject /** Emits animation progress with fixed timing after unfolding */ -internal class FixedTimingTransitionProgressProvider( - private val foldStateProvider: FoldStateProvider -) : UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener { +internal class FixedTimingTransitionProgressProvider +@Inject +constructor(private val foldStateProvider: FoldStateProvider) : + UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener { private val animatorListener = AnimatorListener() private val animator = diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index 074b1e162fed..6ffbe5aa25c0 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -33,9 +33,10 @@ import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener import com.android.systemui.unfold.updates.name +import javax.inject.Inject /** Maps fold updates to unfold transition progress using DynamicAnimation. */ -class PhysicsBasedUnfoldTransitionProgressProvider( +class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( private val foldStateProvider: FoldStateProvider ) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener { diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index 5b458975fa34..97c9ba99f096 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -79,6 +79,7 @@ constructor( screenStatusProvider.addCallback(screenListener) hingeAngleProvider.addCallback(hingeAngleListener) rotationChangeProvider.addCallback(rotationListener) + activityTypeProvider.init() } override fun stop() { @@ -87,6 +88,7 @@ constructor( hingeAngleProvider.removeCallback(hingeAngleListener) hingeAngleProvider.stop() rotationChangeProvider.removeCallback(rotationListener) + activityTypeProvider.uninit() } override fun addCallback(listener: FoldUpdatesListener) { @@ -115,19 +117,17 @@ constructor( } val isClosing = angle < lastHingeAngle - val closingThreshold = getClosingThreshold() - val closingThresholdMet = closingThreshold == null || angle < closingThreshold val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING val screenAvailableEventSent = isUnfoldHandled if (isClosing // hinge angle should be decreasing since last update - && closingThresholdMet // hinge angle is below certain threshold && !closingEventDispatched // we haven't sent closing event already && !isFullyOpened // do not send closing event if we are in fully opened hinge // angle range as closing threshold could overlap this range && screenAvailableEventSent // do not send closing event if we are still in // the process of turning on the inner display + && isClosingThresholdMet(angle) // hinge angle is below certain threshold. ) { notifyFoldUpdate(FOLD_UPDATE_START_CLOSING) } @@ -146,6 +146,11 @@ constructor( outputListeners.forEach { it.onHingeAngleUpdate(angle) } } + private fun isClosingThresholdMet(currentAngle: Float) : Boolean { + val closingThreshold = getClosingThreshold() + return closingThreshold == null || currentAngle < closingThreshold + } + /** * Fold animation should be started only after the threshold returned here. * diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt index 577137ca12f3..89fb12e313ec 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt @@ -20,35 +20,43 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager import android.os.Trace import androidx.core.util.Consumer +import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg import java.util.concurrent.Executor +import javax.inject.Inject -internal class HingeSensorAngleProvider( +internal class HingeSensorAngleProvider +@Inject +constructor( private val sensorManager: SensorManager, - private val executor: Executor -) : - HingeAngleProvider { + @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor +) : HingeAngleProvider { private val sensorListener = HingeAngleSensorListener() private val listeners: MutableList<Consumer<Float>> = arrayListOf() var started = false - override fun start() = executor.execute { - if (started) return@execute - Trace.beginSection("HingeSensorAngleProvider#start") - val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE) - sensorManager.registerListener( - sensorListener, - sensor, - SensorManager.SENSOR_DELAY_FASTEST - ) - Trace.endSection() - started = true + override fun start() { + singleThreadBgExecutor.execute { + if (started) return@execute + Trace.beginSection("HingeSensorAngleProvider#start") + val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE) + sensorManager.registerListener( + sensorListener, + sensor, + SensorManager.SENSOR_DELAY_FASTEST + ) + Trace.endSection() + + started = true + } } - override fun stop() = executor.execute { - if (!started) return@execute - sensorManager.unregisterListener(sensorListener) - started = false + override fun stop() { + singleThreadBgExecutor.execute { + if (!started) return@execute + sensorManager.unregisterListener(sensorListener) + started = false + } } override fun removeCallback(listener: Consumer<Float>) { diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt index d0e6cdc9a3c6..34e7c38c1e59 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt @@ -16,6 +16,11 @@ package com.android.systemui.unfold.util interface CurrentActivityTypeProvider { val isHomeActivity: Boolean? + + /** Starts listening for task updates. */ + fun init() {} + /** Stop listening for task updates. */ + fun uninit() {} } class EmptyCurrentActivityTypeProvider(override val isHomeActivity: Boolean? = null) : diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 4f1efd627e40..425158195940 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3091,7 +3091,7 @@ public class AccountManagerService } } - Intent intent = result.getParcelable(AccountManager.KEY_INTENT); + Intent intent = result.getParcelable(AccountManager.KEY_INTENT, Intent.class); if (intent != null && notifyOnAuthFailure && !customTokens) { /* * Make sure that the supplied intent is owned by the authenticator @@ -3516,8 +3516,7 @@ public class AccountManagerService Bundle.setDefusable(result, true); mNumResults++; Intent intent = null; - if (result != null - && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { + if (result != null) { if (!checkKeyIntent( Binder.getCallingUid(), result)) { @@ -4885,8 +4884,10 @@ public class AccountManagerService EventLog.writeEvent(0x534e4554, "250588548", authUid, ""); return false; } - Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class); + if (intent == null) { + return true; + } // Explicitly set an empty ClipData to ensure that we don't offer to // promote any Uris contained inside for granting purposes if (intent.getClipData() == null) { @@ -4936,8 +4937,12 @@ public class AccountManagerService Bundle simulateBundle = p.readBundle(); p.recycle(); Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class); - return (intent.filterEquals(simulateBundle.getParcelable(AccountManager.KEY_INTENT, - Intent.class))); + Intent simulateIntent = simulateBundle.getParcelable(AccountManager.KEY_INTENT, + Intent.class); + if (intent == null) { + return (simulateIntent == null); + } + return intent.filterEquals(simulateIntent); } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { @@ -5082,8 +5087,7 @@ public class AccountManagerService } } } - if (result != null - && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { + if (result != null) { if (!checkKeyIntent( Binder.getCallingUid(), result)) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index cdcf6c4ea206..662db787ebe2 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -108,7 +108,7 @@ public class PreferencesHelper implements RankingConfig { @VisibleForTesting static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000; @VisibleForTesting - static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000; + static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 6000; private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000; private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index e7221c850da8..fde0b34c7836 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -191,4 +191,12 @@ public interface StatusBarManagerInternal { * @see com.android.internal.statusbar.IStatusBar#enterStageSplitFromRunningApp */ void enterStageSplitFromRunningApp(boolean leftOrTop); + + /** + * Shows the media output switcher dialog. + * + * @param packageName of the session for which the output switcher is shown. + * @see com.android.internal.statusbar.IStatusBar#showMediaOutputSwitcher + */ + void showMediaOutputSwitcher(String packageName); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 45748e6ce76d..ab7292d49c7d 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -714,6 +714,16 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } catch (RemoteException ex) { } } } + + @Override + public void showMediaOutputSwitcher(String packageName) { + if (mBar != null) { + try { + mBar.showMediaOutputSwitcher(packageName); + } catch (RemoteException ex) { + } + } + } }; private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a464112f0492..beaac05d16a6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -609,7 +609,7 @@ class Task extends TaskFragment { */ ActivityRecord mChildPipActivity; - boolean mLastSurfaceShowing = true; + boolean mLastSurfaceShowing; /** * Tracks if a back gesture is in progress. @@ -4197,13 +4197,7 @@ class Task extends TaskFragment { @Override boolean showSurfaceOnCreation() { - if (mCreatedByOrganizer) { - // Tasks created by the organizer are default visible because they can synchronously - // update the leash before new children are added to the task. - return true; - } - // Organized tasks handle their own surface visibility - return !canBeOrganized(); + return false; } @Override diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 693f3a0cf8a0..bd0a814e172a 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -271,22 +271,22 @@ public class DataManager { private ConversationChannel getConversationChannel(String packageName, int userId, String shortcutId, ConversationInfo conversationInfo) { ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId); - return getConversationChannel(shortcutInfo, conversationInfo); + return getConversationChannel( + shortcutInfo, conversationInfo, packageName, userId, shortcutId); } @Nullable private ConversationChannel getConversationChannel(ShortcutInfo shortcutInfo, - ConversationInfo conversationInfo) { + ConversationInfo conversationInfo, String packageName, int userId, String shortcutId) { if (conversationInfo == null || conversationInfo.isDemoted()) { return null; } if (shortcutInfo == null) { - Slog.e(TAG, " Shortcut no longer found"); + Slog.e(TAG, "Shortcut no longer found"); + mInjector.getBackgroundExecutor().execute( + () -> removeConversations(packageName, userId, Set.of(shortcutId))); return null; } - String packageName = shortcutInfo.getPackage(); - String shortcutId = shortcutInfo.getId(); - int userId = shortcutInfo.getUserId(); int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); NotificationChannel parentChannel = mNotificationManagerInternal.getNotificationChannel(packageName, uid, @@ -1130,30 +1130,33 @@ public class DataManager { public void onShortcutsRemoved(@NonNull String packageName, @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { mInjector.getBackgroundExecutor().execute(() -> { - int uid = Process.INVALID_UID; - try { - uid = mContext.getPackageManager().getPackageUidAsUser( - packageName, user.getIdentifier()); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(TAG, "Package not found: " + packageName, e); - } - PackageData packageData = getPackage(packageName, user.getIdentifier()); - Set<String> shortcutIds = new HashSet<>(); + HashSet<String> shortcutIds = new HashSet<>(); for (ShortcutInfo shortcutInfo : shortcuts) { - if (packageData != null) { - if (DEBUG) Log.d(TAG, "Deleting shortcut: " + shortcutInfo.getId()); - packageData.deleteDataForConversation(shortcutInfo.getId()); - } shortcutIds.add(shortcutInfo.getId()); } - if (uid != Process.INVALID_UID) { - mNotificationManagerInternal.onConversationRemoved( - packageName, uid, shortcutIds); - } + removeConversations(packageName, user.getIdentifier(), shortcutIds); }); } } + private void removeConversations( + @NonNull String packageName, @NonNull int userId, @NonNull Set<String> shortcutIds) { + PackageData packageData = getPackage(packageName, userId); + if (packageData != null) { + for (String shortcutId : shortcutIds) { + if (DEBUG) Log.d(TAG, "Deleting shortcut: " + shortcutId); + packageData.deleteDataForConversation(shortcutId); + } + } + try { + int uid = mContext.getPackageManager().getPackageUidAsUser( + packageName, userId); + mNotificationManagerInternal.onConversationRemoved(packageName, uid, shortcutIds); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Package not found when removing conversation: " + packageName, e); + } + } + /** Listener for the notifications and their settings changes. */ private class NotificationListener extends NotificationListenerService { @@ -1349,9 +1352,11 @@ public class DataManager { } private void updateConversationStoreThenNotifyListeners(ConversationStore cs, - ConversationInfo modifiedConv, ShortcutInfo shortcutInfo) { + ConversationInfo modifiedConv, @NonNull ShortcutInfo shortcutInfo) { cs.addOrUpdate(modifiedConv); - ConversationChannel channel = getConversationChannel(shortcutInfo, modifiedConv); + ConversationChannel channel = getConversationChannel( + shortcutInfo, modifiedConv, shortcutInfo.getPackage(), shortcutInfo.getUserId(), + shortcutInfo.getId()); if (channel != null) { notifyConversationsListeners(Arrays.asList(channel)); } diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index b12e6ad563d1..4d177acb99fb 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -687,6 +687,34 @@ public final class DataManagerTest { } @Test + public void testGetConversation_unsyncedShortcut() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_PINNED); + mDataManager.addOrUpdateConversationInfo(shortcut); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNotNull(); + assertThat(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY) + .getConversationStore() + .getConversation(TEST_SHORTCUT_ID)).isNotNull(); + + when(mShortcutServiceInternal.getShortcuts( + anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(), + anyInt(), anyInt(), anyInt(), anyInt())) + .thenReturn(Collections.emptyList()); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNull(); + + // Conversation is removed from store as there is no matching shortcut in ShortcutManager + assertThat(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY) + .getConversationStore() + .getConversation(TEST_SHORTCUT_ID)).isNull(); + verify(mNotificationManagerInternal) + .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, Set.of(TEST_SHORTCUT_ID)); + } + + @Test public void testOnNotificationChannelModified() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java index 47f87d6d75ff..573b3b695a90 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java @@ -102,6 +102,7 @@ public final class NotificationTest { @After public void tearDown() { mNotificationManager.cancelAll(); + mUiDevice.pressHome(); } @Test |