diff options
Diffstat (limited to 'libs')
52 files changed, 863 insertions, 404 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index 822a07c23950..544f0f38f48c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -894,9 +894,7 @@ class DividerPresenter implements View.OnTouchListener { private static boolean isDraggingToFullscreenAllowed( @NonNull DividerAttributes dividerAttributes) { - // TODO(b/293654166) Use DividerAttributes.isDraggingToFullscreenAllowed when extension is - // updated to v7. - return false; + return dividerAttributes.isDraggingToFullscreenAllowed(); } /** diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 1e6824196687..dbcad8aab45b 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -190,6 +190,15 @@ java_library { ], } +java_library { + name: "WindowManager-Shell-shared-desktopMode", + + srcs: [ + "shared/**/desktopmode/*.java", + "shared/**/desktopmode/*.kt", + ], +} + android_library { name: "WindowManager-Shell", srcs: [ diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png Binary files differindex 723c6b8d9c93..e02c89ae07bd 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png Binary files differindex 723c6b8d9c93..e02c89ae07bd 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_open_in_browser.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_open_in_browser.xml new file mode 100644 index 000000000000..7d912a24c443 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_open_in_browser.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal"> + <path android:fillColor="@android:color/black" android:pathData="M160,880Q127,880 103.5,856.5Q80,833 80,800L80,440Q80,407 103.5,383.5Q127,360 160,360L240,360L240,160Q240,127 263.5,103.5Q287,80 320,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,520Q880,553 856.5,576.5Q833,600 800,600L720,600L720,800Q720,833 696.5,856.5Q673,880 640,880L160,880ZM160,800L640,800Q640,800 640,800Q640,800 640,800L640,520L160,520L160,800Q160,800 160,800Q160,800 160,800ZM720,520L800,520Q800,520 800,520Q800,520 800,520L800,240L320,240L320,360L640,360Q673,360 696.5,383.5Q720,407 720,440L720,520Z"/> +</vector> diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml index d5724cc6a420..419d5c0af1a4 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml @@ -135,5 +135,24 @@ android:drawableTint="?androidprv:attr/materialColorOnSurface" style="@style/DesktopModeHandleMenuActionButton"/> </LinearLayout> + + <LinearLayout + android:id="@+id/open_in_browser_pill" + android:layout_width="match_parent" + android:layout_height="@dimen/desktop_mode_handle_menu_open_in_browser_pill_height" + android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin" + android:layout_marginStart="1dp" + android:orientation="vertical" + android:elevation="1dp" + android:background="@drawable/desktop_mode_decor_handle_menu_background"> + + <Button + android:id="@+id/open_in_browser_button" + android:contentDescription="@string/open_in_browser_text" + android:text="@string/open_in_browser_text" + android:drawableStart="@drawable/desktop_mode_ic_handle_menu_open_in_browser" + android:drawableTint="?androidprv:attr/materialColorOnSurface" + style="@style/DesktopModeHandleMenuActionButton"/> + </LinearLayout> </LinearLayout> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index 4b9be47f8023..a4711d4180bc 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -96,7 +96,7 @@ <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن تکضرب بزنید."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه بهطور همزمان استفاده کنید"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامهای دیگر بکشید"</string> - <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"برای جابهجا کردن برنامه، بیرون از آن دوضربه بزنید"</string> + <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"برای جابهجا کردن برنامه، بیرون از آن دو تکضرب بزنید"</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجهام"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"برای اطلاعات بیشتر، گسترده کنید."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"برای نمایش بهتر بازراهاندازی شود؟"</string> @@ -104,7 +104,7 @@ <string name="letterbox_restart_cancel" msgid="1342209132692537805">"لغو کردن"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"بازراهاندازی"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"دوباره نشان داده نشود"</string> - <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"برای جابهجا کردن این برنامه\nدوضربه بزنید"</string> + <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"برای جابهجا کردن این برنامه\nدو تکضرب بزنید"</string> <string name="maximize_button_text" msgid="1650859196290301963">"بزرگ کردن"</string> <string name="minimize_button_text" msgid="271592547935841753">"کوچک کردن"</string> <string name="close_button_text" msgid="2913281996024033299">"بستن"</string> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 74d822ac02c0..7feb772eb10a 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -31,11 +31,11 @@ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string> <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ajouter à la réserve"</string> <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string> - <string name="dock_forced_resizable" msgid="7429086980048964687">"L\'application peut ne pas fonctionner avec l\'écran partagé"</string> - <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"L\'application ne prend pas en charge l\'écran partagé"</string> - <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Cette application ne peut être ouverte que dans une seule fenêtre."</string> - <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string> - <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string> + <string name="dock_forced_resizable" msgid="7429086980048964687">"L\'appli peut ne pas fonctionner avec l\'écran partagé"</string> + <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"L\'appli ne prend pas en charge l\'écran partagé"</string> + <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Cette appli ne peut être ouverte que dans une seule fenêtre."</string> + <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'appli ne fonctionne pas sur un écran secondaire."</string> + <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'appli ne peut pas être lancée sur des écrans secondaires."</string> <string name="accessibility_divider" msgid="6407584574218956849">"Séparateur d\'écran partagé"</string> <string name="divider_title" msgid="1963391955593749442">"Séparateur d\'écran partagé"</string> <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Plein écran à la gauche"</string> @@ -56,7 +56,7 @@ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pour quitter, balayez l\'écran du bas vers le haut, ou touchez n\'importe où sur l\'écran en haut de l\'appli"</string> <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Démarrer le mode Une main"</string> <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Quitter le mode Une main"</string> - <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Paramètres pour les bulles de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Paramètres pour les bulles de l\'appli <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu déroulant"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Replacer sur la pile"</string> <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string> @@ -73,7 +73,7 @@ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Clavarder en utilisant des bulles"</string> <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes (de bulles). Touchez une bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string> <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Paramètres des bulles"</string> - <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toucher Gérer pour désactiver les bulles de cette application"</string> + <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toucher Gérer pour désactiver les bulles de cette appli"</string> <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et les bulles ignorées s\'afficheront ici"</string> @@ -88,29 +88,29 @@ <skip /> <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) --> <skip /> - <string name="restart_button_description" msgid="4564728020654658478">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage"</string> - <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Changer les proportions de cette application dans les paramètres"</string> + <string name="restart_button_description" msgid="4564728020654658478">"Touchez pour redémarrer cette appli afin d\'obtenir un meilleur affichage"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Changer les proportions de cette appli dans les paramètres"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier les proportions"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string> - <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre application pour utiliser l\'écran partagé"</string> - <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Touchez deux fois à côté d\'une application pour la repositionner"</string> + <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string> + <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Touchez deux fois à côté d\'une appli pour la repositionner"</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développer pour en savoir plus."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Redémarrer pour un meilleur affichage?"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redémarrer l\'application pour qu\'elle s\'affiche mieux sur votre écran, mais il se peut que vous perdiez votre progression ou toute modification non enregistrée"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redémarrer l\'appli pour qu\'elle s\'affiche mieux sur votre écran, mais il se peut que vous perdiez votre progression ou toute modification non enregistrée"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annuler"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"Redémarrer"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne plus afficher"</string> - <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toucher deux fois pour\ndéplacer cette application"</string> + <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toucher deux fois pour\ndéplacer cette appli"</string> <string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string> <string name="minimize_button_text" msgid="271592547935841753">"Réduire"</string> <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string> <string name="back_button_text" msgid="1469718707134137085">"Retour"</string> <string name="handle_text" msgid="1766582106752184456">"Identifiant"</string> - <string name="app_icon_text" msgid="2823268023931811747">"Icône de l\'application"</string> + <string name="app_icon_text" msgid="2823268023931811747">"Icône de l\'appli"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string> <string name="desktop_text" msgid="1077633567027630454">"Mode Bureau"</string> <string name="split_screen_text" msgid="1396336058129570886">"Écran divisé"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 1161eb608405..27b79019a57a 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -117,7 +117,7 @@ <string name="more_button_text" msgid="3655388105592893530">"Mer"</string> <string name="float_button_text" msgid="9221657008391364581">"Svevende"</string> <string name="select_text" msgid="5139083974039906583">"Velg"</string> - <string name="screenshot_text" msgid="1477704010087786671">"Skjermdump"</string> + <string name="screenshot_text" msgid="1477704010087786671">"Skjermbilde"</string> <string name="close_text" msgid="4986518933445178928">"Lukk"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string> <string name="expand_menu_text" msgid="3847736164494181168">"Åpne menyen"</string> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 595d34664cfa..d143263b69a5 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -507,8 +507,11 @@ <!-- The height of the handle menu's "More Actions" pill in desktop mode. --> <dimen name="desktop_mode_handle_menu_more_actions_pill_height">52dp</dimen> + <!-- The height of the handle menu's "Open in browser" pill in desktop mode. --> + <dimen name="desktop_mode_handle_menu_open_in_browser_pill_height">52dp</dimen> + <!-- The height of the handle menu in desktop mode. --> - <dimen name="desktop_mode_handle_menu_height">328dp</dimen> + <dimen name="desktop_mode_handle_menu_height">380dp</dimen> <!-- The top margin of the handle menu in desktop mode. --> <dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 47846746b205..4e7cfb638a12 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -280,6 +280,8 @@ <string name="select_text">Select</string> <!-- Accessibility text for the handle menu screenshot button [CHAR LIMIT=NONE] --> <string name="screenshot_text">Screenshot</string> + <!-- Accessibility text for the handle menu open in browser button [CHAR LIMIT=NONE] --> + <string name="open_in_browser_text">Open in browser</string> <!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] --> <string name="close_text">Close</string> <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] --> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt index 59831689b525..f0d80a02243a 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt @@ -37,10 +37,6 @@ enum class DesktopModeFlags( DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true), WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true); - // Local cache for toggle override, which is initialized once on its first access. It needs to be - // refreshed only on reboots as overridden state takes effect on reboots. - private var cachedToggleOverride: ToggleOverride? = null - /** * Determines state of flag based on the actual flag and desktop mode developer option overrides. * @@ -88,11 +84,12 @@ enum class DesktopModeFlags( // Read Setting Global if System Property is not present (just after reboot) // or not valid (user manually changed the value) val overrideFromSettingsGlobal = - Settings.Global.getInt( + convertToToggleOverrideWithFallback( + Settings.Global.getInt( context.contentResolver, Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, - ToggleOverride.OVERRIDE_UNSET.setting) - .convertToToggleOverrideWithFallback(ToggleOverride.OVERRIDE_UNSET) + ToggleOverride.OVERRIDE_UNSET.setting), + ToggleOverride.OVERRIDE_UNSET) // Initialize System Property System.setProperty( SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString()) @@ -101,7 +98,6 @@ enum class DesktopModeFlags( } } - // TODO(b/348193756): Share ToggleOverride enum with Settings 'DesktopModePreferenceController' /** * Override state of desktop mode developer option toggle. * @@ -117,8 +113,6 @@ enum class DesktopModeFlags( OVERRIDE_ON(1) } - private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting } - private fun String?.convertToToggleOverride(): ToggleOverride? { val intValue = this?.toIntOrNull() ?: return null return settingToToggleOverrideMap[intValue] @@ -128,23 +122,33 @@ enum class DesktopModeFlags( } } - private fun Int.convertToToggleOverrideWithFallback( - fallbackOverride: ToggleOverride - ): ToggleOverride { - return settingToToggleOverrideMap[this] - ?: run { - Log.w(TAG, "Unknown toggleOverride int $this") - fallbackOverride - } - } - - private companion object { - const val TAG = "DesktopModeFlags" + companion object { + private const val TAG = "DesktopModeFlags" /** * Key for non-persistent System Property which is used to store desktop windowing developer * option overrides. */ - const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override" + private const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override" + + /** + * Local cache for toggle override, which is initialized once on its first access. It needs to + * be refreshed only on reboots as overridden state takes effect on reboots. + */ + private var cachedToggleOverride: ToggleOverride? = null + + private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting } + + @JvmStatic + fun convertToToggleOverrideWithFallback( + overrideInt: Int, + fallbackOverride: ToggleOverride + ): ToggleOverride { + return settingToToggleOverrideMap[overrideInt] + ?: run { + Log.w(TAG, "Unknown toggleOverride int $overrideInt") + fallbackOverride + } + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index d41600b140c4..f014e559d4b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -699,6 +699,22 @@ public class ShellTaskOrganizer extends TaskOrganizer { } } + /** + * Shows/hides the given task surface. Not for general use as changing the task visibility may + * conflict with other Transitions. This is currently ONLY used to temporarily hide a task + * while a drag is in session. + */ + public void setTaskSurfaceVisibility(int taskId, boolean visible) { + synchronized (mLock) { + final TaskAppearedInfo info = mTasks.get(taskId); + if (info != null) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setVisibility(info.getLeash(), visible); + t.apply(); + } + } + } + private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash, TaskListener oldListener, TaskListener newListener) { if (oldListener == newListener) return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/CreateBubbleShortcutActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/CreateBubbleShortcutActivity.kt index a124f95d7431..c93c11eb2fc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/CreateBubbleShortcutActivity.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/CreateBubbleShortcutActivity.kt @@ -20,10 +20,10 @@ import android.app.Activity import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.Bundle +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.Flags import com.android.wm.shell.R import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES -import com.android.wm.shell.util.KtProtoLog /** Activity to create a shortcut to open bubbles */ class CreateBubbleShortcutActivity : Activity() { @@ -31,7 +31,7 @@ class CreateBubbleShortcutActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (Flags.enableRetrievableBubbles()) { - KtProtoLog.d(WM_SHELL_BUBBLES, "Creating a shortcut for bubbles") + ProtoLog.d(WM_SHELL_BUBBLES, "Creating a shortcut for bubbles") createShortcut() } finish() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/ShowBubblesActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/ShowBubblesActivity.kt index ae7940ca1b65..e578e9e76979 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/ShowBubblesActivity.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/ShowBubblesActivity.kt @@ -21,9 +21,9 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Bundle +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.Flags import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES -import com.android.wm.shell.util.KtProtoLog /** Activity that sends a broadcast to open bubbles */ class ShowBubblesActivity : Activity() { @@ -37,7 +37,7 @@ class ShowBubblesActivity : Activity() { // Set the package as the receiver is not exported `package` = packageName } - KtProtoLog.v(WM_SHELL_BUBBLES, "Sending broadcast to show bubbles") + ProtoLog.v(WM_SHELL_BUBBLES, "Sending broadcast to show bubbles") sendBroadcast(intent) } finish() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt index 81592c35e4ac..e92b0b59d2ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt @@ -17,8 +17,8 @@ package com.android.wm.shell.common import android.window.WindowContainerToken import android.window.WindowContainerTransaction +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG -import com.android.wm.shell.util.KtProtoLog /** * Controller to manage behavior of activities launched with @@ -30,7 +30,7 @@ class LaunchAdjacentController(private val syncQueue: SyncTransactionQueue) { var launchAdjacentEnabled: Boolean = true set(value) { if (field != value) { - KtProtoLog.d(WM_SHELL_TASK_ORG, "set launch adjacent flag root enabled=%b", value) + ProtoLog.d(WM_SHELL_TASK_ORG, "set launch adjacent flag root enabled=%b", value) field = value container?.let { c -> if (value) { @@ -52,7 +52,7 @@ class LaunchAdjacentController(private val syncQueue: SyncTransactionQueue) { * @see WindowContainerTransaction.setLaunchAdjacentFlagRoot */ fun setLaunchAdjacentRoot(container: WindowContainerToken) { - KtProtoLog.d(WM_SHELL_TASK_ORG, "set new launch adjacent flag root container") + ProtoLog.d(WM_SHELL_TASK_ORG, "set new launch adjacent flag root container") this.container = container if (launchAdjacentEnabled) { enableContainer(container) @@ -67,7 +67,7 @@ class LaunchAdjacentController(private val syncQueue: SyncTransactionQueue) { * @see WindowContainerTransaction.clearLaunchAdjacentFlagRoot */ fun clearLaunchAdjacentRoot() { - KtProtoLog.d(WM_SHELL_TASK_ORG, "clear launch adjacent flag root container") + ProtoLog.d(WM_SHELL_TASK_ORG, "clear launch adjacent flag root container") container?.let { disableContainer(it) container = null @@ -75,14 +75,14 @@ class LaunchAdjacentController(private val syncQueue: SyncTransactionQueue) { } private fun enableContainer(container: WindowContainerToken) { - KtProtoLog.v(WM_SHELL_TASK_ORG, "enable launch adjacent flag root container") + ProtoLog.v(WM_SHELL_TASK_ORG, "enable launch adjacent flag root container") val wct = WindowContainerTransaction() wct.setLaunchAdjacentFlagRoot(container) syncQueue.queue(wct) } private fun disableContainer(container: WindowContainerToken) { - KtProtoLog.v(WM_SHELL_TASK_ORG, "disable launch adjacent flag root container") + ProtoLog.v(WM_SHELL_TASK_ORG, "disable launch adjacent flag root container") val wct = WindowContainerTransaction() wct.clearLaunchAdjacentFlagRoot(container) syncQueue.queue(wct) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt index 9e8dfb5f0c6f..a6be64070ac1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt @@ -23,9 +23,9 @@ import android.content.pm.PackageManager import android.os.UserHandle import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI import com.android.internal.annotations.VisibleForTesting +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL -import com.android.wm.shell.util.KtProtoLog import java.util.Arrays /** @@ -52,7 +52,7 @@ class MultiInstanceHelper @JvmOverloads constructor( val packageName = componentName.packageName for (pkg in staticAppsSupportingMultiInstance) { if (pkg == packageName) { - KtProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance", + ProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance", packageName) return true } @@ -70,10 +70,10 @@ class MultiInstanceHelper @JvmOverloads constructor( // If the above call doesn't throw a NameNotFoundException, then the activity property // should override the application property value if (activityProp.isBoolean) { - KtProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName) + ProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName) return activityProp.boolean } else { - KtProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d", + ProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d", PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, activityProp.type) } } catch (nnfe: PackageManager.NameNotFoundException) { @@ -85,10 +85,10 @@ class MultiInstanceHelper @JvmOverloads constructor( val appProp = packageManager.getProperty( PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName) if (appProp.isBoolean) { - KtProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName) + ProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName) return appProp.boolean } else { - KtProtoLog.w(WM_SHELL, + ProtoLog.w(WM_SHELL, "Warning: property=%s for application=%s has non-bool type=%d", PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.type) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 3ad60e7031e5..1bc179551825 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -492,6 +492,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { return mHideHandle; } + /** Returns true if the divider is currently being physically controlled by the user. */ + boolean isMoving() { + return mMoving; + } + private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDoubleTap(MotionEvent e) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index bdef4f4b094c..51f9de8305f8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -652,9 +652,18 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange .ofInt(from, to) .setDuration(duration); mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + + // If the divider is being physically controlled by the user, we use a cool parallax effect + // on the task windows. So if this "snap" animation is an extension of a user-controlled + // movement, we pass in true here to continue the parallax effect smoothly. + boolean isBeingMovedByUser = mSplitWindowManager.getDividerView() != null + && mSplitWindowManager.getDividerView().isMoving(); + mDividerFlingAnimator.addUpdateListener( animation -> updateDividerBounds( - (int) animation.getAnimatedValue(), false /* shouldUseParallaxEffect */) + (int) animation.getAnimatedValue(), + isBeingMovedByUser /* shouldUseParallaxEffect */ + ) ); mDividerFlingAnimator.addListener(new AnimatorListenerAdapter() { @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 5d121c23c6e1..46c1a43f9efe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -37,7 +37,6 @@ import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.SurfaceSession; -import android.view.View; import android.view.WindowManager; import android.view.WindowlessWindowManager; @@ -192,7 +191,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { mDividerView.setInteractive(interactive, hideHandle, from); } - View getDividerView() { + DividerView getDividerView() { return mDividerView; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index aa499d9ee8a0..eeceaa943af2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -604,11 +604,12 @@ public abstract class WMShellModule { Context context, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, Transitions transitions, + ShellTaskOrganizer shellTaskOrganizer, ShellInit shellInit ) { return desktopModeTaskRepository.flatMap(repository -> Optional.of(new DesktopTasksTransitionObserver( - context, repository, transitions, shellInit)) + context, repository, transitions, shellTaskOrganizer, shellInit)) ); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index fbc11c19a5a2..400882a8da9a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -16,9 +16,9 @@ package com.android.wm.shell.desktopmode +import com.android.internal.protolog.ProtoLog import com.android.internal.util.FrameworkStatsLog import com.android.wm.shell.protolog.ShellProtoLogGroup -import com.android.wm.shell.util.KtProtoLog /** Event logger for logging desktop mode session events */ class DesktopModeEventLogger { @@ -27,7 +27,7 @@ class DesktopModeEventLogger { * entering desktop mode */ fun logSessionEnter(sessionId: Int, enterReason: EnterReason) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session enter, session: %s reason: %s", sessionId, @@ -47,7 +47,7 @@ class DesktopModeEventLogger { * exiting desktop mode */ fun logSessionExit(sessionId: Int, exitReason: ExitReason) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session exit, session: %s reason: %s", sessionId, @@ -67,7 +67,7 @@ class DesktopModeEventLogger { * session id [sessionId] */ fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task added, session: %s taskId: %s", sessionId, @@ -99,7 +99,7 @@ class DesktopModeEventLogger { * session id [sessionId] */ fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task remove, session: %s taskId: %s", sessionId, @@ -131,7 +131,7 @@ class DesktopModeEventLogger { * having session id [sessionId] */ fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task info changed, session: %s taskId: %s", sessionId, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index 275f725dc054..066b5ad39d0f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -50,7 +50,6 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions -import com.android.wm.shell.util.KtProtoLog /** * A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log @@ -106,7 +105,7 @@ class DesktopModeLoggerTransitionObserver( ) { // this was a new recents animation if (info.isExitToRecentsTransition() && tasksSavedForRecents.isEmpty()) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Recents animation running, saving tasks for later" ) @@ -132,7 +131,7 @@ class DesktopModeLoggerTransitionObserver( info.flags == 0 && tasksSavedForRecents.isNotEmpty() ) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Canceled recents animation, restoring tasks" ) @@ -202,7 +201,7 @@ class DesktopModeLoggerTransitionObserver( } } - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: taskInfo map after processing changes %s", postTransitionFreeformTasks.size() 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 df79b1575894..ca0586418041 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 @@ -26,8 +26,8 @@ import android.window.WindowContainerToken import androidx.core.util.forEach import androidx.core.util.keyIterator import androidx.core.util.valueIterator +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE -import com.android.wm.shell.util.KtProtoLog import java.io.PrintWriter import java.util.concurrent.Executor import java.util.function.Consumer @@ -142,7 +142,7 @@ class DesktopModeTaskRepository { val added = displayData.getOrCreate(displayId).activeTasks.add(taskId) if (added) { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: add active task=%d displayId=%d", taskId, @@ -167,7 +167,7 @@ class DesktopModeTaskRepository { } } if (result) { - KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove active task=%d", taskId) + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove active task=%d", taskId) } return result } @@ -180,7 +180,7 @@ class DesktopModeTaskRepository { fun addClosingTask(displayId: Int, taskId: Int): Boolean { val added = displayData.getOrCreate(displayId).closingTasks.add(taskId) if (added) { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: added closing task=%d displayId=%d", taskId, @@ -203,7 +203,7 @@ class DesktopModeTaskRepository { } } if (removed) { - KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove closing task=%d", taskId) + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove closing task=%d", taskId) } return removed } @@ -316,14 +316,14 @@ class DesktopModeTaskRepository { // Check if count changed if (prevCount != newCount) { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: update task visibility taskId=%d visible=%b displayId=%d", taskId, visible, displayId ) - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: visibleTaskCount has changed from %d to %d", prevCount, @@ -341,7 +341,7 @@ class DesktopModeTaskRepository { /** Get number of tasks that are marked as visible on given [displayId] */ fun getVisibleTaskCount(displayId: Int): Int { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: visibleTaskCount= %d", displayData[displayId]?.visibleTasks?.size ?: 0 @@ -353,7 +353,7 @@ class DesktopModeTaskRepository { // TODO(b/342417921): Identify if there is additional checks needed to move tasks for // multi-display scenarios. fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: add or move task to top: display=%d, taskId=%d", displayId, @@ -365,7 +365,7 @@ class DesktopModeTaskRepository { /** Mark a Task as minimized. */ fun minimizeTask(displayId: Int, taskId: Int) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopModeTaskRepository: minimize Task: display=%d, task=%d", displayId, @@ -376,7 +376,7 @@ class DesktopModeTaskRepository { /** Mark a Task as non-minimized. */ fun unminimizeTask(displayId: Int, taskId: Int) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopModeTaskRepository: unminimize Task: display=%d, task=%d", displayId, @@ -387,7 +387,7 @@ class DesktopModeTaskRepository { /** Remove the task from the ordered list. */ fun removeFreeformTask(displayId: Int, taskId: Int) { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove freeform task from ordered list: display=%d, taskId=%d", displayId, @@ -395,7 +395,7 @@ class DesktopModeTaskRepository { ) displayData[displayId]?.freeformTasksInZOrder?.remove(taskId) boundsBeforeMaximizeByTaskId.remove(taskId) - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remaining freeform tasks: %s", displayData[displayId]?.freeformTasksInZOrder?.toDumpString() ?: "" 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 985901d5ac75..18157d6255e3 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 @@ -50,6 +50,7 @@ import android.window.WindowContainerTransaction import androidx.annotation.BinderThread import com.android.internal.annotations.VisibleForTesting import com.android.internal.policy.ScreenDecorationsUtils +import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer @@ -88,7 +89,6 @@ import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions -import com.android.wm.shell.util.KtProtoLog import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener @@ -186,7 +186,7 @@ class DesktopTasksController( } private fun onInit() { - KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController") + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController") shellCommandHandler.addDumpCallback(this::dump, this) shellCommandHandler.addCommandCallback("desktopmode", desktopModeShellCommandHandler, this) shellController.addExternalInterface( @@ -200,7 +200,7 @@ class DesktopTasksController( recentsTransitionHandler.addTransitionStateListener( object : RecentsTransitionStateListener { override fun onAnimationStateChanged(running: Boolean) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: recents animation state changed running=%b", running @@ -231,7 +231,7 @@ class DesktopTasksController( /** Show all tasks, that are part of the desktop, on top of launcher */ fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) { - KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps") + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps") val wct = WindowContainerTransaction() bringDesktopAppsToFront(displayId, wct) @@ -282,7 +282,7 @@ class DesktopTasksController( moveToDesktop(allFocusedTasks[0].taskId, transitionSource = transitionSource) } else -> { - KtProtoLog.w( + ProtoLog.w( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, expected less " + "than 3 focused tasks but found %d", @@ -312,7 +312,7 @@ class DesktopTasksController( transitionSource: DesktopModeTransitionSource, ): Boolean { recentTasksController?.findTaskInBackground(taskId)?.let { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: moveToDesktopFromNonRunningTask taskId=%d", taskId @@ -346,14 +346,14 @@ class DesktopTasksController( ) { if (Flags.enableDesktopWindowingModalsPolicy() && isTopActivityExemptFromDesktopWindowing(context, task)) { - KtProtoLog.w( + ProtoLog.w( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, " + "ineligible top activity found." ) return } - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: moveToDesktop taskId=%d", task.taskId @@ -380,7 +380,7 @@ class DesktopTasksController( taskInfo: RunningTaskInfo, dragToDesktopValueAnimator: MoveToDesktopAnimator, ) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: startDragToDesktop taskId=%d", taskInfo.taskId @@ -396,7 +396,7 @@ class DesktopTasksController( * [startDragToDesktop]. */ private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: finalizeDragToDesktop taskId=%d", taskInfo.taskId @@ -440,7 +440,7 @@ class DesktopTasksController( } if (!desktopModeTaskRepository.addClosingTask(displayId, taskId)) { // Could happen if the task hasn't been removed from closing list after it disappeared - KtProtoLog.w( + ProtoLog.w( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: the task with taskId=%d is already closing!", taskId @@ -464,7 +464,7 @@ class DesktopTasksController( /** Move a desktop app to split screen. */ fun moveToSplit(task: RunningTaskInfo) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: moveToSplit taskId=%d", task.taskId @@ -497,7 +497,7 @@ class DesktopTasksController( * [startDragToDesktop]. */ fun cancelDragToDesktop(task: RunningTaskInfo) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: cancelDragToDesktop taskId=%d", task.taskId @@ -512,7 +512,7 @@ class DesktopTasksController( position: Point, transitionSource: DesktopModeTransitionSource ) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: moveToFullscreen with animation taskId=%d", task.taskId @@ -540,7 +540,7 @@ class DesktopTasksController( /** Move a task to the front */ fun moveTaskToFront(taskInfo: RunningTaskInfo) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: moveTaskToFront taskId=%d", taskInfo.taskId @@ -571,10 +571,10 @@ class DesktopTasksController( fun moveToNextDisplay(taskId: Int) { val task = shellTaskOrganizer.getRunningTaskInfo(taskId) if (task == null) { - KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d not found", taskId) + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d not found", taskId) return } - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d taskDisplayId=%d", taskId, @@ -589,7 +589,7 @@ class DesktopTasksController( newDisplayId = displayIds.firstOrNull { displayId -> displayId < task.displayId } } if (newDisplayId == null) { - KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: next display not found") + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: next display not found") return } moveToDisplay(task, newDisplayId) @@ -601,7 +601,7 @@ class DesktopTasksController( * No-op if task is already on that display per [RunningTaskInfo.displayId]. */ private fun moveToDisplay(task: RunningTaskInfo, displayId: Int) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "moveToDisplay: taskId=%d displayId=%d", task.taskId, @@ -609,13 +609,13 @@ class DesktopTasksController( ) if (task.displayId == displayId) { - KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "moveToDisplay: task already on display") + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "moveToDisplay: task already on display") return } val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId) if (displayAreaInfo == null) { - KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToDisplay: display not found") + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToDisplay: display not found") return } @@ -770,7 +770,7 @@ class DesktopTasksController( wct: WindowContainerTransaction, newTaskIdInFront: Int? = null ): RunningTaskInfo? { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bringDesktopAppsToFront, newTaskIdInFront=%s", newTaskIdInFront ?: "null" @@ -815,7 +815,7 @@ class DesktopTasksController( } private fun addWallpaperActivity(wct: WindowContainerTransaction) { - KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper") + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper") val intent = Intent(context, DesktopWallpaperActivity::class.java) val options = ActivityOptions.makeBasic().apply { @@ -835,7 +835,7 @@ class DesktopTasksController( private fun removeWallpaperActivity(wct: WindowContainerTransaction) { desktopModeTaskRepository.wallpaperActivityToken?.let { token -> - KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: removeWallpaper") + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: removeWallpaper") wct.removeTask(token) } } @@ -873,7 +873,7 @@ class DesktopTasksController( transition: IBinder, request: TransitionRequestInfo ): WindowContainerTransaction? { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleRequest request=%s", request @@ -915,7 +915,7 @@ class DesktopTasksController( } if (!shouldHandleRequest) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: skipping handleRequest reason=%s", reason @@ -939,7 +939,7 @@ class DesktopTasksController( } } } - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleRequest result=%s", result ?: "null" @@ -977,15 +977,15 @@ class DesktopTasksController( task: RunningTaskInfo, transition: IBinder ): WindowContainerTransaction? { - KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch") + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch") if (keyguardManager.isKeyguardLocked) { // Do NOT handle freeform task launch when locked. // It will be launched in fullscreen windowing mode (Details: b/160925539) - KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: skip keyguard is locked") + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: skip keyguard is locked") return null } if (!desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bring desktop tasks to front on transition" + " taskId=%d", @@ -1014,9 +1014,9 @@ class DesktopTasksController( task: RunningTaskInfo, transition: IBinder ): WindowContainerTransaction? { - KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch") + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch") if (desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) { - KtProtoLog.d( + ProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: switch fullscreen task to freeform on transition" + " taskId=%d", @@ -1059,7 +1059,7 @@ class DesktopTasksController( } if (!desktopModeTaskRepository.addClosingTask(task.displayId, task.taskId)) { // Could happen if the task hasn't been removed from closing list after it disappeared - KtProtoLog.w( + ProtoLog.w( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: the task with taskId=%d is already closing!", task.taskId @@ -1398,7 +1398,7 @@ class DesktopTasksController( if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent)) { // TODO(b/320797628): Should only return early if there is an existing running task, and // notify the user as well. But for now, just ignore the drop. - KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "Dropped intent does not support multi-instance") + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "Dropped intent does not support multi-instance") return false } @@ -1489,7 +1489,7 @@ class DesktopTasksController( private val listener: VisibleTasksListener = object : VisibleTasksListener { override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: onVisibilityChanged display=%d visible=%d", displayId, @@ -1534,11 +1534,11 @@ class DesktopTasksController( } override fun stashDesktopApps(displayId: Int) { - KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: stashDesktopApps is deprecated") + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: stashDesktopApps is deprecated") } override fun hideStashedDesktopApps(displayId: Int) { - KtProtoLog.w( + ProtoLog.w( WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: hideStashedDesktopApps is deprecated" ) @@ -1565,7 +1565,7 @@ class DesktopTasksController( } override fun setTaskListener(listener: IDesktopTaskListener?) { - KtProtoLog.v( + ProtoLog.v( WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: set task listener=%s", listener ?: "null" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt index 3f5bd1a4f5c7..534cc22ada47 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt @@ -23,12 +23,12 @@ import android.view.WindowManager.TRANSIT_TO_BACK import android.window.TransitionInfo import android.window.WindowContainerTransaction import androidx.annotation.VisibleForTesting +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.protolog.ShellProtoLogGroup import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionObserver -import com.android.wm.shell.util.KtProtoLog /** * Limits the number of tasks shown in Desktop Mode. @@ -71,7 +71,7 @@ class DesktopTasksLimiter ( if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return if (!isTaskReorderedToBackOrInvisible(info, taskToMinimize)) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: task %d is not reordered to back nor invis", taskToMinimize.taskId) @@ -109,7 +109,7 @@ class DesktopTasksLimiter ( } override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: transition %s finished", transition) mPendingTransitionTokensAndTasks.remove(transition) @@ -133,7 +133,7 @@ class DesktopTasksLimiter ( if (remainingMinimizedTasks.isEmpty()) { return } - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: removing leftover minimized tasks: $remainingMinimizedTasks") remainingMinimizedTasks.forEach { taskIdToRemove -> @@ -150,7 +150,7 @@ class DesktopTasksLimiter ( * finished so we don't minimize the task if the transition fails. */ private fun markTaskMinimized(displayId: Int, taskId: Int) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: marking %d as minimized", taskId) taskRepository.minimizeTask(displayId, taskId) @@ -169,7 +169,7 @@ class DesktopTasksLimiter ( wct: WindowContainerTransaction, newFrontTaskInfo: RunningTaskInfo, ): RunningTaskInfo? { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: addMinimizeBackTaskChangesIfNeeded, newFrontTask=%d", newFrontTaskInfo.taskId) @@ -217,7 +217,7 @@ class DesktopTasksLimiter ( visibleFreeformTaskIdsOrderedFrontToBack: List<Int> ): RunningTaskInfo? { if (visibleFreeformTaskIdsOrderedFrontToBack.size <= getMaxTaskLimit()) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: no need to minimize; tasks below limit") // No need to minimize anything @@ -227,7 +227,7 @@ class DesktopTasksLimiter ( shellTaskOrganizer.getRunningTaskInfo( visibleFreeformTaskIdsOrderedFrontToBack.last()) if (taskToMinimize == null) { - KtProtoLog.e( + ProtoLog.e( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: taskToMinimize == null") return null diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt index f01f6450ae30..246fd9281975 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt @@ -21,35 +21,38 @@ import android.os.IBinder import android.view.SurfaceControl import android.view.WindowManager import android.window.TransitionInfo +import android.window.WindowContainerTransaction +import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity +import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions -import com.android.wm.shell.util.KtProtoLog /** - * A [Transitions.TransitionObserver] that observes shell transitions and updates - * the [DesktopModeTaskRepository] state TODO: b/332682201 - * This observes transitions related to desktop mode - * and other transitions that originate both within and outside shell. + * A [Transitions.TransitionObserver] that observes shell transitions and updates the + * [DesktopModeTaskRepository] state TODO: b/332682201 This observes transitions related to desktop + * mode and other transitions that originate both within and outside shell. */ class DesktopTasksTransitionObserver( context: Context, private val desktopModeTaskRepository: DesktopModeTaskRepository, private val transitions: Transitions, + private val shellTaskOrganizer: ShellTaskOrganizer, shellInit: ShellInit ) : Transitions.TransitionObserver { init { - if (Transitions.ENABLE_SHELL_TRANSITIONS && - DesktopModeStatus.canEnterDesktopMode(context)) { + if ( + Transitions.ENABLE_SHELL_TRANSITIONS && DesktopModeStatus.canEnterDesktopMode(context) + ) { shellInit.addInitCallback(::onInit, this) } } fun onInit() { - KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTasksTransitionObserver: onInit") + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTasksTransitionObserver: onInit") transitions.registerObserver(this) } @@ -83,8 +86,16 @@ class DesktopTasksTransitionObserver( change.taskInfo?.let { taskInfo -> if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) { when (change.mode) { - WindowManager.TRANSIT_OPEN -> + WindowManager.TRANSIT_OPEN -> { desktopModeTaskRepository.wallpaperActivityToken = taskInfo.token + // After the task for the wallpaper is created, set it non-trimmable. + // This is important to prevent recents from trimming and removing the + // task. + shellTaskOrganizer.applyTransaction( + WindowContainerTransaction() + .setTaskTrimmableFromRecents(taskInfo.token, false) + ) + } WindowManager.TRANSIT_CLOSE -> desktopModeTaskRepository.wallpaperActivityToken = null else -> {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt index c4a4474689fa..1c2415c236ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt @@ -21,8 +21,8 @@ import android.app.ActivityManager import android.content.ComponentName import android.os.Bundle import android.view.WindowManager +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE -import com.android.wm.shell.util.KtProtoLog /** * A transparent activity used in the desktop mode to show the wallpaper under the freeform windows. @@ -36,7 +36,7 @@ import com.android.wm.shell.util.KtProtoLog class DesktopWallpaperActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { - KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopWallpaperActivity: onCreate") + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopWallpaperActivity: onCreate") super.onCreate(savedInstanceState) window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt index d99b724c936f..ddee8fac8f44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt @@ -29,6 +29,7 @@ import android.window.TransitionInfo.Change import android.window.TransitionRequestInfo import android.window.WindowContainerToken import android.window.WindowContainerTransaction +import com.android.internal.protolog.ProtoLog import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT @@ -42,7 +43,6 @@ import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_D import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP import com.android.wm.shell.transition.Transitions.TransitionHandler -import com.android.wm.shell.util.KtProtoLog import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import com.android.wm.shell.windowdecor.MoveToDesktopAnimator.Companion.DRAG_FREEFORM_SCALE import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener @@ -114,7 +114,7 @@ class DragToDesktopTransitionHandler( dragToDesktopAnimator: MoveToDesktopAnimator, ) { if (inProgress) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DragToDesktop: Drag to desktop transition already in progress." ) @@ -599,7 +599,7 @@ class DragToDesktopTransitionHandler( ) { val state = transitionState ?: return if (aborted && state.startTransitionToken == transition) { - KtProtoLog.v( + ProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DragToDesktop: onTransitionConsumed() start transition aborted" ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md index b1cbe8d98397..3572d161f5b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md @@ -97,7 +97,7 @@ adb reboot adb logcat -s "SurfaceControlRegistry" ``` -## Tracing activity starts in the app process +## Tracing activity starts & finishes in the app process It's sometimes useful to know when to see a stack trace of when an activity starts in the app code (ie. if you are repro'ing a bug related to activity starts). You can enable this system property to @@ -113,6 +113,19 @@ adb shell setprop persist.wm.debug.start_activity \"\" adb reboot ``` +Likewise, to trace where a finish() call may be made in the app process, you can enable this system +property: +```shell +# Enabling +adb shell setprop persist.wm.debug.finish_activity true +adb reboot +adb logcat -s "Instrumentation" + +# Disabling +adb shell setprop persist.wm.debug.finish_activity \"\" +adb reboot +``` + ## Dumps Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index b3c3a3dcf272..e0b08668b1d4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -52,6 +52,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.BinderThread; @@ -353,6 +354,12 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll pd.dragSession.initialize(); pd.activeDragCount++; pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession)); + if (pd.dragSession.hideDragSourceTaskId != -1) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Hiding task surface: taskId=%d", pd.dragSession.hideDragSourceTaskId); + mShellTaskOrganizer.setTaskSurfaceVisibility( + pd.dragSession.hideDragSourceTaskId, false /* visible */); + } setDropTargetWindowVisibility(pd, View.VISIBLE); notifyListeners(l -> { l.onDragStarted(); @@ -382,6 +389,13 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll if (pd.dragLayout.hasDropped()) { mLogger.logDrop(); } else { + if (pd.dragSession.hideDragSourceTaskId != -1) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Re-showing task surface: taskId=%d", + pd.dragSession.hideDragSourceTaskId); + mShellTaskOrganizer.setTaskSurfaceVisibility( + pd.dragSession.hideDragSourceTaskId, true /* visible */); + } pd.activeDragCount--; pd.dragLayout.hide(event, () -> { if (pd.activeDragCount == 0) { @@ -435,7 +449,16 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll private boolean handleDrop(DragEvent event, PerDisplay pd) { final SurfaceControl dragSurface = event.getDragSurface(); pd.activeDragCount--; - return pd.dragLayout.drop(event, dragSurface, () -> { + // Find the token of the task to hide as a part of entering split + WindowContainerToken hideTaskToken = null; + if (pd.dragSession.hideDragSourceTaskId != -1) { + ActivityManager.RunningTaskInfo info = mShellTaskOrganizer.getRunningTaskInfo( + pd.dragSession.hideDragSourceTaskId); + if (info != null) { + hideTaskToken = info.token; + } + } + return pd.dragLayout.drop(event, dragSurface, hideTaskToken, () -> { if (pd.activeDragCount == 0) { // Hide the window if another drag hasn't been started while animating the drop setDropTargetWindowVisibility(pd, View.INVISIBLE); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 9c7476d1a1b0..95fe8b6f1f4e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -59,6 +59,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Slog; +import android.window.WindowContainerToken; import androidx.annotation.IntDef; import androidx.annotation.NonNull; @@ -234,8 +235,13 @@ public class DragAndDropPolicy { return null; } + /** + * Handles the drop on a given {@param target}. If a {@param hideTaskToken} is set, then the + * handling of the drop will attempt to hide the given task as a part of the same window + * container transaction if possible. + */ @VisibleForTesting - void handleDrop(Target target) { + void handleDrop(Target target, @Nullable WindowContainerToken hideTaskToken) { if (target == null || !mTargets.contains(target)) { return; } @@ -254,16 +260,17 @@ public class DragAndDropPolicy { ? mFullscreenStarter : mSplitscreenStarter; if (mSession.appData != null) { - launchApp(mSession, starter, position); + launchApp(mSession, starter, position, hideTaskToken); } else { - launchIntent(mSession, starter, position); + launchIntent(mSession, starter, position, hideTaskToken); } } /** * Launches an app provided by SysUI. */ - private void launchApp(DragSession session, Starter starter, @SplitPosition int position) { + private void launchApp(DragSession session, Starter starter, @SplitPosition int position, + @Nullable WindowContainerToken hideTaskToken) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d", position); final ClipDescription description = session.getClipDescription(); @@ -283,8 +290,12 @@ public class DragAndDropPolicy { if (isTask) { final int taskId = session.appData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID); - starter.startTask(taskId, position, opts); + starter.startTask(taskId, position, opts, hideTaskToken); } else if (isShortcut) { + if (hideTaskToken != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Can not hide task token with starting shortcut"); + } final String packageName = session.appData.getStringExtra(EXTRA_PACKAGE_NAME); final String id = session.appData.getStringExtra(EXTRA_SHORTCUT_ID); starter.startShortcut(packageName, id, position, opts, user); @@ -297,14 +308,15 @@ public class DragAndDropPolicy { } } starter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */, - position, opts); + position, opts, hideTaskToken); } } /** * Launches an intent sender provided by an application. */ - private void launchIntent(DragSession session, Starter starter, @SplitPosition int position) { + private void launchIntent(DragSession session, Starter starter, @SplitPosition int position, + @Nullable WindowContainerToken hideTaskToken) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d", position); final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic(); @@ -319,18 +331,20 @@ public class DragAndDropPolicy { final Bundle opts = baseActivityOpts.toBundle(); starter.startIntent(session.launchableIntent, session.launchableIntent.getCreatorUserHandle().getIdentifier(), - null /* fillIntent */, position, opts); + null /* fillIntent */, position, opts, hideTaskToken); } /** * Interface for actually committing the task launches. */ public interface Starter { - void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options); + void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken); void startShortcut(String packageName, String shortcutId, @SplitPosition int position, @Nullable Bundle options, UserHandle user); void startIntent(PendingIntent intent, int userId, Intent fillInIntent, - @SplitPosition int position, @Nullable Bundle options); + @SplitPosition int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken); void enterSplitScreen(int taskId, boolean leftOrTop); /** @@ -352,7 +366,12 @@ public class DragAndDropPolicy { } @Override - public void startTask(int taskId, int position, @Nullable Bundle options) { + public void startTask(int taskId, int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken) { + if (hideTaskToken != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Default starter does not support hide task token"); + } try { ActivityTaskManager.getService().startActivityFromRecents(taskId, options); } catch (RemoteException e) { @@ -375,7 +394,12 @@ public class DragAndDropPolicy { @Override public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent, - int position, @Nullable Bundle options) { + int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken) { + if (hideTaskToken != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Default starter does not support hide task token"); + } try { intent.send(mContext, 0, fillInIntent, null, null, null, options); } catch (PendingIntent.CanceledException e) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 910175ef3023..f0514e3e49f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -51,8 +51,10 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowInsets.Type; import android.widget.LinearLayout; +import android.window.WindowContainerToken; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.logging.InstanceId; import com.android.internal.protolog.ProtoLog; @@ -483,13 +485,13 @@ public class DragLayout extends LinearLayout /** * Handles the drop onto a target and animates out the visible drop targets. */ - public boolean drop(DragEvent event, SurfaceControl dragSurface, - Runnable dropCompleteCallback) { + public boolean drop(DragEvent event, @NonNull SurfaceControl dragSurface, + @Nullable WindowContainerToken hideTaskToken, Runnable dropCompleteCallback) { final boolean handledDrop = mCurrentTarget != null; mHasDropped = true; // Process the drop - mPolicy.handleDrop(mCurrentTarget); + mPolicy.handleDrop(mCurrentTarget, hideTaskToken); // Start animating the drop UI out with the drag surface hide(event, dropCompleteCallback); @@ -499,7 +501,7 @@ public class DragLayout extends LinearLayout return handledDrop; } - private void hideDragSurface(SurfaceControl dragSurface) { + private void hideDragSurface(@NonNull SurfaceControl dragSurface) { final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); final ValueAnimator dragSurfaceAnimator = ValueAnimator.ofFloat(0f, 1f); // Currently the splash icon animation runs with the default ValueAnimator duration of diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java index 3bedef21184e..dcbdfa349687 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java @@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -27,6 +28,7 @@ import android.content.ClipData; import android.content.ClipDescription; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.os.PersistableBundle; import androidx.annotation.Nullable; @@ -63,6 +65,7 @@ public class DragSession { @WindowConfiguration.ActivityType int runningTaskActType = ACTIVITY_TYPE_STANDARD; boolean dragItemSupportsSplitscreen; + int hideDragSourceTaskId = -1; DragSession(ActivityTaskManager activityTaskManager, DisplayLayout dispLayout, ClipData data, int dragFlags) { @@ -70,6 +73,11 @@ public class DragSession { mInitialDragData = data; mInitialDragFlags = dragFlags; displayLayout = dispLayout; + hideDragSourceTaskId = data.getDescription().getExtras() != null + ? data.getDescription().getExtras().getInt(EXTRA_HIDE_DRAG_SOURCE_TASK_ID, -1) + : -1; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Extracting drag source taskId: taskId=%d", hideDragSourceTaskId); } /** @@ -84,16 +92,27 @@ public class DragSession { * Updates the running task for this drag session. */ void updateRunningTask() { + final boolean hideDragSourceTask = hideDragSourceTaskId != -1; final List<ActivityManager.RunningTaskInfo> tasks = - mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */); + mActivityTaskManager.getTasks(hideDragSourceTask ? 2 : 1, + false /* filterOnlyVisibleRecents */); if (!tasks.isEmpty()) { - final ActivityManager.RunningTaskInfo task = tasks.get(0); - runningTaskInfo = task; - runningTaskWinMode = task.getWindowingMode(); - runningTaskActType = task.getActivityType(); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, - "Running task: id=%d component=%s", task.taskId, - task.baseIntent != null ? task.baseIntent.getComponent() : "null"); + for (int i = tasks.size() - 1; i >= 0; i--) { + final ActivityManager.RunningTaskInfo task = tasks.get(i); + if (hideDragSourceTask && hideDragSourceTaskId == task.taskId) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Skipping running task: id=%d component=%s", task.taskId, + task.baseIntent != null ? task.baseIntent.getComponent() : "null"); + continue; + } + runningTaskInfo = task; + runningTaskWinMode = task.getWindowingMode(); + runningTaskActType = task.getActivityType(); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Running task: id=%d component=%s", task.taskId, + task.baseIntent != null ? task.baseIntent.getComponent() : "null"); + break; + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index fbb4bc42a6b8..9539a456502f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -37,6 +37,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.IRecentsAnimationRunner; +import android.window.WindowContainerToken; import androidx.annotation.BinderThread; import androidx.annotation.NonNull; @@ -457,11 +458,31 @@ public class RecentTasksController implements TaskStackListenerCallback, } /** - * Find the background task that match the given component. + * Returns the top running leaf task ignoring {@param ignoreTaskToken} if it is specified. + * NOTE: This path currently makes assumptions that ignoreTaskToken is for the top task. + */ + @Nullable + public ActivityManager.RunningTaskInfo getTopRunningTask( + @Nullable WindowContainerToken ignoreTaskToken) { + List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(2, + false /* filterOnlyVisibleRecents */); + for (int i = tasks.size() - 1; i >= 0; i--) { + final ActivityManager.RunningTaskInfo task = tasks.get(i); + if (task.token.equals(ignoreTaskToken)) { + continue; + } + return task; + } + return null; + } + + /** + * Find the background task that match the given component. Ignores tasks match + * {@param ignoreTaskToken} if it is non-null. */ @Nullable public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName, - int userId) { + int userId, @Nullable WindowContainerToken ignoreTaskToken) { if (componentName == null) { return null; } @@ -473,6 +494,9 @@ public class RecentTasksController implements TaskStackListenerCallback, if (task.isVisible) { continue; } + if (task.token.equals(ignoreTaskToken)) { + continue; + } if (componentName.equals(task.baseIntent.getComponent()) && userId == task.userId) { return task; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index b4941a5b49b5..e659151fee7f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -64,6 +64,7 @@ import android.view.SurfaceSession; import android.view.WindowManager; import android.widget.Toast; import android.window.RemoteTransition; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.BinderThread; @@ -526,7 +527,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.requestEnterSplitSelect(taskInfo, wct, splitPosition, taskBounds); } - public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) { + /** + * Starts an existing task into split. + * TODO(b/351900580): We should remove this path and use StageCoordinator#startTask() instead + * @param hideTaskToken is not supported. + */ + public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Legacy startTask does not support hide task token"); final int[] result = new int[1]; IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { @Override @@ -584,8 +593,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, if (options == null) options = new Bundle(); final ActivityOptions activityOptions = ActivityOptions.fromBundle(options); - if (samePackage(packageName, getPackageName(reverseSplitPosition(position)), - user.getIdentifier(), getUserId(reverseSplitPosition(position)))) { + if (samePackage(packageName, getPackageName(reverseSplitPosition(position), null), + user.getIdentifier(), getUserId(reverseSplitPosition(position), null))) { if (mMultiInstanceHelpher.supportsMultiInstanceSplit( getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) { activityOptions.setApplyMultipleTaskFlagForShortcut(true); @@ -676,10 +685,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, * See {@link #startIntent(PendingIntent, int, Intent, int, Bundle)} * @param instanceId to be used by {@link SplitscreenEventLogger} */ - public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent, - @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) { + public void startIntentWithInstanceId(PendingIntent intent, int userId, + @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options, + @NonNull InstanceId instanceId) { mStageCoordinator.onRequestToSplit(instanceId, ENTER_REASON_LAUNCHER); - startIntent(intent, userId, fillInIntent, position, options); + startIntent(intent, userId, fillInIntent, position, options, null /* hideTaskToken */); } private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, @@ -825,9 +835,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, instanceId); } + /** + * Starts the given intent into split. + * @param hideTaskToken If non-null, a task matching this token will be moved to back in the + * same window container transaction as the starting of the intent. + */ @Override public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent, - @SplitPosition int position, @Nullable Bundle options) { + @SplitPosition int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1, fillInIntent, position); @@ -838,23 +854,24 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION); final String packageName1 = SplitScreenUtils.getPackageName(intent); - final String packageName2 = getPackageName(reverseSplitPosition(position)); - final int userId2 = getUserId(reverseSplitPosition(position)); + final String packageName2 = getPackageName(reverseSplitPosition(position), hideTaskToken); + final int userId2 = getUserId(reverseSplitPosition(position), hideTaskToken); final ComponentName component = intent.getIntent().getComponent(); // To prevent accumulating large number of instances in the background, reuse task // in the background. If we don't explicitly reuse, new may be created even if the app // isn't multi-instance because WM won't automatically remove/reuse the previous instance final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional - .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1)) + .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1, + hideTaskToken)) .orElse(null); if (taskInfo != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Found suitable background task=%s", taskInfo); if (ENABLE_SHELL_TRANSITIONS) { - mStageCoordinator.startTask(taskInfo.taskId, position, options); + mStageCoordinator.startTask(taskInfo.taskId, position, options, hideTaskToken); } else { - startTask(taskInfo.taskId, position, options); + startTask(taskInfo.taskId, position, options, hideTaskToken); } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Start task in background"); return; @@ -879,19 +896,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - mStageCoordinator.startIntent(intent, fillInIntent, position, options); + mStageCoordinator.startIntent(intent, fillInIntent, position, options, hideTaskToken); } - /** Retrieve package name of a specific split position if split screen is activated, otherwise - * returns the package name of the top running task. */ + /** + * Retrieve package name of a specific split position if split screen is activated, otherwise + * returns the package name of the top running task. + * TODO(b/351900580): Merge this with getUserId() so we don't make multiple binder calls + */ @Nullable - private String getPackageName(@SplitPosition int position) { + private String getPackageName(@SplitPosition int position, + @Nullable WindowContainerToken ignoreTaskToken) { ActivityManager.RunningTaskInfo taskInfo; if (isSplitScreenVisible()) { taskInfo = getTaskInfo(position); } else { taskInfo = mRecentTasksOptional - .map(recentTasks -> recentTasks.getTopRunningTask()) + .map(recentTasks -> recentTasks.getTopRunningTask(ignoreTaskToken)) .orElse(null); if (!isValidToSplit(taskInfo)) { return null; @@ -901,15 +922,19 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null; } - /** Retrieve user id of a specific split position if split screen is activated, otherwise - * returns the user id of the top running task. */ - private int getUserId(@SplitPosition int position) { + /** + * Retrieve user id of a specific split position if split screen is activated, otherwise + * returns the user id of the top running task. + * TODO: Merge this with getPackageName() so we don't make multiple binder calls + */ + private int getUserId(@SplitPosition int position, + @Nullable WindowContainerToken ignoreTaskToken) { ActivityManager.RunningTaskInfo taskInfo; if (isSplitScreenVisible()) { taskInfo = getTaskInfo(position); } else { taskInfo = mRecentTasksOptional - .map(recentTasks -> recentTasks.getTopRunningTask()) + .map(recentTasks -> recentTasks.getTopRunningTask(ignoreTaskToken)) .orElse(null); if (!isValidToSplit(taskInfo)) { return -1; @@ -1290,7 +1315,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startTask(int taskId, int position, @Nullable Bundle options) { executeRemoteCallWithTaskPermission(mController, "startTask", - (controller) -> controller.startTask(taskId, position, options)); + (controller) -> controller.startTask(taskId, position, options, + null /* hideTaskToken */)); } @Override @@ -1402,8 +1428,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position, @Nullable Bundle options, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntent", - (controller) -> controller.startIntent(intent, userId, fillInIntent, position, - options, instanceId)); + (controller) -> controller.startIntentWithInstanceId(intent, userId, + fillInIntent, position, options, instanceId)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index d9e97766e4fe..41042344fd3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -592,12 +592,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - /** Use this method to launch an existing Task via a taskId */ - void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) { + /** + * Use this method to launch an existing Task via a taskId. + * @param hideTaskToken If non-null, a task matching this token will be moved to back in the + * same window container transaction as the starting of the intent. + */ + void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options, + @Nullable WindowContainerToken hideTaskToken) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position); mSplitRequest = new SplitRequest(taskId, position); final WindowContainerTransaction wct = new WindowContainerTransaction(); options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); + if (hideTaskToken != null) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom"); + wct.reorder(hideTaskToken, false /* onTop */); + } wct.startTask(taskId, options); // If this should be mixed, send the task to avoid split handle transition directly. if (mMixedHandler != null && mMixedHandler.isTaskInPip(taskId, mTaskOrganizer)) { @@ -623,9 +632,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, extraTransitType, !mIsDropEntering); } - /** Launches an activity into split. */ + /** + * Launches an activity into split. + * @param hideTaskToken If non-null, a task matching this token will be moved to back in the + * same window container transaction as the starting of the intent. + */ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, - @Nullable Bundle options) { + @Nullable Bundle options, @Nullable WindowContainerToken hideTaskToken) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(), position); mSplitRequest = new SplitRequest(intent.getIntent(), position); @@ -636,6 +649,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); + if (hideTaskToken != null) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom"); + wct.reorder(hideTaskToken, false /* onTop */); + } wct.sendPendingIntent(intent, fillInIntent, options); // If this should be mixed, just send the intent to avoid split handle transition directly. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java index 3a680097554f..dd4595a70211 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java @@ -27,6 +27,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; @@ -75,6 +76,7 @@ public class ShellInit { */ @VisibleForTesting public void init() { + ProtoLog.registerGroups(ShellProtoLogGroup.values()); ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size()); SurfaceControl.setDebugUsageAfterRelease(true); // Init in order of registration diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt deleted file mode 100644 index ee6c81a3fa04..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.util - -import android.util.Log -import com.android.internal.protolog.common.IProtoLogGroup -import com.android.internal.protolog.ProtoLog - -/** - * Log messages using an API similar to [com.android.internal.protolog.ProtoLog]. Useful for - * logging from Kotlin classes as ProtoLog does not have support for Kotlin. - * - * All messages are logged to logcat if logging is enabled for that [IProtoLogGroup]. - */ -// TODO(b/168581922): remove once ProtoLog adds support for Kotlin -class KtProtoLog { - companion object { - /** @see [com.android.internal.protolog.ProtoLog.d] */ - fun d(group: IProtoLogGroup, messageString: String, vararg args: Any) { - if (group.isLogToLogcat) { - Log.d(group.tag, String.format(messageString, *args)) - } - } - - /** @see [com.android.internal.protolog.ProtoLog.v] */ - fun v(group: IProtoLogGroup, messageString: String, vararg args: Any) { - if (group.isLogToLogcat) { - Log.v(group.tag, String.format(messageString, *args)) - } - } - - /** @see [com.android.internal.protolog.ProtoLog.i] */ - fun i(group: IProtoLogGroup, messageString: String, vararg args: Any) { - if (group.isLogToLogcat) { - Log.i(group.tag, String.format(messageString, *args)) - } - } - - /** @see [com.android.internal.protolog.ProtoLog.w] */ - fun w(group: IProtoLogGroup, messageString: String, vararg args: Any) { - if (group.isLogToLogcat) { - Log.w(group.tag, String.format(messageString, *args)) - } - } - - /** @see [com.android.internal.protolog.ProtoLog.e] */ - fun e(group: IProtoLogGroup, messageString: String, vararg args: Any) { - if (group.isLogToLogcat) { - Log.e(group.tag, String.format(messageString, *args)) - } - } - - /** @see [com.android.internal.protolog.ProtoLog.wtf] */ - fun wtf(group: IProtoLogGroup, messageString: String, vararg args: Any) { - if (group.isLogToLogcat) { - Log.wtf(group.tag, String.format(messageString, *args)) - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 1be33e5d2d95..faf6a627febe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.view.InputDevice.SOURCE_TOUCHSCREEN; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_HOVER_ENTER; @@ -41,12 +42,17 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; +import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -410,6 +416,26 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.closeMaximizeMenu(); } + private void onOpenInBrowser(@NonNull DesktopModeWindowDecoration decor, @NonNull Uri uri) { + openInBrowser(uri); + decor.closeHandleMenu(); + decor.closeMaximizeMenu(); + } + + private void openInBrowser(Uri uri) { + final Intent intent = new Intent(Intent.ACTION_VIEW, uri) + .setComponent(getDefaultBrowser()) + .addFlags(FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + private ComponentName getDefaultBrowser() { + final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://")); + final ResolveInfo info = mContext.getPackageManager() + .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + return info.getComponentInfo().getComponentName(); + } + private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, View.OnGenericMotionListener, DragDetector.MotionEventHandler { @@ -489,6 +515,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); mDesktopTasksController.requestSplit(decoration.mTaskInfo); + } else if (id == R.id.open_in_browser_button) { + // TODO(b/346441962): let the decoration handle the click gesture and only call back + // to the ViewModel via #setOpenInBrowserClickListener + decoration.onOpenInBrowserClick(); } else if (id == R.id.collapse_menu_button) { decoration.closeHandleMenu(); } else if (id == R.id.maximize_window) { @@ -1091,6 +1121,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { windowDecoration.setOnRightSnapClickListener((taskId, tag) -> { onSnapResize(taskId, false /* isLeft */); }); + windowDecoration.setOpenInBrowserClickListener(this::onOpenInBrowser); windowDecoration.setCaptionListeners( touchEventListener, touchEventListener, touchEventListener, touchEventListener); windowDecoration.setExclusionRegionListener(mExclusionRegionListener); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index b62194ca6239..5ffd883a7ceb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -44,6 +44,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Handler; import android.os.Trace; import android.util.Log; @@ -88,6 +89,7 @@ import java.util.function.Supplier; */ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> { private static final String TAG = "DesktopModeWindowDecoration"; + private static final int CAPTURED_LINK_TIMEOUT_MS = 7000; @VisibleForTesting static final long CLOSE_MAXIMIZE_MENU_DELAY_MS = 150L; @@ -124,6 +126,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private Bitmap mResizeVeilBitmap; private CharSequence mAppName; + private CapturedLink mCapturedLink; + private OpenInBrowserClickListener mOpenInBrowserClickListener; private ExclusionRegionListener mExclusionRegionListener; @@ -138,6 +142,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // being hovered. There's a small delay after stopping the hover, to allow a quick reentry // to cancel the close. private final Runnable mCloseMaximizeWindowRunnable = this::closeMaximizeMenu; + private final Runnable mCapturedLinkExpiredRunnable = this::onCapturedLinkExpired; DesktopModeWindowDecoration( Context context, @@ -153,8 +158,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, - new SurfaceControlViewHostFactory() {}, - DefaultMaximizeMenuFactory.INSTANCE); + new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE); } DesktopModeWindowDecoration( @@ -232,6 +236,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop()); } + void setOpenInBrowserClickListener(OpenInBrowserClickListener listener) { + mOpenInBrowserClickListener = listener; + } + @Override void relayout(ActivityManager.RunningTaskInfo taskInfo) { final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); @@ -323,6 +331,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces"); + + if (Flags.enableDesktopWindowingAppToWeb()) { + setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp); + } + if (isHandleMenuActive()) { mHandleMenu.relayout(startT); } @@ -367,6 +380,28 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces } + private void setCapturedLink(Uri capturedLink, long timeStamp) { + if (capturedLink == null + || (mCapturedLink != null && mCapturedLink.mTimeStamp == timeStamp)) { + return; + } + mCapturedLink = new CapturedLink(capturedLink, timeStamp); + mHandler.postDelayed(mCapturedLinkExpiredRunnable, CAPTURED_LINK_TIMEOUT_MS); + } + + private void onCapturedLinkExpired() { + mHandler.removeCallbacks(mCapturedLinkExpiredRunnable); + if (mCapturedLink != null) { + mCapturedLink.setExpired(); + } + } + + void onOpenInBrowserClick() { + if (mOpenInBrowserClickListener == null || mCapturedLink == null) return; + mOpenInBrowserClickListener.onClick(this, mCapturedLink.mUri); + onCapturedLinkExpired(); + } + private void updateDragResizeListener(SurfaceControl oldDecorationSurface) { if (!isDragResizable(mTaskInfo)) { if (!mTaskInfo.positionInParent.equals(mPositionInParent)) { @@ -827,11 +862,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin .setCaptionHeight(mResult.mCaptionHeight) .setDisplayController(mDisplayController) .setSplitScreenController(splitScreenController) + .setBrowserLinkAvailable(browserLinkAvailable()) .build(); mWindowDecorViewHolder.onHandleMenuOpened(); mHandleMenu.show(); } + @VisibleForTesting + boolean browserLinkAvailable() { + return mCapturedLink != null && !mCapturedLink.mExpired; + } + /** * Close the handle menu window. */ @@ -1121,6 +1162,31 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } } + @VisibleForTesting + static class CapturedLink { + private final long mTimeStamp; + private final Uri mUri; + private boolean mExpired; + + CapturedLink(@NonNull Uri uri, long timeStamp) { + mUri = uri; + mTimeStamp = timeStamp; + mExpired = false; + } + + void setExpired() { + mExpired = true; + } + } + + + /** Listener for the handle menu's "Open in browser" button */ + interface OpenInBrowserClickListener { + + /** Inform the implementing class that the "Open in browser" button has been clicked */ + void onClick(DesktopModeWindowDecoration decoration, Uri uri); + } + interface ExclusionRegionListener { /** Inform the implementing class of this task's change in region resize handles */ void onExclusionRegionChanged(int taskId, Region region); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java index df0836c1121d..7e44f32bcbeb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -42,6 +42,7 @@ import android.graphics.Rect; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; +import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; @@ -81,6 +82,7 @@ class HandleMenu { // those as well. final Point mGlobalMenuPosition = new Point(); private final boolean mShouldShowWindowingPill; + private final boolean mShouldShowBrowserPill; private final Bitmap mAppIconBitmap; private final CharSequence mAppName; private final View.OnClickListener mOnClickListener; @@ -101,7 +103,7 @@ class HandleMenu { View.OnClickListener onClickListener, View.OnTouchListener onTouchListener, Bitmap appIcon, CharSequence appName, DisplayController displayController, SplitScreenController splitScreenController, boolean shouldShowWindowingPill, - int captionHeight) { + boolean shouldShowBrowserPill, int captionHeight) { mParentDecor = parentDecor; mContext = mParentDecor.mDecorWindowContext; mTaskInfo = mParentDecor.mTaskInfo; @@ -113,6 +115,7 @@ class HandleMenu { mAppIconBitmap = appIcon; mAppName = appName; mShouldShowWindowingPill = shouldShowWindowingPill; + mShouldShowBrowserPill = shouldShowBrowserPill; mCaptionHeight = captionHeight; loadHandleMenuDimensions(); updateHandleMenuPillPositions(); @@ -170,6 +173,7 @@ class HandleMenu { setupWindowingPill(handleMenu); } setupMoreActionsPill(handleMenu); + setupOpenInBrowserPill(handleMenu); } /** @@ -228,6 +232,15 @@ class HandleMenu { } } + private void setupOpenInBrowserPill(View handleMenu) { + if (!mShouldShowBrowserPill) { + handleMenu.findViewById(R.id.open_in_browser_pill).setVisibility(View.GONE); + return; + } + final Button browserButton = handleMenu.findViewById(R.id.open_in_browser_button); + browserButton.setOnClickListener(mOnClickListener); + } + /** * Returns array of windowing icon color based on current UI theme. First element of the * array is for inactive icons and the second is for active icons. @@ -423,6 +436,10 @@ class HandleMenu { menuHeight -= loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_more_actions_pill_height); } + if (!mShouldShowBrowserPill) { + menuHeight -= loadDimensionPixelSize(resources, + R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height); + } return menuHeight; } @@ -457,6 +474,7 @@ class HandleMenu { private int mCaptionHeight; private DisplayController mDisplayController; private SplitScreenController mSplitScreenController; + private boolean mShowBrowserPill; Builder(@NonNull DesktopModeWindowDecoration parent) { mParent = parent; @@ -507,10 +525,15 @@ class HandleMenu { return this; } + Builder setBrowserLinkAvailable(Boolean showBrowserPill) { + mShowBrowserPill = showBrowserPill; + return this; + } + HandleMenu build() { return new HandleMenu(mParent, mLayoutId, mOnClickListener, mOnTouchListener, mAppIcon, mName, mDisplayController, mSplitScreenController, - mShowWindowingPill, mCaptionHeight); + mShowWindowingPill, mShowBrowserPill, mCaptionHeight); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt index 8c5d4a2c2ffb..25a829b44448 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt @@ -26,6 +26,7 @@ import android.view.View.SCALE_Y import android.view.View.TRANSLATION_Y import android.view.View.TRANSLATION_Z import android.view.ViewGroup +import android.widget.Button import androidx.core.animation.doOnEnd import androidx.core.view.children import com.android.wm.shell.R @@ -72,6 +73,7 @@ class HandleMenuAnimator( private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill) private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill) private val moreActionsPill: ViewGroup = handleMenu.requireViewById(R.id.more_actions_pill) + private val openInBrowserPill: ViewGroup = handleMenu.requireViewById(R.id.open_in_browser_pill) /** Animates the opening of the handle menu. */ fun animateOpen() { @@ -80,6 +82,7 @@ class HandleMenuAnimator( animateAppInfoPillOpen() animateWindowingPillOpen() animateMoreActionsPillOpen() + animateOpenInBrowserPill() runAnimations() } @@ -94,6 +97,7 @@ class HandleMenuAnimator( animateAppInfoPillOpen() animateWindowingPillOpen() animateMoreActionsPillOpen() + animateOpenInBrowserPill() runAnimations() } @@ -109,6 +113,7 @@ class HandleMenuAnimator( animateAppInfoPillFadeOut() windowingPillClose() moreActionsPillClose() + openInBrowserPillClose() runAnimations(after) } @@ -125,6 +130,7 @@ class HandleMenuAnimator( animateAppInfoPillFadeOut() windowingPillClose() moreActionsPillClose() + openInBrowserPillClose() runAnimations(after) } @@ -137,6 +143,7 @@ class HandleMenuAnimator( appInfoPill.children.forEach { it.alpha = 0f } windowingPill.alpha = 0f moreActionsPill.alpha = 0f + openInBrowserPill.alpha = 0f // Setup pivots. handleMenu.pivotX = menuWidth / 2f @@ -147,6 +154,9 @@ class HandleMenuAnimator( moreActionsPill.pivotX = menuWidth / 2f moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat() + + openInBrowserPill.pivotX = menuWidth / 2f + openInBrowserPill.pivotY = appInfoPill.measuredHeight.toFloat() } private fun animateAppInfoPillOpen() { @@ -268,12 +278,50 @@ class HandleMenuAnimator( // More Actions Content Opacity Animation moreActionsPill.children.forEach { animators += - ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { + ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_CONTENT_ALPHA_OPEN_DURATION + interpolator = Interpolators.FAST_OUT_SLOW_IN + } + } + } + + private fun animateOpenInBrowserPill() { + // Open in Browser X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(openInBrowserPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION + } + + animators += + ObjectAnimator.ofFloat(openInBrowserPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION + } + + // Open in Browser Opacity Animation + animators += + ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 1f).apply { + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_ALPHA_OPEN_DURATION + } + + // Open in Browser Elevation Animation + animators += + ObjectAnimator.ofFloat(openInBrowserPill, TRANSLATION_Z, 1f).apply { + startDelay = ELEVATION_OPEN_DELAY + duration = BODY_ELEVATION_OPEN_DURATION + } + + // Open in Browser Button Opacity Animation + val button = openInBrowserPill.requireViewById<Button>(R.id.open_in_browser_button) + animators += + ObjectAnimator.ofFloat(button, ALPHA, 1f).apply { startDelay = BODY_ALPHA_OPEN_DELAY duration = BODY_CONTENT_ALPHA_OPEN_DURATION interpolator = Interpolators.FAST_OUT_SLOW_IN } - } } private fun appInfoPillCollapse() { @@ -379,6 +427,37 @@ class HandleMenuAnimator( } } + private fun openInBrowserPillClose() { + // Open in Browser X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(openInBrowserPill, SCALE_X, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(openInBrowserPill, SCALE_Y, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + // Open in Browser Opacity Animation + animators += + ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + // Upward Open in Browser y-translation Animation + val yStart: Float = -captionHeight / 2 + animators += + ObjectAnimator.ofFloat(openInBrowserPill, TRANSLATION_Y, yStart).apply { + duration = BODY_CLOSE_DURATION + } + } + /** * Runs the list of hide animators concurrently. * diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt index c725b08d2f5e..430f80b9a927 100644 --- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt @@ -20,6 +20,7 @@ import android.tools.flicker.AssertionInvocationGroup import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart +import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd @@ -45,27 +46,30 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - return transitions.filter { - it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions.filter { + // TODO(351168217) Use jank CUJ to extract a longer trace + it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP + } + } } - } - } - ), + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( - AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP), - AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP), - AppWindowHasDesktopModeInitialBoundsAtTheEnd( - Components.DESKTOP_MODE_APP + AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP), + AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP), + AppWindowHasDesktopModeInitialBoundsAtTheEnd( + Components.DESKTOP_MODE_APP + ), + AppWindowBecomesVisible(DESKTOP_WALLPAPER) ) - ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) // Use this scenario for closing an app in desktop windowing, except the last app. For the @@ -74,63 +78,65 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CLOSE_APP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - // In case there are multiple windows closing, filter out the - // last window closing. It should use the CLOSE_LAST_APP - // scenario below. - return transitions - .filter { it.type == TransitionType.CLOSE } - .sortedByDescending { it.id } - .drop(1) - } - } - ), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + // In case there are multiple windows closing, filter out the + // last window closing. It should use the CLOSE_LAST_APP + // scenario below. + return transitions + .filter { it.type == TransitionType.CLOSE } + .sortedByDescending { it.id } + .drop(1) + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( - AppWindowOnTopAtStart(Components.DESKTOP_MODE_APP), - AppLayerIsVisibleAtStart(Components.DESKTOP_MODE_APP), - AppLayerIsInvisibleAtEnd(Components.DESKTOP_MODE_APP), - ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + AppWindowOnTopAtStart(Components.DESKTOP_MODE_APP), + AppLayerIsVisibleAtStart(Components.DESKTOP_MODE_APP), + AppLayerIsInvisibleAtEnd(Components.DESKTOP_MODE_APP), + ) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) val CLOSE_LAST_APP = FlickerConfigEntry( scenarioId = ScenarioId("CLOSE_LAST_APP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - val lastTransition = - transitions - .filter { it.type == TransitionType.CLOSE } - .maxByOrNull { it.id }!! - return listOf(lastTransition) - } - } - ), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + val lastTransition = + transitions + .filter { it.type == TransitionType.CLOSE } + .maxByOrNull { it.id }!! + return listOf(lastTransition) + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( - AppWindowIsInvisibleAtEnd(Components.DESKTOP_MODE_APP), - LauncherWindowReplacesAppAsTopWindow(Components.DESKTOP_MODE_APP), - AppWindowIsInvisibleAtEnd(DESKTOP_WALLPAPER) - ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + AppWindowIsInvisibleAtEnd(Components.DESKTOP_MODE_APP), + LauncherWindowReplacesAppAsTopWindow(Components.DESKTOP_MODE_APP), + AppWindowIsInvisibleAtEnd(DESKTOP_WALLPAPER) + ) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) val CORNER_RESIZE = FlickerConfigEntry( scenarioId = ScenarioId("CORNER_RESIZE"), extractor = - TaggedScenarioExtractorBuilder() + TaggedScenarioExtractorBuilder() .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) .setTransitionMatcher( TaggedCujTransitionMatcher(associatedTransitionRequired = false) @@ -143,16 +149,16 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"), extractor = - TaggedScenarioExtractorBuilder() + TaggedScenarioExtractorBuilder() .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) .setTransitionMatcher( TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ).build(), + ) + .build(), assertions = - AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + - listOf( - AppWindowHasSizeOfAtLeast(Components.DESKTOP_MODE_APP, 770, 700) - ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowHasSizeOfAtLeast(Components.DESKTOP_MODE_APP, 770, 700)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp index 3f2603aec86a..35b2f56bca92 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp @@ -107,7 +107,7 @@ android_test { instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", test_config_template: "AndroidTestTemplate.xml", srcs: [ - ":WMShellFlickerTestsSplitScreenGroup1-src", + ":WMShellFlickerTestsSplitScreenGroup2-src", ], static_libs: [ "WMShellFlickerTestsBase", @@ -124,7 +124,7 @@ android_test { instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", test_config_template: "AndroidTestTemplate.xml", srcs: [ - ":WMShellFlickerTestsSplitScreenGroup1-src", + ":WMShellFlickerTestsSplitScreenGroup3-src", ], static_libs: [ "WMShellFlickerTestsBase", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java index 636c6326d213..f5847cc27071 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java @@ -70,7 +70,7 @@ public class DividerViewTest extends ShellTestCase { mContext, configuration, mCallbacks); splitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */); - mDividerView = spy((DividerView) splitWindowManager.getDividerView()); + mDividerView = spy(splitWindowManager.getDividerView()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 582fb91559e5..97fa8d6ceca9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -289,9 +289,9 @@ public class DragAndDropPolicyTest extends ShellTestCase { ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_FULLSCREEN); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN)); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), null /* hideTaskToken */); verify(mFullscreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_UNDEFINED), any()); + eq(SPLIT_POSITION_UNDEFINED), any(), any()); } private void dragOverFullscreenApp_expectSplitScreenTargets(ClipData data) { @@ -304,14 +304,14 @@ public class DragAndDropPolicyTest extends ShellTestCase { ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT)); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_TOP_OR_LEFT), any()); + eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any()); reset(mSplitScreenStarter); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT)); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any()); } private void dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(ClipData data) { @@ -324,14 +324,15 @@ public class DragAndDropPolicyTest extends ShellTestCase { ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP)); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_TOP_OR_LEFT), any()); + eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any()); reset(mSplitScreenStarter); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM)); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), + null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt index 4661042fcb6d..b1d62f485a2a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt @@ -26,6 +26,7 @@ import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY import com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.Companion.convertToToggleOverrideWithFallback import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_ON @@ -286,9 +287,9 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags( - FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, - FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, + FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun isEnabled_dwFlagEnabled_overrideUnset_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_UNSET.setting) @@ -308,9 +309,9 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags( - FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, - FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, + FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun isEnabled_dwFlagEnabled_overrideOn_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_ON.setting) @@ -330,9 +331,9 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags( - FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, - FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, + FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun isEnabled_dwFlagEnabled_overrideOff_featureFlagOn_returnsFalse() { setOverride(OVERRIDE_OFF.setting) @@ -352,7 +353,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags( - FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) fun isEnabled_dwFlagDisabled_overrideUnset_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_UNSET.setting) @@ -364,7 +365,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags( - FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun isEnabled_dwFlagDisabled_overrideUnset_featureFlagOff_returnsFalse() { setOverride(OVERRIDE_UNSET.setting) @@ -374,7 +375,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags( - FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) fun isEnabled_dwFlagDisabled_overrideOn_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_ON.setting) @@ -386,7 +387,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags( - FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun isEnabled_dwFlagDisabled_overrideOn_featureFlagOff_returnTrue() { setOverride(OVERRIDE_ON.setting) @@ -396,7 +397,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags( - FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) fun isEnabled_dwFlagDisabled_overrideOff_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_OFF.setting) @@ -408,7 +409,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags( - FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun isEnabled_dwFlagDisabled_overrideOff_featureFlagOff_returnsFalse() { setOverride(OVERRIDE_OFF.setting) @@ -416,6 +417,19 @@ class DesktopModeFlagsTest : ShellTestCase() { assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse() } + @Test + fun convertToToggleOverrideWithFallback_validInt_returnsToggleOverride() { + assertThat(convertToToggleOverrideWithFallback(0, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_OFF) + assertThat(convertToToggleOverrideWithFallback(1, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_ON) + assertThat(convertToToggleOverrideWithFallback(-1, OVERRIDE_ON)).isEqualTo(OVERRIDE_UNSET) + } + + @Test + fun convertToToggleOverrideWithFallback_invalidInt_returnsFallback() { + assertThat(convertToToggleOverrideWithFallback(2, OVERRIDE_ON)).isEqualTo(OVERRIDE_ON) + assertThat(convertToToggleOverrideWithFallback(-2, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_UNSET) + } + private fun setOverride(setting: Int?) { val contentResolver = mContext.contentResolver val key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES @@ -427,15 +441,10 @@ class DesktopModeFlagsTest : ShellTestCase() { } private fun resetCache() { - val cachedToggleOverrideDesktopMode = - DESKTOP_WINDOWING_MODE::class.java.getDeclaredField("cachedToggleOverride") - cachedToggleOverrideDesktopMode.isAccessible = true - cachedToggleOverrideDesktopMode.set(DESKTOP_WINDOWING_MODE, null) - - val cachedToggleOverrideWallpaperActivity = - WALLPAPER_ACTIVITY::class.java.getDeclaredField("cachedToggleOverride") - cachedToggleOverrideWallpaperActivity.isAccessible = true - cachedToggleOverrideWallpaperActivity.set(WALLPAPER_ACTIVITY, null) + val cachedToggleOverride = + DesktopModeFlags::class.java.getDeclaredField("cachedToggleOverride") + cachedToggleOverride.isAccessible = true + cachedToggleOverride.set(null, null) // Clear override cache stored in System property System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index 3c387f0d7c34..5b95b1588814 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -49,6 +50,9 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Bundle; +import android.os.IBinder; +import android.window.IWindowContainerToken; +import android.window.WindowContainerToken; import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -195,10 +199,10 @@ public class SplitScreenControllerTests extends ShellTestCase { PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(), - eq(SPLIT_POSITION_TOP_OR_LEFT), isNull()); + eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull()); assertEquals(FLAG_ACTIVITY_NO_USER_ACTION, mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_NO_USER_ACTION); } @@ -213,19 +217,20 @@ public class SplitScreenControllerTests extends ShellTestCase { ActivityManager.RunningTaskInfo topRunningTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent); doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(); + doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any()); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(), - eq(SPLIT_POSITION_TOP_OR_LEFT), isNull()); + eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull()); assertEquals(FLAG_ACTIVITY_MULTIPLE_TASK, mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK); } @Test public void startIntent_multiInstancesNotSupported_startTaskInBackgroundBeforeSplitActivated() { - doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any()); + doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any(), any()); Intent startIntent = createStartIntent("startActivity"); PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); @@ -233,15 +238,16 @@ public class SplitScreenControllerTests extends ShellTestCase { ActivityManager.RunningTaskInfo topRunningTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent); doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(); + doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any()); // Put the same component into a task in the background ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo(); - doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any(), anyInt()); + doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any(), anyInt(), any()); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), - isNull()); + isNull(), isNull()); verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any()); verify(mStageCoordinator, never()).switchSplitPosition(any()); } @@ -249,7 +255,7 @@ public class SplitScreenControllerTests extends ShellTestCase { @Test public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() { doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any()); - doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any()); + doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any(), any()); Intent startIntent = createStartIntent("startActivity"); PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); @@ -261,13 +267,13 @@ public class SplitScreenControllerTests extends ShellTestCase { SPLIT_POSITION_BOTTOM_OR_RIGHT); // Put the same component into a task in the background doReturn(new ActivityManager.RecentTaskInfo()).when(mRecentTasks) - .findTaskInBackground(any(), anyInt()); + .findTaskInBackground(any(), anyInt(), any()); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any()); verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), - isNull()); + isNull(), isNull()); } @Test @@ -284,7 +290,7 @@ public class SplitScreenControllerTests extends ShellTestCase { SPLIT_POSITION_BOTTOM_OR_RIGHT); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); verify(mStageCoordinator).switchSplitPosition(anyString()); } @@ -312,6 +318,7 @@ public class SplitScreenControllerTests extends ShellTestCase { info.supportsMultiWindow = true; info.baseIntent = strIntent; info.baseActivity = strIntent.getComponent(); + info.token = new WindowContainerToken(mock(IWindowContainerToken.class)); ActivityInfo activityInfo = new ActivityInfo(); activityInfo.packageName = info.baseActivity.getPackageName(); activityInfo.name = info.baseActivity.getClassName(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 316cefa1eb99..b1803e97b107 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -360,7 +360,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { isTopActivityStyleFloating = true numActivities = 1 } - doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) } setUpMockDecorationsForTasks(task) onTaskOpening(task) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index b355137a9077..d8606093ac5c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -31,6 +31,7 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; @@ -50,6 +51,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PointF; +import android.net.Uri; import android.os.Handler; import android.os.SystemProperties; import android.platform.test.annotations.DisableFlags; @@ -118,6 +120,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private static final String USE_ROUNDED_CORNERS_SYSPROP_KEY = "persist.wm.debug.desktop_use_rounded_corners"; + private static final Uri TEST_URI = Uri.parse("www.google.com"); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @Mock @@ -150,6 +154,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private PackageManager mMockPackageManager; @Mock private Handler mMockHandler; + @Mock + private DesktopModeWindowDecoration.OpenInBrowserClickListener mMockOpenInBrowserClickListener; @Captor private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener; @Captor @@ -555,6 +561,65 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { verify(mMockHandler).removeCallbacks(any()); } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void capturedLink_postsOnCapturedLinkExpiredRunnable() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); + + decor.relayout(taskInfo); + // Assert captured link is set + assertTrue(decor.browserLinkAvailable()); + // Asset runnable posted to set captured link to expired + verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong()); + runnableArgument.getValue().run(); + assertFalse(decor.browserLinkAvailable()); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void capturedLink_capturedLinkNotResetToSameLink() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); + final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + + // Set captured link and run on captured link expired runnable + decor.relayout(taskInfo); + verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong()); + runnableArgument.getValue().run(); + + decor.relayout(taskInfo); + // Assert captured link not set to same value twice + assertFalse(decor.browserLinkAvailable()); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void capturedLink_capturedLinkExpiresAfterClick() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); + + decor.relayout(taskInfo); + // Assert captured link is set + assertTrue(decor.browserLinkAvailable()); + decor.onOpenInBrowserClick(); + //Assert Captured link expires after button is clicked + assertFalse(decor.browserLinkAvailable()); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void capturedLink_openInBrowserListenerCalledOnClick() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); + + decor.relayout(taskInfo); + decor.onOpenInBrowserClick(); + + verify(mMockOpenInBrowserClickListener).onClick(any(), any()); + } + private void createMaximizeMenu(DesktopModeWindowDecoration decoration, MaximizeMenu menu) { final OnTaskActionClickListener l = (taskId, tag) -> {}; decoration.setOnMaximizeOrRestoreClickListener(l); @@ -595,11 +660,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, - mMockSurfaceControlViewHostFactory, - maximizeMenuFactory); + mMockSurfaceControlViewHostFactory, maximizeMenuFactory); windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener); windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); + windowDecor.setOpenInBrowserClickListener(mMockOpenInBrowserClickListener); return windowDecor; } @@ -615,6 +680,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { "DesktopModeWindowDecorationTests"); taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor", "DesktopModeWindowDecorationTests"); + taskInfo.capturedLink = TEST_URI; + taskInfo.capturedLinkTimestamp = System.currentTimeMillis(); return taskInfo; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt index 5582e0f46321..0c50ab6b5008 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt @@ -196,9 +196,9 @@ class HandleMenuTest : ShellTestCase() { R.layout.desktop_mode_app_header } val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId, - onClickListener, onTouchListener, appIcon, appName, displayController, - splitScreenController, true /* shouldShowWindowingPill */, - 50 /* captionHeight */ ) + onClickListener, onTouchListener, appIcon, appName, displayController, + splitScreenController, true /* shouldShowWindowingPill */, + true /* shouldShowBrowserPill */, 50 /* captionHeight */) handleMenu.show() return handleMenu } |