diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index ff97b22e4e..9051ca8562 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -5,7 +5,7 @@ ktfmt = true ktfmt = --kotlinlang-style [Tool Paths] -ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar +ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --config_xml tools/checkstyle.xml --sha ${PREUPLOAD_COMMIT} diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig index 9147e4c54f..f1f9966f78 100644 --- a/aconfig/launcher.aconfig +++ b/aconfig/launcher.aconfig @@ -78,6 +78,13 @@ flag { bug: "296231746" } +flag { + name: "enable_taskbar_customization" + namespace: "launcher" + description: "Enables taskbar customization framework." + bug: "347281365" +} + flag { name: "enable_unfolded_two_pane_picker" namespace: "launcher" @@ -284,3 +291,28 @@ flag { description: "Enables folders in all apps" bug: "341582436" } + +flag { + name: "enable_recents_in_taskbar" + namespace: "launcher" + description: "Replace hybrid hotseat app predictions with strictly Recent Apps" + bug: "315354060" +} + +flag { + name: "enable_first_screen_broadcast_archiving_extras" + namespace: "launcher" + description: "adds Extras to first screen broadcast for archived apps" + bug: "322314760" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "floating_search_bar" + namespace: "launcher" + description: "Search bar persists at the bottom of the screen across Launcher states" + bug: "346408388" +} diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig index b243922a29..b98eee6e32 100644 --- a/aconfig/launcher_search.aconfig +++ b/aconfig/launcher_search.aconfig @@ -45,8 +45,11 @@ flag { flag { - name: "private_space_floating_mask_view" + name: "private_space_add_floating_mask_view" namespace: "launcher_search" description: "This flag enables the floating mask view as part of the Private Space animation. " bug: "339850589" + metadata { + purpose: PURPOSE_BUGFIX + } } diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml index c6e2d8cb74..80d8154ba1 100644 --- a/quickstep/AndroidManifest-launcher.xml +++ b/quickstep/AndroidManifest-launcher.xml @@ -48,7 +48,7 @@ android:stateNotNeeded="true" android:windowSoftInputMode="adjustPan" android:screenOrientation="unspecified" - android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" + android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" android:resizeableActivity="true" android:resumeWhilePausing="true" android:taskAffinity="" diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index bf198b605f..4abf6e150b 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -80,7 +80,7 @@ android:stateNotNeeded="true" android:theme="@style/LauncherTheme" android:screenOrientation="behind" - android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" + android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" android:resizeableActivity="true" android:resumeWhilePausing="true" android:enableOnBackInvokedCallback="false" diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml index 42cddbfa0d..9144c7f9f7 100644 --- a/quickstep/res/layout/digital_wellbeing_toast.xml +++ b/quickstep/res/layout/digital_wellbeing_toast.xml @@ -19,7 +19,7 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" style="@style/TextTitle" android:layout_width="match_parent" - android:layout_height="48dp" + android:layout_height="@dimen/digital_wellbeing_toast_height" android:background="@drawable/bg_wellbeing_toast" android:forceHasOverlappingRendering="false" android:gravity="center" diff --git a/quickstep/res/layout/split_instructions_view.xml b/quickstep/res/layout/split_instructions_view.xml index 1115ff2b7c..797ea45be4 100644 --- a/quickstep/res/layout/split_instructions_view.xml +++ b/quickstep/res/layout/split_instructions_view.xml @@ -29,6 +29,7 @@ android:id="@+id/split_instructions_text" android:layout_height="wrap_content" android:layout_width="wrap_content" + android:maxWidth="@dimen/split_instructions_view_max_width" android:textColor="?androidprv:attr/textColorOnAccent" android:text="@string/toast_split_select_app" /> @@ -36,6 +37,7 @@ android:id="@+id/split_instructions_text_cancel" android:layout_height="wrap_content" android:layout_width="wrap_content" + android:layout_gravity="center_vertical" android:textColor="?androidprv:attr/textColorOnAccent" android:layout_marginStart="@dimen/split_instructions_start_margin_cancel" android:text="@string/toast_split_select_app_cancel" diff --git a/quickstep/res/layout/taskbar_divider_popup_menu.xml b/quickstep/res/layout/taskbar_divider_popup_menu.xml index 43dc8d8f7d..b184191fc0 100644 --- a/quickstep/res/layout/taskbar_divider_popup_menu.xml +++ b/quickstep/res/layout/taskbar_divider_popup_menu.xml @@ -16,8 +16,9 @@ --> @@ -54,10 +55,10 @@ android:gravity="start|center_vertical" android:textAlignment="viewStart" android:paddingStart="12dp" + android:switchPadding="12dp" android:layout_weight="1" android:fontFamily="@*android:string/config_bodyFontFamilyMedium" android:lines="1" - android:ellipsize="end" android:textSize="14sp" android:textColor="?android:attr/textColorPrimary" android:text="@string/always_show_taskbar" /> diff --git a/quickstep/res/layout/transient_taskbar.xml b/quickstep/res/layout/transient_taskbar.xml index 7c55bf821c..f3c3383ff7 100644 --- a/quickstep/res/layout/transient_taskbar.xml +++ b/quickstep/res/layout/transient_taskbar.xml @@ -45,8 +45,6 @@ android:layout_gravity="bottom|end" android:layout_marginHorizontal="@dimen/transient_taskbar_bottom_margin" android:paddingTop="@dimen/bubblebar_pointer_visible_size" - android:paddingEnd="@dimen/taskbar_icon_spacing" - android:paddingStart="@dimen/taskbar_icon_spacing" android:visibility="gone" android:gravity="center" android:clipChildren="false" diff --git a/quickstep/res/raw-h480dp/all_set_page_bg.json b/quickstep/res/raw-h480dp/all_set_page_bg.json new file mode 100644 index 0000000000..f2998a038a --- /dev/null +++ b/quickstep/res/raw-h480dp/all_set_page_bg.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":181,"w":701,"h":841,"nm":"SUW_WelcomeScreen_FoldableOpen_Portrait_Dynamic","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".primary","cl":"primary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":180,"s":[55]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.949},"o":{"x":0.167,"y":0.167},"t":0,"s":[181.172,148.425,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.038},"t":95,"s":[181.172,-68.377,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[181.172,148.425,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[21.6,21.6,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[100.594,-128.921],[118.85,-93.491],[148.984,-67.406],[148.684,-27.55],[163.244,9.552],[144.457,44.702],[140.106,84.321],[107.136,106.715],[84.872,139.773],[45.271,144.279],[10.194,163.205],[-26.964,148.792],[-66.818,149.249],[-93.023,119.218],[-128.524,101.101],[-137.771,62.332],[-160.786,29.793],[-150.957,-8.833],[-156.215,-48.341],[-129.561,-77.974],[-115.856,-115.401],[-78.484,-129.253],[-48.956,-156.023],[-9.427,-150.921],[29.159,-160.903],[61.789,-138.015]],"o":[[-100.594,128.921],[-118.85,93.491],[-148.984,67.406],[-148.684,27.55],[-163.244,-9.552],[-144.456,-44.702],[-140.106,-84.321],[-107.136,-106.715],[-84.872,-139.773],[-45.271,-144.279],[-10.194,-163.205],[26.964,-148.792],[66.818,-149.249],[93.023,-119.218],[128.524,-101.101],[137.771,-62.332],[160.786,-29.793],[150.957,8.833],[156.215,48.341],[129.561,77.974],[115.856,115.4],[78.484,129.253],[48.956,156.023],[9.427,150.921],[-29.159,160.903],[-61.789,138.015]],"v":[[975.226,761.299],[707.165,899.424],[509.8,1127.42],[208.253,1125.148],[-72.46,1235.308],[-338.41,1093.162],[-638.164,1060.25],[-807.592,810.792],[-1057.715,642.348],[-1091.808,342.727],[-1235.002,77.338],[-1125.948,-203.807],[-1129.407,-505.342],[-902.191,-703.604],[-765.123,-972.207],[-471.796,-1042.166],[-225.603,-1216.305],[66.637,-1141.935],[365.557,-1181.715],[589.761,-980.053],[872.928,-876.361],[977.734,-593.606],[1180.277,-370.197],[1141.675,-71.124],[1217.196,220.821],[1044.029,467.699]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.713725490196,0.556862745098,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.3,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":181,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".tertiary","cl":"tertiary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[0.619]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[67]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[-0.263]},"t":95,"s":[82]},{"t":180,"s":[67]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[0.927]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[458.803]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[-0.05]},"t":95,"s":[536.803]},{"t":180,"s":[458.803]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[707.143]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.069]},"t":95,"s":[639.643]},{"t":180,"s":[707.143]}],"ix":4}},"a":{"k":[{"s":[164.438,1433.781,0],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[164.438,1433.781,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"s":{"a":0,"k":[-30,30,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[2361.125,4541.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.901960784314,0.764705882353,0.423529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":181,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[350.5,420.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[420.5,-350.5],[-420.5,-350.5],[-420.5,350.5],[420.5,350.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":181,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/quickstep/res/raw-w840dp-h480dp/all_set_page_bg.json b/quickstep/res/raw-w840dp-h480dp/all_set_page_bg.json new file mode 100644 index 0000000000..ae1b5604e5 --- /dev/null +++ b/quickstep/res/raw-w840dp-h480dp/all_set_page_bg.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":181,"w":841,"h":701,"nm":"SUW_WelcomeScreen_FelixOpen_Dynamic","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".primary","cl":"primary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":180,"s":[55]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.939},"o":{"x":0.167,"y":0.167},"t":0,"s":[140.975,228.318,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.045},"t":95,"s":[140.975,47.65,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[140.975,228.318,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[18,18,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[100.594,-128.921],[118.85,-93.491],[148.984,-67.406],[148.684,-27.55],[163.244,9.552],[144.457,44.702],[140.106,84.321],[107.136,106.715],[84.872,139.773],[45.271,144.279],[10.194,163.205],[-26.964,148.792],[-66.818,149.249],[-93.023,119.218],[-128.524,101.101],[-137.771,62.332],[-160.786,29.793],[-150.957,-8.833],[-156.215,-48.341],[-129.561,-77.974],[-115.856,-115.401],[-78.484,-129.253],[-48.956,-156.023],[-9.427,-150.921],[29.159,-160.903],[61.789,-138.015]],"o":[[-100.594,128.921],[-118.85,93.491],[-148.984,67.406],[-148.684,27.55],[-163.244,-9.552],[-144.456,-44.702],[-140.106,-84.321],[-107.136,-106.715],[-84.872,-139.773],[-45.271,-144.279],[-10.194,-163.205],[26.964,-148.792],[66.818,-149.249],[93.023,-119.218],[128.524,-101.101],[137.771,-62.332],[160.786,-29.793],[150.957,8.833],[156.215,48.341],[129.561,77.974],[115.856,115.4],[78.484,129.253],[48.956,156.023],[9.427,150.921],[-29.159,160.903],[-61.789,138.015]],"v":[[975.226,761.299],[707.165,899.424],[509.8,1127.42],[208.253,1125.148],[-72.46,1235.308],[-338.41,1093.162],[-638.164,1060.25],[-807.592,810.792],[-1057.715,642.348],[-1091.808,342.727],[-1235.002,77.338],[-1125.948,-203.807],[-1129.407,-505.342],[-902.191,-703.604],[-765.123,-972.207],[-471.796,-1042.166],[-225.603,-1216.305],[66.637,-1141.935],[365.557,-1181.715],[589.761,-980.053],[872.928,-876.361],[977.734,-593.606],[1180.277,-370.197],[1141.675,-71.124],[1217.196,220.821],[1044.029,467.699]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.713725490196,0.556862745098,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"k":[{"s":[11.111],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[11.111],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":181,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".tertiary","cl":"tertiary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[0.619]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[67]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[-0.263]},"t":95,"s":[82]},{"t":180,"s":[67]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[0.913]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[639]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[-0.06]},"t":95,"s":[704]},{"t":180,"s":[639]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.12]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[527.25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.083]},"t":95,"s":[471]},{"t":180,"s":[527.25]}],"ix":4}},"a":{"k":[{"s":[164.438,1433.781,0],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[164.438,1433.781,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"s":{"a":0,"k":[-25,25,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[2361.125,4541.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.901960784314,0.764705882353,0.423529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"k":[{"s":[6],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[6],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":181,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[420.5,350.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[420.5,-350.5],[-420.5,-350.5],[-420.5,350.5],[420.5,350.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":181,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml index 0b03e02122..73c8129160 100644 --- a/quickstep/res/values-af/strings.xml +++ b/quickstep/res/values-af/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Wys nog # app.}other{Wys nog # apps.}}" "{count,plural, =1{Wys # rekenaarapp.}other{Wys # rekenaarapps.}}" "%1$s en %2$s" + "Borrel" + "Oorvloei" + "%1$s vanaf %2$s" + "%1$s en nog %2$d" diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml index c8fd27668f..f9eed3972c 100644 --- a/quickstep/res/values-am/strings.xml +++ b/quickstep/res/values-am/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{ተጨማሪ # መተግበሪያ አሳይ።}one{ተጨማሪ # መተግበሪያ አሳይ።}other{ተጨማሪ # መተግበሪያዎች አሳይ።}}" "{count,plural, =1{# የዴስክቶፕ መተግበሪያ አሳይ።}one{# የዴስክቶፕ መተግበሪያ አሳይ።}other{# የዴስክቶፕ መተግበሪያዎች አሳይ።}}" "%1$s እና %2$s" + "አረፋ" + "ትርፍ ፍሰት" + "%1$s%2$s" + "%1$s እና %2$d ተጨማሪ" diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml index ca0e1dda9b..7a0be9b979 100644 --- a/quickstep/res/values-ar/strings.xml +++ b/quickstep/res/values-ar/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{إظهار تطبيق واحد آخر}zero{إظهار # تطبيق آخر}two{إظهار تطبيقَين آخرَين}few{إظهار # تطبيقات أخرى}many{إظهار # تطبيقًا آخر}other{إظهار # تطبيق آخر}}" "{count,plural, =1{عرض تطبيق واحد متوافق مع الكمبيوتر المكتبي}zero{عرض # تطبيق متوافق مع الكمبيوتر المكتبي}two{عرض تطبيقَين متوافقين مع الكمبيوتر المكتبي}few{عرض # تطبيقات متوافقة مع الكمبيوتر المكتبي}many{عرض # تطبيقًا متوافقًا مع الكمبيوتر المكتبي}other{عرض # تطبيق متوافق مع الكمبيوتر المكتبي}}" "\"%1$s\" و\"%2$s\"" + "فقاعة" + "القائمة الكاملة" + "‫\"%1$s\" من \"%2$s\"" + "‫\"%1$s\" و%2$d غيرها" diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml index 7cd769645c..d440400367 100644 --- a/quickstep/res/values-as/strings.xml +++ b/quickstep/res/values-as/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{আৰু # টা এপ্‌ দেখুৱাওক।}one{আৰু # টা এপ্‌ দেখুৱাওক।}other{আৰু # টা এপ্‌ দেখুৱাওক।}}" "{count,plural, =1{# টা ডেস্কটপ এপ্ দেখুৱাওক।}one{# টা ডেস্কটপ এপ্ দেখুৱাওক।}other{# টা ডেস্কটপ এপ্ দেখুৱাওক।}}" "%1$s আৰু %2$s" + "বাবল" + "অ’ভাৰফ্ল’" + "%2$sৰ পৰা %1$s" + "%1$s আৰু %2$d টা" diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml index f5840abbc9..9cdcc010f9 100644 --- a/quickstep/res/values-az/strings.xml +++ b/quickstep/res/values-az/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Daha # tətbiqi göstərin.}other{Daha # tətbiqi göstərin.}}" "{count,plural, =1{# masaüstü tətbiqini göstərin.}other{# masaüstü tətbiqini göstərin.}}" "%1$s%2$s" + "Yumrucuq" + "Kənara çıxma" + "%1$s, %2$s" + "%1$s və daha %2$d yumrucuq" diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml index 276a8b9549..b6271a9a9c 100644 --- a/quickstep/res/values-b+sr+Latn/strings.xml +++ b/quickstep/res/values-b+sr+Latn/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Prikaži još # aplikaciju.}one{Prikaži još # aplikaciju.}few{Prikaži još # aplikacije.}other{Prikaži još # aplikacija.}}" "{count,plural, =1{Prikaži # aplikaciju za računare.}one{Prikaži # aplikaciju za računare.}few{Prikaži # aplikacije za računare.}other{Prikaži # aplikacija za računare.}}" "%1$s i %2$s" + "Oblačić" + "Preklopni" + "%1$s%2$s" + "%1$s i još %2$d" diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml index 0785e0f487..bb6c7641f0 100644 --- a/quickstep/res/values-be/strings.xml +++ b/quickstep/res/values-be/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Паказаць ячшэ # праграму.}one{Паказаць ячшэ # праграму.}few{Паказаць ячшэ # праграмы.}many{Паказаць ячшэ # праграм.}other{Паказаць ячшэ # праграмы.}}" "{count,plural, =1{Паказаць # праграму для ПК.}one{Паказаць # праграму для ПК.}few{Паказаць # праграмы для ПК.}many{Паказаць # праграм для ПК.}other{Паказаць # праграмы для ПК.}}" "%1$s і %2$s" + "Бурбалкі" + "Меню з пашырэннем" + "%1$s, крыніца: %2$s" + "%1$s і яшчэ %2$d" diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml index c26bc6fd93..d674dbcdbb 100644 --- a/quickstep/res/values-bg/strings.xml +++ b/quickstep/res/values-bg/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Показване на още # приложение.}other{Показване на още # приложения.}}" "{count,plural, =1{Показване на # настолно приложение.}other{Показване на # настолни приложения.}}" "%1$s и %2$s" + "Балонче" + "Препълване" + "%1$s от %2$s" + "%1$s и още %2$d" diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml index f7f666bb9e..3e974f53f3 100644 --- a/quickstep/res/values-bn/strings.xml +++ b/quickstep/res/values-bn/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{আরও #টি অ্যাপ দেখুন।}one{আরও #টি অ্যাপ দেখুন।}other{আরও #টি অ্যাপ দেখুন।}}" "{count,plural, =1{#টি ডেস্কটপ অ্যাপ দেখুন।}one{#টি ডেস্কটপ অ্যাপ দেখুন।}other{#টি ডেস্কটপ অ্যাপ দেখুন।}}" "%1$s%2$s" + "বাবল" + "ওভারফ্লো" + "%2$s থেকে %1$s" + "%1$s এবং আরও %2$dটি" diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml index 11671452b2..19268be2dd 100644 --- a/quickstep/res/values-bs/strings.xml +++ b/quickstep/res/values-bs/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Prikaži još # aplikaciju.}one{Prikaži još # aplikaciju.}few{Prikaži još # aplikacije.}other{Prikaži još # aplikacija.}}" "{count,plural, =1{Prikaži # aplikaciju za računar.}one{Prikaži # aplikaciju za računar.}few{Prikaži # aplikacije za računar.}other{Prikaži # aplikacija za računar.}}" "%1$s i %2$s" + "Oblačić" + "Preklopni meni" + "%1$s iz aplikacije %2$s" + "%1$s i još %2$d" diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml index ba8e3c160e..6a6f131858 100644 --- a/quickstep/res/values-ca/strings.xml +++ b/quickstep/res/values-ca/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Mostra # aplicació més.}other{Mostra # aplicacions més.}}" "{count,plural, =1{Mostra # aplicació per a ordinadors.}other{Mostra # aplicacions per a ordinadors.}}" "%1$s i %2$s" + "Bombolla" + "Desbordament" + "%1$s de %2$s" + "%1$s i %2$d més" diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml index 7f1cd94f9c..e1348500cc 100644 --- a/quickstep/res/values-cs/strings.xml +++ b/quickstep/res/values-cs/strings.xml @@ -73,7 +73,7 @@ "Přejeďte prstem nahoru z dolního okraje obrazovky" "Zkuste podržet okno delší dobu, než ho uvolníte" "Přejeďte prstem přímo nahoru a pak udělejte pauzu" - "Naučili jste se používat gesta. Vypnout je můžete v nastavení." + "Naučili jste se používat gesta. Vypnout je můžete v Nastavení." "Dokončili jste gesto pro přepínání aplikací" "Přepínání aplikací přejetím prstem" "Přejeďte nahoru z dolního okraje obrazovky, podržte obrazovku a uvolněte." @@ -140,4 +140,8 @@ "{count,plural, =1{Zobrazit # další aplikaci.}few{Zobrazit # další aplikace.}many{Zobrazit # další aplikace.}other{Zobrazit # dalších aplikací.}}" "{count,plural, =1{Zobrazit # aplikaci pro počítač.}few{Zobrazit # aplikace pro počítač.}many{Zobrazit # aplikace pro počítač.}other{Zobrazit # aplikací pro počítač.}}" "%1$s%2$s" + "Bublina" + "Rozbalovací nabídka" + "%1$s z aplikace %2$s" + "%1$s a ještě %2$d" diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml index 1daf365c49..e9cdbce258 100644 --- a/quickstep/res/values-da/strings.xml +++ b/quickstep/res/values-da/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Vis # app mere.}one{Vis # app mere.}other{Vis # apps mere.}}" "{count,plural, =1{Vis # computerprogram.}one{Vis # computerprogram.}other{Vis # computerprogrammer.}}" "%1$s og %2$s" + "Boble" + "Overløb" + "%1$s fra %2$s" + "%1$s og %2$d mere" diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml index b089976936..9e5cb12596 100644 --- a/quickstep/res/values-de/strings.xml +++ b/quickstep/res/values-de/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{# weitere App anzeigen}other{# weitere Apps anzeigen}}" "{count,plural, =1{# Desktop-App anzeigen.}other{# Desktop-Apps anzeigen.}}" "%1$s und %2$s" + "Bubble" + "Weitere Optionen" + "„%1$s“ aus %2$s" + "%1$s und %2$d weitere" diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml index 66ca90ecbe..bbb12828f3 100644 --- a/quickstep/res/values-el/strings.xml +++ b/quickstep/res/values-el/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Εμφάνιση # ακόμα εφαρμογής.}other{Εμφάνιση # ακόμα εφαρμογών.}}" "{count,plural, =1{Εμφάνιση # εφαρμογής υπολογιστή.}other{Εμφάνιση # εφαρμογών υπολογιστή.}}" "%1$s και %2$s" + "Συννεφάκι" + "Υπερχείλιση" + "%1$s από %2$s" + "%1$s και %2$d ακόμα" diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml index fe5b479bcd..eedb29ec84 100644 --- a/quickstep/res/values-en-rAU/strings.xml +++ b/quickstep/res/values-en-rAU/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Show # more app.}other{Show # more apps.}}" "{count,plural, =1{Show # desktop app.}other{Show # desktop apps.}}" "%1$s and %2$s" + "Bubble" + "Overflow" + "%1$s from %2$s" + "%1$s and %2$d more" diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml index 3bd2bdd85f..88cd0dd855 100644 --- a/quickstep/res/values-en-rCA/strings.xml +++ b/quickstep/res/values-en-rCA/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Show # more app.}other{Show # more apps.}}" "{count,plural, =1{Show # desktop app.}other{Show # desktop apps.}}" "%1$s and %2$s" + "Bubble" + "Overflow" + "%1$s from %2$s" + "%1$s and %2$d more" diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml index fe5b479bcd..eedb29ec84 100644 --- a/quickstep/res/values-en-rGB/strings.xml +++ b/quickstep/res/values-en-rGB/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Show # more app.}other{Show # more apps.}}" "{count,plural, =1{Show # desktop app.}other{Show # desktop apps.}}" "%1$s and %2$s" + "Bubble" + "Overflow" + "%1$s from %2$s" + "%1$s and %2$d more" diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml index fe5b479bcd..eedb29ec84 100644 --- a/quickstep/res/values-en-rIN/strings.xml +++ b/quickstep/res/values-en-rIN/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Show # more app.}other{Show # more apps.}}" "{count,plural, =1{Show # desktop app.}other{Show # desktop apps.}}" "%1$s and %2$s" + "Bubble" + "Overflow" + "%1$s from %2$s" + "%1$s and %2$d more" diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml index 52efe9d08b..76dab0d3c8 100644 --- a/quickstep/res/values-en-rXC/strings.xml +++ b/quickstep/res/values-en-rXC/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎Show # more app.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎Show # more apps.‎‏‎‎‏‎}}" "{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎Show # desktop app.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎Show # desktop apps.‎‏‎‎‏‎}}" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎%2$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‏‎Bubble‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‎Overflow‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎%2$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎%2$d‎‏‎‎‏‏‏‎ more‎‏‎‎‏‎" diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml index 095b06e2d4..58d540f7bc 100644 --- a/quickstep/res/values-es-rUS/strings.xml +++ b/quickstep/res/values-es-rUS/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Mostrar # app más.}other{Mostrar # apps más.}}" "{count,plural, =1{Mostrar # app para computadoras.}other{Mostrar # apps para computadoras.}}" "%1$s y %2$s" + "Burbuja" + "Ampliada" + "%1$s de %2$s" + "%1$s y %2$d más" diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml index dbc7caaa88..2a956f0b48 100644 --- a/quickstep/res/values-es/strings.xml +++ b/quickstep/res/values-es/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Mostrar # aplicación más.}other{Mostrar # aplicaciones más.}}" "{count,plural, =1{Mostrar # aplicación para ordenadores.}other{Mostrar # aplicaciones para ordenadores.}}" "%1$s y %2$s" + "Burbuja" + "Menú adicional" + "%1$s de %2$s" + "%1$s y %2$d más" diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml index c53f80c64f..0ecc0c01df 100644 --- a/quickstep/res/values-et/strings.xml +++ b/quickstep/res/values-et/strings.xml @@ -73,7 +73,7 @@ "Pühkige kindlasti ekraani alumisest servast üles." "Hoidke sõrme aknal pisut kauem, enne kui vabastate" "Pühkige kindlasti otse üles, seejärel peatuge" - "Õppisite liigutusi kasutama. Liigutuste väljalülitamiseks avage seaded." + "Õppisite liigutusi kasutama. Liigutuste väljalülitamiseks avage Seaded." "Tegite rakenduste vahel vahetamise liigutuse" "Pühkige rakenduste vahetamiseks" "Rakenduste vahel vahetamiseks pühkige ekraanikuva alaosast üles, hoidke ja seejärel vabastage." @@ -140,4 +140,8 @@ "{count,plural, =1{Kuva veel # rakendus.}other{Kuva veel # rakendust.}}" "{count,plural, =1{Kuva # töölauarakendus.}other{Kuva # töölauarakendust.}}" "%1$s ja %2$s" + "Mull" + "Ületäide" + "%1$s%2$s" + "%1$s ja veel %2$d mulli" diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml index ecdacd2465..e83ee28653 100644 --- a/quickstep/res/values-eu/strings.xml +++ b/quickstep/res/values-eu/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Erakutsi beste # aplikazio.}other{Erakutsi beste # aplikazio.}}" "{count,plural, =1{Erakutsi ordenagailuetarako # aplikazio.}other{Erakutsi ordenagailuetarako # aplikazio.}}" "%1$s eta %2$s" + "Burbuila" + "Luzapena" + "%1$s (%2$s)" + "%1$s eta beste %2$d" diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml index 0dad8016aa..b296080164 100644 --- a/quickstep/res/values-fa/strings.xml +++ b/quickstep/res/values-fa/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{نمایش # برنامه دیگر.}one{نمایش # برنامه دیگر.}other{نمایش # برنامه دیگر.}}" "{count,plural, =1{نمایش # برنامه رایانه.}one{نمایش # برنامه رایانه.}other{نمایش # برنامه رایانه.}}" "%1$s و %2$s" + "حبابک" + "سرریز" + "%1$s از %2$s" + "%1$s و %2$d حبابک دیگر" diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml index 8624fe7b3b..5ac124a8ee 100644 --- a/quickstep/res/values-fi/strings.xml +++ b/quickstep/res/values-fi/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Näytä # muu sovellus.}other{Näytä # muuta sovellusta.}}" "{count,plural, =1{Näytä # työpöytäsovellus.}other{Näytä # työpöytäsovellusta.}}" "%1$s ja %2$s" + "Kupla" + "Ylivuoto" + "%1$s: %2$s" + "%1$s ja %2$d muuta" diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml index 737380057f..edfb59ebc4 100644 --- a/quickstep/res/values-fr-rCA/strings.xml +++ b/quickstep/res/values-fr-rCA/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Afficher # autre application.}one{Afficher # autre application.}other{Afficher # autres applications.}}" "{count,plural, =1{Afficher # appli de bureau.}one{Afficher # appli de bureau.}other{Afficher # applis de bureau.}}" "%1$s et %2$s" + "Bulle" + "Bulle à développer" + "%1$s de %2$s" + "%1$s et %2$d autres" diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml index f19a435e97..60f894469e 100644 --- a/quickstep/res/values-fr/strings.xml +++ b/quickstep/res/values-fr/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Afficher # autre appli}one{Afficher # autre appli}other{Afficher # autre applis}}" "{count,plural, =1{Afficher # application de bureau.}one{Afficher # application de bureau.}other{Afficher # applications de bureau.}}" "%1$s et %2$s" + "Bulle" + "Dépassement" + "%1$s (%2$s)" + "%1$s et %2$d autre(s)" diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml index 0afb2883ba..bf081d4e75 100644 --- a/quickstep/res/values-gl/strings.xml +++ b/quickstep/res/values-gl/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Mostrar # aplicación máis.}other{Mostrar # aplicacións máis.}}" "{count,plural, =1{Mostrar # aplicación para ordenadores.}other{Mostrar # aplicacións para ordenadores.}}" "%1$s e %2$s" + "Burbulla" + "Menú adicional" + "%1$s de %2$s" + "%1$s e %2$d máis" diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml index fd5dca6ca2..b20f77154c 100644 --- a/quickstep/res/values-gu/strings.xml +++ b/quickstep/res/values-gu/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{વધુ # ઍપ બતાવો.}one{વધુ # ઍપ બતાવો.}other{વધુ # ઍપ બતાવો.}}" "{count,plural, =1{# ડેસ્કટૉપ ઍપ બતાવો.}one{# ડેસ્કટૉપ ઍપ બતાવો.}other{# ડેસ્કટૉપ ઍપ બતાવો.}}" "%1$s અને %2$s" + "બબલ" + "ઓવરફ્લો" + "%2$sથી %1$s" + "%1$s અને વધુ %2$d" diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml index 246bdc71ef..a645186475 100644 --- a/quickstep/res/values-hi/strings.xml +++ b/quickstep/res/values-hi/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{# और ऐप्लिकेशन दिखाएं.}one{# और ऐप्लिकेशन दिखाएं.}other{# और ऐप्लिकेशन दिखाएं.}}" "{count,plural, =1{# डेस्कटॉप ऐप्लिकेशन दिखाएं.}one{# डेस्कटॉप ऐप्लिकेशन दिखाएं.}other{# डेस्कटॉप ऐप्लिकेशन दिखाएं.}}" "%1$s और %2$s" + "बबल" + "ओवरफ़्लो" + "%2$s की %1$s वाली सूचना" + "%1$s और %2$d अन्य" diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml index 0069edabbf..c96381d4a9 100644 --- a/quickstep/res/values-hr/strings.xml +++ b/quickstep/res/values-hr/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Prikaži još # aplikaciju}one{Prikaži još # aplikaciju}few{Prikaži još # aplikacije}other{Prikaži još # aplikacija}}" "{count,plural, =1{Prikaži # računalnu aplikaciju.}one{Prikaži # računalnu aplikaciju.}few{Prikaži # računalne aplikacije.}other{Prikaži # računalnih aplikacija.}}" "%1$s i %2$s" + "Oblačić" + "Dodatni izbornik" + "%1$s, %2$s" + "%1$s i još %2$d" diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml index 162337e388..03235aa5a6 100644 --- a/quickstep/res/values-hu/strings.xml +++ b/quickstep/res/values-hu/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{# további alkalmazás megjelenítése.}other{# további alkalmazás megjelenítése.}}" "{count,plural, =1{# asztali alkalmazás megjelenítése.}other{# asztali alkalmazás megjelenítése.}}" "%1$s és %2$s" + "Buborék" + "Túlcsordulás" + "%1$s, forrás: %2$s" + "%1$s és %2$d további" diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml index 8919191c81..3cb79904e4 100644 --- a/quickstep/res/values-hy/strings.xml +++ b/quickstep/res/values-hy/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Ցուցադրել ևս # հավելված։}one{Ցուցադրել ևս # հավելված։}other{Ցուցադրել ևս # հավելված։}}" "{count,plural, =1{Ցույց տալ # համակարգչային հավելված։}one{Ցույց տալ # համակարգչային հավելված։}other{Ցույց տալ # համակարգչային հավելված։}}" "%1$s և %2$s" + "Ամպիկ" + "Լրացուցիչ ընտրացանկ" + "%1$s՝ %2$s հավելվածից" + "%1$s ու ևս %2$d ամպիկ" diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml index ea62b4dd17..015b09e1c5 100644 --- a/quickstep/res/values-in/strings.xml +++ b/quickstep/res/values-in/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Tampilkan # aplikasi lainnya.}other{Tampilkan # aplikasi lainnya.}}" "{count,plural, =1{Tampilkan # aplikasi desktop.}other{Tampilkan # aplikasi desktop.}}" "%1$s dan %2$s" + "Balon" + "Tambahan" + "%1$s dari %2$s" + "%1$s dan %2$d lainnya" diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml index 3e204ce0a7..883fe82647 100644 --- a/quickstep/res/values-is/strings.xml +++ b/quickstep/res/values-is/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Sýna # forrit í viðbót.}one{Sýna # forrit í viðbót.}other{Sýna # forrit í viðbót.}}" "{count,plural, =1{Sýna # skjáborðsforrit.}one{Sýna # skjáborðsforrit.}other{Sýna # skjáborðsforrit.}}" "%1$s og %2$s" + "Blaðra" + "Yfirflæði" + "%1$s frá %2$s" + "%1$s og %2$d í viðbót" diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml index f295b0730f..59b195ae61 100644 --- a/quickstep/res/values-it/strings.xml +++ b/quickstep/res/values-it/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Mostra # altra app.}other{Mostra altre # app.}}" "{count,plural, =1{Mostra # app desktop.}other{Mostra # app desktop.}}" "%1$s e %2$s" + "Fumetto" + "Extra" + "%1$s da %2$s" + "%1$s e altri %2$d" diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml index 037deec6d9..9f0ed1453a 100644 --- a/quickstep/res/values-iw/strings.xml +++ b/quickstep/res/values-iw/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{הצגת אפליקציה אחת (#) נוספת.}one{הצגת # אפליקציות נוספות.}two{הצגת # אפליקציות נוספות.}other{הצגת # אפליקציות נוספות.}}" "{count,plural, =1{הצגת אפליקציה אחת (#) למחשב.}one{הצגת # אפליקציות למחשב.}two{הצגת # אפליקציות למחשב.}other{הצגת # אפליקציות למחשב.}}" "%1$s ו-%2$s" + "בועה" + "אפשרויות נוספות" + "‫%1$s מתוך %2$s" + "‫%1$s ועוד %2$d" diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml index ef335f2a16..4f1a162c3f 100644 --- a/quickstep/res/values-ja/strings.xml +++ b/quickstep/res/values-ja/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{他 # 件のアプリを表示できます。}other{他 # 件のアプリを表示できます。}}" "{count,plural, =1{# 個のデスクトップ アプリが表示されます。}other{# 個のデスクトップ アプリが表示されます。}}" "%1$s%2$s" + "ふきだし" + "オーバーフロー" + "%1$s%2$s)" + "%1$s、他 %2$d 件" diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml index 6da246ab9a..1fb60773eb 100644 --- a/quickstep/res/values-ka/strings.xml +++ b/quickstep/res/values-ka/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{#-ით მეტი აპის ჩენება}other{#-ით მეტი აპის ჩვენება.}}" "{count,plural, =1{# დესკტოპის აპის ჩვენება.}other{# დესკტოპის აპის ჩვენება.}}" "%1$s და %2$s" + "ბუშტი" + "გადავსება" + "%1$s: %2$s-იდან" + "%1$s და %2$d სხვა" diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml index 20a1d5945e..d83e2d346a 100644 --- a/quickstep/res/values-kk/strings.xml +++ b/quickstep/res/values-kk/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Тағы # қолданбаны көрсету.}other{Тағы # қолданбаны көрсету.}}" "{count,plural, =1{Компьютерге арналған # қолданбаны көрсету}other{Компьютерге арналған # қолданбаны көрсету}}" "%1$s және %2$s" + "Қалқыма терезе" + "Қосымша мәзір" + "%2$s ұсынатын %1$s" + "%1$s және тағы %2$d" diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml index 6d5e6f1492..5448433044 100644 --- a/quickstep/res/values-km/strings.xml +++ b/quickstep/res/values-km/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{បង្ហាញកម្មវិធី # ទៀត។}other{បង្ហាញ​កម្មវិធី # ទៀត។}}" "{count,plural, =1{បង្ហាញកម្មវិធី​កុំព្យូទ័រ #។}other{បង្ហាញកម្មវិធី​កុំព្យូទ័រ #។}}" "%1$s និង %2$s" + "ផ្ទាំងសារ" + "ម៉ឺនុយបន្ថែម" + "%1$s ពី %2$s" + "%1$s និង %2$d នាក់ទៀត" diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml index 10ab9224bf..74c77506fb 100644 --- a/quickstep/res/values-kn/strings.xml +++ b/quickstep/res/values-kn/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{ಇನ್ನೂ # ಆ್ಯಪ್ ಅನ್ನು ತೋರಿಸಿ.}one{ಇನ್ನೂ # ಆ್ಯಪ್‌ಗಳನ್ನು ತೋರಿಸಿ.}other{ಇನ್ನೂ # ಆ್ಯಪ್‌ಗಳನ್ನು ತೋರಿಸಿ.}}" "{count,plural, =1{# ಡೆಸ್ಕ್‌ಟಾಪ್ ಆ್ಯಪ್ ತೋರಿಸಿ.}one{# ಡೆಸ್ಕ್‌ಟಾಪ್ ಆ್ಯಪ್‌ಗಳನ್ನು ತೋರಿಸಿ.}other{# ಡೆಸ್ಕ್‌ಟಾಪ್ ಆ್ಯಪ್‌ಗಳನ್ನು ತೋರಿಸಿ.}}" "%1$s ಮತ್ತು %2$s" + "ಬಬಲ್" + "ಓವರ್‌ಫ್ಲೋ" + "%2$s ನಿಂದ %1$s" + "%1$s ಮತ್ತು ಇನ್ನೂ %2$d" diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml index 24934f9fef..1f4275a2d7 100644 --- a/quickstep/res/values-ko/strings.xml +++ b/quickstep/res/values-ko/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{앱 #개 더 표시}other{앱 #개 더 표시}}" "{count,plural, =1{데스크톱 앱 #개를 표시합니다.}other{데스크톱 앱 #개를 표시합니다.}}" "%1$s%2$s" + "풍선" + "오버플로" + "%2$s%1$s" + "%1$s%2$d개" diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml index 525a248499..f0d2af8935 100644 --- a/quickstep/res/values-ky/strings.xml +++ b/quickstep/res/values-ky/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Дагы # колдонмону көрсөтүү.}other{Дагы # колдонмону көрсөтүү.}}" "{count,plural, =1{# иш такта колдонмосун көрсөтүү.}other{# иш такта колдонмосун көрсөтүү.}}" "%1$s жана %2$s" + "Көбүкчө" + "Кошумча меню" + "%2$s колдонмосунан %1$s" + "%1$s жана дагы %2$d" diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml index b5a7ebbf72..f54c7121f5 100644 --- a/quickstep/res/values-lo/strings.xml +++ b/quickstep/res/values-lo/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{ສະແດງອີກ # ແອັບ.}other{ສະແດງອີກ # ແອັບ.}}" "{count,plural, =1{ສະແດງແອັບເດັສທັອບ # ລາຍການ.}other{ສະແດງແອັບເດັສທັອບ # ລາຍການ.}}" "%1$s ແລະ %2$s" + "ຟອງ" + "ລາຍການເພີ່ມເຕີມ" + "%1$s ຈາກ %2$s" + "%1$s ແລະ ອີກ %2$d ລາຍການ" diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml index 4817e25adb..554745e732 100644 --- a/quickstep/res/values-lt/strings.xml +++ b/quickstep/res/values-lt/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Rodyti dar # programą.}one{Rodyti dar # programą.}few{Rodyti dar # programas.}many{Rodyti dar # programos.}other{Rodyti dar # programų.}}" "{count,plural, =1{Rodyti # darbalaukio programą.}one{Rodyti # darbalaukio programą.}few{Rodyti # darbalaukio programas.}many{Rodyti # darbalaukio programos.}other{Rodyti # darbalaukio programų.}}" "„%1$s“ ir „%2$s“" + "Burbulas" + "Perpildymas" + "„%1$s“ iš „%2$s“" + "„%1$s“ ir dar %2$d" diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml index aa80b7e6ba..a6a0dab930 100644 --- a/quickstep/res/values-lv/strings.xml +++ b/quickstep/res/values-lv/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Rādīt vēl # lietotni}zero{Rādīt vēl # lietotnes}one{Rādīt vēl # lietotni}other{Rādīt vēl # lietotnes}}" "{count,plural, =1{Rādīt # datora lietotni.}zero{Rādīt # datora lietotnes.}one{Rādīt # datora lietotni.}other{Rādīt # datora lietotnes.}}" "“%1$s” un “%2$s”" + "Burbulis" + "Pārpilde" + "%1$s no lietotnes %2$s" + "%1$s un vēl %2$d" diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml index 039ee9f0fb..4859055e94 100644 --- a/quickstep/res/values-mk/strings.xml +++ b/quickstep/res/values-mk/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Прикажи уште # апликација.}one{Прикажи уште # апликација.}other{Прикажи уште # апликации.}}" "{count,plural, =1{Прикажи # апликација за компјутер.}one{Прикажи # апликација за компјутер.}other{Прикажи # апликации за компјутер.}}" "%1$s и %2$s" + "Балонче" + "Проширено балонче" + "%1$s од %2$s" + "%1$s и уште %2$d" diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml index 8fbc8f7100..85b093d6fa 100644 --- a/quickstep/res/values-ml/strings.xml +++ b/quickstep/res/values-ml/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{# ആപ്പ് കൂടി കാണിക്കുക.}other{# ആപ്പുകൾ കൂടി കാണിക്കുക.}}" "{count,plural, =1{# ഡെസ്‌ക്ടോപ്പ് ആപ്പ് കാണിക്കുക.}other{# ഡെസ്‌ക്ടോപ്പ് ആപ്പുകൾ കാണിക്കുക.}}" "%1$s, %2$s" + "ബബിൾ" + "ഓവർഫ്ലോ" + "%2$s എന്നതിൽ നിന്നുള്ള %1$s" + "%1$s എന്നതും മറ്റ് %2$d എണ്ണവും" diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml index d5e41057e3..7a4c7e9cd6 100644 --- a/quickstep/res/values-mn/strings.xml +++ b/quickstep/res/values-mn/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Өөр # аппыг харуулна уу.}other{Өөр # аппыг харуулна уу.}}" "{count,plural, =1{Компьютерын # аппыг харуулна уу.}other{Компьютерын # аппыг харуулна уу.}}" "%1$s болон %2$s" + "Бөмбөлөг" + "Урт цэс" + "%2$s-с ирсэн %1$s" + "%1$s болон бусад %2$d" diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml index cbce562153..b053a216a7 100644 --- a/quickstep/res/values-mr/strings.xml +++ b/quickstep/res/values-mr/strings.xml @@ -95,7 +95,7 @@ "सिस्टीम नेव्हिगेशन सेटिंग्ज" "शेअर करा" "स्क्रीनशॉट" - "स्प्लिट" + "स्प्लिट करा" "ॲपची जोडी सेव्ह करा" "स्प्लिट स्क्रीन वापरण्यासाठी दुसऱ्या ॲपवर टॅप करा" "स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप निवडा" @@ -140,4 +140,8 @@ "{count,plural, =1{आणखी # अ‍ॅप दाखवा.}other{आणखी # अ‍ॅप्स दाखवा.}}" "{count,plural, =1{# डेस्‍कटॉप अ‍ॅप दाखवा.}other{# डेस्‍कटॉप अ‍ॅप्स दाखवा.}}" "%1$s आणि %2$s" + "बबल" + "ओव्हरफ्लो" + "%2$s वरील %1$s" + "%1$s आणि आणखी %2$d" diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml index cc1ecdd7c1..c0219e04fc 100644 --- a/quickstep/res/values-ms/strings.xml +++ b/quickstep/res/values-ms/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Tunjukkan # lagi apl.}other{Tunjukkan # lagi apl.}}" "{count,plural, =1{Tunjukkan # apl desktop.}other{Tunjukkan # apl desktop.}}" "%1$s dan %2$s" + "Gelembung" + "Limpahan" + "%1$s daripada %2$s" + "%1$s dan %2$d lagi" diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml index 0b3a708e38..7c7ff82b85 100644 --- a/quickstep/res/values-my/strings.xml +++ b/quickstep/res/values-my/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{နောက်ထပ်အက်ပ် # ခု ပြပါ။}other{နောက်ထပ်အက်ပ် # ခု ပြပါ။}}" "{count,plural, =1{ဒက်စတော့ အက်ပ် # ခု ပြပါ။}other{ဒက်စတော့ အက်ပ် # ခု ပြပါ။}}" "%1$s နှင့် %2$s" + "ပူဖောင်းကွက်" + "မီနူးအပို" + "%2$s မှ %1$s" + "%1$s နှင့် နောက်ထပ် %2$d ခု" diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml index 655ffecc02..dd3d16e18a 100644 --- a/quickstep/res/values-nb/strings.xml +++ b/quickstep/res/values-nb/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Vis # app til.}other{Vis # apper til.}}" "{count,plural, =1{Vis # datamaskinprogram.}other{Vis # datamaskinprogrammer.}}" "%1$s og %2$s" + "Boble" + "Overflyt" + "%1$s fra %2$s" + "%1$s og %2$d andre" diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml index 3554aa5c10..d49fd2d53e 100644 --- a/quickstep/res/values-ne/strings.xml +++ b/quickstep/res/values-ne/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{थप # एप देखाउनुहोस्।}other{थप # वटा एप देखाउनुहोस्।}}" "{count,plural, =1{# डेस्कटप एप देखाउनुहोस्।}other{# वटा डेस्कटप एप देखाउनुहोस्।}}" "%1$s%2$s" + "बबल" + "ओभरफ्लो" + "%2$s मा देखाइएका %1$s" + "%1$s र थप %2$d" diff --git a/quickstep/res/values-night/colors.xml b/quickstep/res/values-night/colors.xml index 8d03ce6f96..94100ba47a 100644 --- a/quickstep/res/values-night/colors.xml +++ b/quickstep/res/values-night/colors.xml @@ -22,7 +22,7 @@ #202124 #3c4043 - #FF000000 + @android:color/system_neutral1_900 ?androidprv:attr/colorAccentSecondaryVariant diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml index c5cebdb71a..ca44a6914c 100644 --- a/quickstep/res/values-nl/strings.xml +++ b/quickstep/res/values-nl/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Nog # app tonen.}other{Nog # apps tonen.}}" "{count,plural, =1{# desktop-app tonen.}other{# desktop-apps tonen.}}" "%1$s en %2$s" + "Bubbel" + "Overloop" + "%1$s van %2$s" + "%1$s en nog %2$d" diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml index 765935e408..bf0bdc8323 100644 --- a/quickstep/res/values-or/strings.xml +++ b/quickstep/res/values-or/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{ଅଧିକ #ଟି ଆପ ଦେଖାନ୍ତୁ।}other{ଅଧିକ #ଟି ଆପ୍ସ ଦେଖାନ୍ତୁ।}}" "{count,plural, =1{# ଡେସ୍କଟପ ଆପ ଦେଖାନ୍ତୁ।}other{# ଡେସ୍କଟପ ଆପ୍ସ ଦେଖାନ୍ତୁ।}}" "%1$s ଏବଂ %2$s" + "ବବଲ" + "ଓଭରଫ୍ଲୋ" + "%2$sରୁ %1$s" + "%1$s ଏବଂ ଅଧିକ %2$d" diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml index 769608b0ca..fc603969a7 100644 --- a/quickstep/res/values-pa/strings.xml +++ b/quickstep/res/values-pa/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{# ਹੋਰ ਐਪ ਦਿਖਾਓ।}one{# ਹੋਰ ਐਪ ਦਿਖਾਓ।}other{# ਹੋਰ ਐਪਾਂ ਦਿਖਾਓ।}}" "{count,plural, =1{# ਡੈਸਕਟਾਪ ਐਪ ਦਿਖਾਓ।}one{# ਡੈਸਕਟਾਪ ਐਪ ਦਿਖਾਓ।}other{# ਡੈਸਕਟਾਪ ਐਪਾਂ ਦਿਖਾਓ।}}" "%1$s ਅਤੇ %2$s" + "ਬਬਲ" + "ਓਵਰਫ਼ਲੋ" + "%2$s ਤੋਂ %1$s" + "%1$s ਅਤੇ %2$d ਹੋਰ" diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml index 752764fbdc..d88e28ac3b 100644 --- a/quickstep/res/values-pl/strings.xml +++ b/quickstep/res/values-pl/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Pokaż jeszcze # aplikację.}few{Pokaż jeszcze # aplikacje.}many{Pokaż jeszcze # aplikacji.}other{Pokaż jeszcze # aplikacji.}}" "{count,plural, =1{Pokaż # aplikację komputerową.}few{Pokaż # aplikacje komputerowe.}many{Pokaż # aplikacji komputerowych.}other{Pokaż # aplikacji komputerowej.}}" "%1$s%2$s" + "Dymek" + "Rozwijany" + "%1$s z aplikacji %2$s" + "%1$s i jeszcze %2$d" diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml index a692e69b3e..47498ccb82 100644 --- a/quickstep/res/values-pt-rPT/strings.xml +++ b/quickstep/res/values-pt-rPT/strings.xml @@ -57,7 +57,7 @@ "Deslize rapidamente com o dedo para retroceder" "Para voltar ao último ecrã, deslize rapidamente do limite esquerdo ou direito até ao centro do ecrã." "Para voltar ao último ecrã, deslize rapidamente com 2 dedos a partir da extremidade esquerda ou direita até ao centro do ecrã." - "Voltar" + "Retroceder" "Deslize rapidamente a partir da extremidade esquerda ou direita para o meio do ecrã" "Deslize rapidamente com o dedo a partir do limite inferior do ecrã" "Não faça uma pausa antes de soltar" @@ -140,4 +140,8 @@ "{count,plural, =1{Mostrar mais # app.}other{Mostrar mais # apps.}}" "{count,plural, =1{Mostrar # app para computador.}other{Mostrar # apps para computador.}}" "%1$s e %2$s" + "Balão" + "Menu adicional" + "%1$s da app %2$s" + "%1$s e mais %2$d pessoas" diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml index dd3c037829..4fec4f8511 100644 --- a/quickstep/res/values-pt/strings.xml +++ b/quickstep/res/values-pt/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Mostrar mais # app.}one{Mostrar mais # app.}other{Mostrar mais # apps.}}" "{count,plural, =1{Mostrar # app para computador.}one{Mostrar # app para computador.}other{Mostrar # apps para computador.}}" "%1$s e %2$s" + "Balão" + "Balão flutuante" + "%1$s do app %2$s" + "%1$s e mais %2$d" diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml index 7efeda27fb..c839602e89 100644 --- a/quickstep/res/values-ro/strings.xml +++ b/quickstep/res/values-ro/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Afișează încă # aplicație}few{Afișează încă # aplicații}other{Afișează încă # de aplicații}}" "{count,plural, =1{Afișează # aplicație pentru computer.}few{Afișează # aplicații pentru computer.}other{Afișează # de aplicații pentru computer.}}" "%1$s și %2$s" + "Balon" + "Suplimentar" + "%1$s de la %2$s" + "%1$s și încă %2$d" diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml index e8d1ddb441..da49ad32b8 100644 --- a/quickstep/res/values-ru/strings.xml +++ b/quickstep/res/values-ru/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Показать ещё # приложение}one{Показать ещё # приложение}few{Показать ещё # приложения}many{Показать ещё # приложений}other{Показать ещё # приложения}}" "{count,plural, =1{Показать # компьютерное приложение.}one{Показать # компьютерное приложение.}few{Показать # компьютерных приложения.}many{Показать # компьютерных приложений.}other{Показать # компьютерного приложения.}}" "%1$s и %2$s" + "Всплывающая подсказка" + "Дополнительное меню" + "\"%1$s\" из приложения \"%2$s\"" + "%1$s и ещё %2$d" diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml index 9d1c3121ae..9cbe837f94 100644 --- a/quickstep/res/values-si/strings.xml +++ b/quickstep/res/values-si/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{තවත් # යෙදුමක් පෙන්වන්න.}one{තවත් යෙදුම් #ක් පෙන්වන්න.}other{තවත් යෙදුම් #ක් පෙන්වන්න.}}" "{count,plural, =1{# ඩෙස්ක්ටොප් යෙදුමක් පෙන්වන්න.}one{ඩෙස්ක්ටොප් යෙදුම් # ක් පෙන්වන්න.}other{ඩෙස්ක්ටොප් යෙදුම් # ක් පෙන්වන්න.}}" "%1$s සහ %2$s" + "බුබුළු" + "පිටාර යාම" + "%2$s සිට %1$s" + "%1$s හා තව %2$dක්" diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml index a573b4569a..3eca787fed 100644 --- a/quickstep/res/values-sk/strings.xml +++ b/quickstep/res/values-sk/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Zobraziť # ďalšiu aplikáciu.}few{Zobraziť # ďalšie aplikácie.}many{Show # more apps.}other{Zobraziť # ďalších aplikácií.}}" "{count,plural, =1{Zobraziť # aplikáciu pre počítač.}few{Zobraziť # aplikácie pre počítač.}many{Show # desktop apps.}other{Zobraziť # aplikácií pre počítač.}}" "%1$s%2$s" + "Bublina" + "Rozbaľovacia ponuka" + "%1$s z aplikácie %2$s" + "%1$s a ešte %2$d" diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml index 355dc61464..52faeb7a9e 100644 --- a/quickstep/res/values-sl/strings.xml +++ b/quickstep/res/values-sl/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Pokaži še # aplikacijo.}one{Pokaži še # aplikacijo.}two{Pokaži še # aplikaciji.}few{Pokaži še # aplikacije.}other{Pokaži še # aplikacij.}}" "{count,plural, =1{Prikaz # aplikacije za namizni računalnik.}one{Prikaz # aplikacije za namizni računalnik.}two{Prikaz # aplikacij za namizni računalnik.}few{Prikaz # aplikacij za namizni računalnik.}other{Prikaz # aplikacij za namizni računalnik.}}" "%1$s in %2$s" + "Oblaček" + "Oblaček z dodatnimi elementi" + "%1$s iz aplikacije %2$s" + "%1$s in še %2$d" diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml index 71eca07288..cdb9cf9a7c 100644 --- a/quickstep/res/values-sq/strings.xml +++ b/quickstep/res/values-sq/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Shfaq # aplikacion tjetër.}other{Shfaq # aplikacione të tjera.}}" "{count,plural, =1{Shfaq # aplikacion për desktop.}other{Shfaq # aplikacione për desktop.}}" "%1$s dhe %2$s" + "Flluska" + "Tejkalimi" + "\"%1$s\" nga %2$s" + "\"%1$s\" dhe %2$d të tjera" diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml index 34bcb1c28e..7456a36b7e 100644 --- a/quickstep/res/values-sr/strings.xml +++ b/quickstep/res/values-sr/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Прикажи још # апликацију.}one{Прикажи још # апликацију.}few{Прикажи још # апликације.}other{Прикажи још # апликација.}}" "{count,plural, =1{Прикажи # апликацију за рачунаре.}one{Прикажи # апликацију за рачунаре.}few{Прикажи # апликације за рачунаре.}other{Прикажи # апликација за рачунаре.}}" "%1$s и %2$s" + "Облачић" + "Преклопни" + "%1$s%2$s" + "%1$s и још %2$d" diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml index 723cb82797..f369daeb33 100644 --- a/quickstep/res/values-sv/strings.xml +++ b/quickstep/res/values-sv/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Visa # app till.}other{Visa # appar till.}}" "{count,plural, =1{Visa # datorapp.}other{Visa # datorappar.}}" "%1$s och %2$s" + "Bubbla" + "Fler alternativ" + "%1$s från %2$s" + "%1$s och %2$d till" diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml index 6a3f624576..3d8277b448 100644 --- a/quickstep/res/values-sw/strings.xml +++ b/quickstep/res/values-sw/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Onyesha programu # zaidi.}other{Onyesha programu # zaidi.}}" "{count,plural, =1{Onyesha programu # ya kompyuta ya mezani.}other{Onyesha programu # za kompyuta ya mezani.}}" "%1$s na %2$s" + "Kiputo" + "Kiputo cha vipengee vya ziada" + "%1$s kutoka %2$s" + "%1$s na vingine %2$d" diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml index f9528b3632..e24d8fea79 100644 --- a/quickstep/res/values-sw600dp/dimens.xml +++ b/quickstep/res/values-sw600dp/dimens.xml @@ -45,4 +45,8 @@ 38sp 15sp + + + 300dp + diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml index 90ff5eaa0c..ed3ebeedaa 100644 --- a/quickstep/res/values-ta/strings.xml +++ b/quickstep/res/values-ta/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{மேலும் # ஆப்ஸைக் காட்டு.}other{மேலும் # ஆப்ஸைக் காட்டு.}}" "{count,plural, =1{# டெஸ்க்டாப் ஆப்ஸைக் காட்டு.}other{# டெஸ்க்டாப் ஆப்ஸைக் காட்டு.}}" "%1$s மற்றும் %2$s" + "குமிழ்" + "கூடுதல் விருப்பங்களைக் காட்டும்" + "%2$s வழங்கும் %1$s" + "%1$s மற்றும் %2$d" diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml index c32f360592..a4e1cbf09c 100644 --- a/quickstep/res/values-te/strings.xml +++ b/quickstep/res/values-te/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{మరో # యాప్‌ను చూడండి.}other{మరో # యాప్‌లను చూడండి.}}" "{count,plural, =1{# డెస్క్‌టాప్ యాప్‌ను చూపండి.}other{# డెస్క్‌టాప్ యాప్‌లను చూపండి.}}" "%1$s, %2$s" + "బబుల్" + "ఓవర్‌ఫ్లో" + "%2$s నుండి %1$s" + "%1$s, మరో %2$d" diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml index f1fd49b25b..1bbb137a1b 100644 --- a/quickstep/res/values-th/strings.xml +++ b/quickstep/res/values-th/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{แสดงเพิ่มเติมอีก # แอป}other{แสดงเพิ่มเติมอีก # แอป}}" "{count,plural, =1{แสดงแอปบนเดสก์ท็อป # รายการ}other{แสดงแอปบนเดสก์ท็อป # รายการ}}" "%1$s และ %2$s" + "บับเบิล" + "การดำเนินการเพิ่มเติม" + "%1$s จาก %2$s" + "%1$s และอีก %2$d รายการ" diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml index f0e231166d..978a5a37f8 100644 --- a/quickstep/res/values-tl/strings.xml +++ b/quickstep/res/values-tl/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Magpakita ng # pang app.}one{Magpakita ng # pang app.}other{Magpakita ng # pang app.}}" "{count,plural, =1{Ipakita ang # desktop app.}one{Ipakita ang # desktop app.}other{Ipakita ang # na desktop app.}}" "%1$s at %2$s" + "Bubble" + "Overflow" + "%1$s mula sa %2$s" + "%1$s at %2$d pa" diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml index 5eb84d65f3..0cc5d7f856 100644 --- a/quickstep/res/values-tr/strings.xml +++ b/quickstep/res/values-tr/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{# uygulama daha göster.}other{# uygulama daha göster}}" "{count,plural, =1{# masaüstü uygulamasını göster.}other{# masaüstü uygulamasını göster.}}" "%1$s ve %2$s" + "Balon" + "Taşma" + "%2$s uygulamasından %1$s" + "%1$s ve %2$d tane daha" diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml index 3be16279ed..9c706a8403 100644 --- a/quickstep/res/values-uk/strings.xml +++ b/quickstep/res/values-uk/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Показати ще # додаток.}one{Показати ще # додаток.}few{Показати ще # додатки.}many{Показати ще # додатків.}other{Показати ще # додатка.}}" "{count,plural, =1{Показати # комп’ютерну програму.}one{Показати # комп’ютерну програму.}few{Показати # комп’ютерні програми.}many{Показати # комп’ютерних програм.}other{Показати # комп’ютерної програми.}}" "%1$s та %2$s" + "Повідомлення" + "Додаткове повідомлення" + "%1$s з додатка %2$s" + "%1$s і ще %2$d" diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml index 0e951f074b..e12524868f 100644 --- a/quickstep/res/values-ur/strings.xml +++ b/quickstep/res/values-ur/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{# مزید ایپ دکھائیں۔}other{# مزید ایپس دکھائیں۔}}" "{count,plural, =1{# ڈیسک ٹاپ ایپ دکھائیں۔}other{# ڈیسک ٹاپ ایپس دکھائیں۔}}" "%1$s اور %2$s" + "ببل" + "اوورفلو" + "%2$s سے %1$s" + "%1$s اور %2$d مزید" diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml index 4d57b2248d..3f4f981095 100644 --- a/quickstep/res/values-uz/strings.xml +++ b/quickstep/res/values-uz/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Yana # ta ilovani chiqarish}other{Yana # ta ilovani chiqarish}}" "{count,plural, =1{# ta desktop ilovani chiqarish.}other{# ta desktop ilovani chiqarish.}}" "%1$s va %2$s" + "Pufak" + "Kengaytirish" + "%1$s (%2$s)" + "%1$s va yana %2$d kishi" diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml index df8ccdca0c..9bc526faac 100644 --- a/quickstep/res/values-vi/strings.xml +++ b/quickstep/res/values-vi/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Hiện thêm # ứng dụng.}other{Hiện thêm # ứng dụng.}}" "{count,plural, =1{Hiện # ứng dụng dành cho máy tính.}other{Hiện # ứng dụng dành cho máy tính.}}" "%1$s%2$s" + "Bong bóng" + "Bong bóng bổ sung" + "%1$s từ %2$s" + "%1$s%2$d bong bóng khác" diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml index e7507308c3..a89227e17e 100644 --- a/quickstep/res/values-zh-rCN/strings.xml +++ b/quickstep/res/values-zh-rCN/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{显示另外 # 个应用。}other{显示另外 # 个应用。}}" "{count,plural, =1{显示 # 款桌面应用。}other{显示 # 款桌面应用。}}" "%1$s%2$s" + "气泡框" + "菜单" + "来自“%2$s”的%1$s" + "%1$s以及另外 %2$d 个" diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml index 9427b07e9a..b9d8eb765a 100644 --- a/quickstep/res/values-zh-rHK/strings.xml +++ b/quickstep/res/values-zh-rHK/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{顯示另外 # 個應用程式。}other{顯示另外 # 個應用程式。}}" "{count,plural, =1{顯示 # 個桌面應用程式。}other{顯示 # 個桌面應用程式。}}" "「%1$s」和「%2$s」" + "對話氣泡" + "展開式" + "%2$s 的「%1$s」通知" + "%1$s和其他 %2$d 則通知" diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml index 3b06edad15..90140cb587 100644 --- a/quickstep/res/values-zh-rTW/strings.xml +++ b/quickstep/res/values-zh-rTW/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{再多顯示 # 個應用程式。}other{再多顯示 # 個應用程式。}}" "{count,plural, =1{顯示 # 個電腦版應用程式。}other{顯示 # 個電腦版應用程式。}}" "「%1$s」和「%2$s」" + "泡泡" + "溢位" + "「%2$s」的「%1$s」通知" + "%1$s和另外 %2$d 則通知" diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml index cb216fc6a6..73be445a55 100644 --- a/quickstep/res/values-zu/strings.xml +++ b/quickstep/res/values-zu/strings.xml @@ -140,4 +140,8 @@ "{count,plural, =1{Bonisa i-app e-# ngaphezulu.}one{Bonisa ama-app angu-# ngaphezulu.}other{Bonisa ama-app angu-# ngaphezulu.}}" "{count,plural, =1{Bonisa i-app engu-# yedeskithophu.}one{Bonisa ama-app angu-# wedeskithophu.}other{Bonisa ama-app angu-# wedeskithophu.}}" "I-%1$s ne-%2$s" + "Ibhamuza" + "Ukugcwala kakhulu" + "%1$s kusuka ku-%2$s" + "%1$s nokunye okungu-%2$d" diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml index 14a916f68b..0f997f9b77 100644 --- a/quickstep/res/values/colors.xml +++ b/quickstep/res/values/colors.xml @@ -76,7 +76,7 @@ #80868b #bdc1c6 - #FFFFFFFF + @android:color/system_neutral1_50 #333333 diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 08d36d8d87..5c82c991c7 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -367,6 +367,7 @@ 150dp 24dp 40dp + 1dp 10dp 32dp 8dp @@ -384,6 +385,11 @@ 24dp + + + -288dp + 80 + 4.5 10 @@ -487,4 +493,11 @@ 24dp 8dp + + 48dp + + + + 220dp + diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index cf987c3db3..340d25b0f9 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -332,4 +332,14 @@ %1$s and %2$s + + + + Bubble + + Overflow + + %1$s from %2$s + + %1$s and %2$d more diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 0c499b8bc6..fae281a70a 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -151,6 +151,7 @@ import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig; import com.android.quickstep.util.RectFSpringAnim.TaskbarHotseatSpringConfig; +import com.android.quickstep.util.ScalingWorkspaceRevealAnim; import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; @@ -174,7 +175,6 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; -import java.util.Map.Entry; /** * Manages the opening and closing app transitions from Launcher @@ -1630,10 +1630,15 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets)); } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get() && !playFallBackAnimation) { - // Use a fixed velocity to start the animation. - float velocityPxPerS = DynamicResource.provider(mLauncher) - .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); - PointF velocity = new PointF(0, -velocityPxPerS); + PointF velocity; + if (enableScalingRevealHomeAnimation()) { + velocity = new PointF(); + } else { + // Use a fixed velocity to start the animation. + float velocityPxPerS = DynamicResource.provider(mLauncher) + .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); + velocity = new PointF(0, -velocityPxPerS); + } rectFSpringAnim = getClosingWindowAnimators( anim, appTargets, launcherView, velocity, startRect, startWindowCornerRadius); @@ -1642,8 +1647,15 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // layout bounds. skipAllAppsScale = true; } else if (!fromPredictiveBack) { - anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, - true /* animateOverviewScrim */, launcherView).getAnimators()); + if (enableScalingRevealHomeAnimation()) { + anim.play( + new ScalingWorkspaceRevealAnim( + mLauncher, rectFSpringAnim, + rectFSpringAnim.getTargetRect()).getAnimators()); + } else { + anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, + true /* animateOverviewScrim */, launcherView).getAnimators()); + } if (!areAllTargetsTranslucent(appTargets)) { anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController().stateDepth, diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java index 23cb8e9baa..943c08c786 100644 --- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java +++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java @@ -194,6 +194,11 @@ public class WidgetPickerActivity extends BaseActivity { return false; } + View dragView = widgetCell.getDragAndDropView(); + if (dragView == null) { + return false; + } + ClipData clipData = new ClipData( new ClipDescription( /* label= */ "", // not displayed anywhere; so, set to empty. @@ -209,9 +214,9 @@ public class WidgetPickerActivity extends BaseActivity { .putExtra(EXTRA_IS_PENDING_WIDGET_DRAG, true)); // DRAG_FLAG_GLOBAL permits dragging data beyond app window. - return view.startDragAndDrop( + return dragView.startDragAndDrop( clipData, - new View.DragShadowBuilder(view), + new View.DragShadowBuilder(dragView), /* myLocalState= */ null, View.DRAG_FLAG_GLOBAL ); diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt index 3ef8e546e0..9178062c2d 100644 --- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt +++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt @@ -30,11 +30,12 @@ import com.android.launcher3.util.Executors.MAIN_EXECUTOR import com.android.quickstep.SystemUiProxy import com.android.quickstep.TaskViewUtils import com.android.quickstep.views.DesktopTaskView +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource import java.util.function.Consumer /** Manage recents related operations with desktop tasks */ class DesktopRecentsTransitionController( - private val stateManager: StateManager<*>, + private val stateManager: StateManager<*, *>, private val systemUiProxy: SystemUiProxy, private val appThread: IApplicationThread, private val depthController: DepthController? @@ -57,13 +58,13 @@ class DesktopRecentsTransitionController( } /** Launch desktop tasks from recents view */ - fun moveToDesktop(taskId: Int) { - systemUiProxy.moveToDesktop(taskId) + fun moveToDesktop(taskId: Int, transitionSource: DesktopModeTransitionSource) { + systemUiProxy.moveToDesktop(taskId, transitionSource) } private class RemoteDesktopLaunchTransitionRunner( private val desktopTaskView: DesktopTaskView, - private val stateManager: StateManager<*>, + private val stateManager: StateManager<*, *>, private val depthController: DepthController?, private val successCallback: Consumer? ) : RemoteTransitionStub() { diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java index 39f2c00c99..64bb05e1ab 100644 --- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java +++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java @@ -19,6 +19,7 @@ import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION; import android.app.prediction.AppTarget; +import android.content.ComponentName; import android.content.Context; import android.text.TextUtils; @@ -29,7 +30,6 @@ import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.QuickstepModelDelegate.PredictorState; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.picker.WidgetRecommendationCategoryProvider; @@ -64,36 +64,24 @@ public final class WidgetsPredictionUpdateTask implements ModelUpdateTask { widget -> new ComponentKey(widget.providerName, widget.user)).collect( Collectors.toSet()); Predicate notOnWorkspace = w -> !widgetsInWorkspace.contains(w); - Map> allWidgets = - dataModel.widgetsModel.getAllWidgetsWithoutShortcuts(); + Map allWidgets = + dataModel.widgetsModel.getAllWidgetComponentsWithoutShortcuts(); List servicePredictedItems = new ArrayList<>(); - List localFilteredWidgets = new ArrayList<>(); for (AppTarget app : mTargets) { - PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser()); - List widgets = allWidgets.get(packageUserKey); - if (widgets == null || widgets.isEmpty()) { + ComponentKey componentKey = new ComponentKey( + new ComponentName(app.getPackageName(), app.getClassName()), app.getUser()); + WidgetItem widget = allWidgets.get(componentKey); + if (widget == null) { continue; } String className = app.getClassName(); if (!TextUtils.isEmpty(className)) { - WidgetItem item = widgets.stream() - .filter(w -> className.equals(w.componentName.getClassName())) - .filter(notOnWorkspace) - .findFirst() - .orElse(null); - if (item != null) { - servicePredictedItems.add(item); - continue; + if (notOnWorkspace.test(widget)) { + servicePredictedItems.add(widget); } } - // No widget was added by the service, try local filtering - widgets.stream().filter(notOnWorkspace).findFirst() - .ifPresent(localFilteredWidgets::add); - } - if (servicePredictedItems.isEmpty()) { - servicePredictedItems.addAll(localFilteredWidgets); } List items; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 0de0550016..6b62c86792 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -41,8 +41,6 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; -import static com.android.window.flags.Flags.enableDesktopWindowingMode; -import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps; import static com.android.wm.shell.Flags.enableTinyTaskbar; import android.animation.AnimatorSet; @@ -64,7 +62,6 @@ import android.provider.Settings; import android.util.Log; import android.view.Display; import android.view.Gravity; -import android.view.RoundedCorner; import android.view.Surface; import android.view.View; import android.view.WindowInsets; @@ -168,7 +165,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { private final TaskbarControllers mControllers; private final WindowManager mWindowManager; - private final @Nullable RoundedCorner mLeftCorner, mRightCorner; private DeviceProfile mDeviceProfile; private WindowManager.LayoutParams mWindowLayoutParams; private boolean mIsFullscreen; @@ -229,16 +225,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { Context c = getApplicationContext(); mWindowManager = c.getSystemService(WindowManager.class); - boolean phoneMode = isPhoneMode(); - mLeftCorner = phoneMode - ? null - : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); - mRightCorner = phoneMode - ? null - : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); - // Inflate views. - int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode + int taskbarLayout = DisplayController.isTransientTaskbar(this) && !isPhoneMode() ? R.layout.transient_taskbar : R.layout.taskbar; mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false); @@ -304,7 +292,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext { new VoiceInteractionWindowController(this), new TaskbarTranslationController(this), new TaskbarSpringOnStashController(this), - createTaskbarRecentAppsController(), + new TaskbarRecentAppsController( + RecentsModel.INSTANCE.get(this), + LauncherActivityInterface.INSTANCE::getDesktopVisibilityController), TaskbarEduTooltipController.newInstance(this), new KeyboardQuickSwitchController(), new TaskbarPinningController(this, () -> @@ -314,16 +304,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mLauncherPrefs = LauncherPrefs.get(this); } - private TaskbarRecentAppsController createTaskbarRecentAppsController() { - // TODO(b/335401172): unify DesktopMode checks in Launcher - if (enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps()) { - return new DesktopTaskbarRunningAppsController( - RecentsModel.INSTANCE.get(this), - LauncherActivityInterface.INSTANCE::getDesktopVisibilityController); - } - return TaskbarRecentAppsController.DEFAULT; - } - /** Updates {@link DeviceProfile} instances for any Taskbar windows. */ public void updateDeviceProfile(DeviceProfile launcherDp) { applyDeviceProfile(launcherDp); @@ -625,12 +605,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mImeDrawsImeNavBar; } - public int getLeftCornerRadius() { - return mLeftCorner == null ? 0 : mLeftCorner.getRadius(); - } - - public int getRightCornerRadius() { - return mRightCorner == null ? 0 : mRightCorner.getRadius(); + public int getCornerRadius() { + return isPhoneMode() ? 0 : getResources().getDimensionPixelSize( + R.dimen.persistent_taskbar_corner_radius); } public WindowManager.LayoutParams getWindowLayoutParams() { @@ -1023,7 +1000,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mDeviceProfile.taskbarHeight - + Math.max(getLeftCornerRadius(), getRightCornerRadius()) + + getCornerRadius() + extraHeightForTaskbarTooltips; } @@ -1405,6 +1382,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.taskbarStashController.toggleTaskbarStash(); } + /** + * Plays the taskbar background alpha animation if one is not currently playing. + */ + public void playTaskbarBackgroundAlphaAnimation() { + mControllers.taskbarStashController.playTaskbarBackgroundAlphaAnimation(); + } + /** * Called to start the taskbar translation spring to its settled translation (0). */ @@ -1605,4 +1589,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext { boolean canToggleHomeAllApps() { return mControllers.uiController.canToggleHomeAllApps(); } + + @VisibleForTesting + public TaskbarControllers getControllers() { + return mControllers; + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt index bafd05924f..2737cbd467 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt @@ -37,8 +37,6 @@ import kotlin.math.min class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { private val isInSetup: Boolean = !context.isUserSetupComplete - private val DARK_THEME_SHADOW_ALPHA = 51f - private val LIGHT_THEME_SHADOW_ALPHA = 25f private val maxTransientTaskbarHeight = context.transientTaskbarDeviceProfile.taskbarHeight.toFloat() @@ -54,6 +52,7 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { var isAnimatingPinning = false val paint = Paint() + private val strokePaint = Paint() val lastDrawnTransientRect = RectF() var backgroundHeight = context.deviceProfile.taskbarHeight.toFloat() var translationYForSwipe = 0f @@ -62,19 +61,18 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { private val transientBackgroundBounds = context.transientTaskbarBounds private val shadowAlpha: Float + private val strokeAlpha: Int private var shadowBlur = 0f private var keyShadowDistance = 0f private var bottomMargin = 0 - private val fullLeftCornerRadius = context.leftCornerRadius.toFloat() - private val fullRightCornerRadius = context.rightCornerRadius.toFloat() - private var leftCornerRadius = fullLeftCornerRadius - private var rightCornerRadius = fullRightCornerRadius + private val fullCornerRadius = context.cornerRadius.toFloat() + private var cornerRadius = fullCornerRadius private var widthInsetPercentage = 0f - private val square: Path = Path() - private val circle: Path = Path() - private val invertedLeftCornerPath: Path = Path() - private val invertedRightCornerPath: Path = Path() + private val square = Path() + private val circle = Path() + private val invertedLeftCornerPath = Path() + private val invertedRightCornerPath = Path() private var stashedHandleWidth = context.resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width) @@ -86,10 +84,18 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { paint.color = context.getColor(R.color.taskbar_background) paint.flags = Paint.ANTI_ALIAS_FLAG paint.style = Paint.Style.FILL - - shadowAlpha = - if (Utilities.isDarkTheme(context)) DARK_THEME_SHADOW_ALPHA - else LIGHT_THEME_SHADOW_ALPHA + strokePaint.color = context.getColor(R.color.taskbar_stroke) + strokePaint.flags = Paint.ANTI_ALIAS_FLAG + strokePaint.style = Paint.Style.STROKE + strokePaint.strokeWidth = + context.resources.getDimension(R.dimen.transient_taskbar_stroke_width) + if (Utilities.isDarkTheme(context)) { + strokeAlpha = DARK_THEME_STROKE_ALPHA + shadowAlpha = DARK_THEME_SHADOW_ALPHA + } else { + strokeAlpha = LIGHT_THEME_STROKE_ALPHA + shadowAlpha = LIGHT_THEME_SHADOW_ALPHA + } setCornerRoundness(DEFAULT_ROUNDNESS) } @@ -106,7 +112,7 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { } /** - * Sets the roundness of the round corner above Taskbar. No effect on transient Taskkbar. + * Sets the roundness of the round corner above Taskbar. No effect on transient Taskbar. * * @param cornerRoundness 0 has no round corner, 1 has complete round corner. */ @@ -115,21 +121,18 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { return } - leftCornerRadius = fullLeftCornerRadius * cornerRoundness - rightCornerRadius = fullRightCornerRadius * cornerRoundness + cornerRadius = fullCornerRadius * cornerRoundness // Create the paths for the inverted rounded corners above the taskbar. Start with a filled // square, and then subtract out a circle from the appropriate corner. square.reset() - square.addRect(0f, 0f, leftCornerRadius, leftCornerRadius, Path.Direction.CW) + square.addRect(0f, 0f, cornerRadius, cornerRadius, Path.Direction.CW) circle.reset() - circle.addCircle(leftCornerRadius, 0f, leftCornerRadius, Path.Direction.CW) + circle.addCircle(cornerRadius, 0f, cornerRadius, Path.Direction.CW) invertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE) - square.reset() - square.addRect(0f, 0f, rightCornerRadius, rightCornerRadius, Path.Direction.CW) circle.reset() - circle.addCircle(0f, 0f, rightCornerRadius, Path.Direction.CW) + circle.addCircle(0f, 0f, cornerRadius, Path.Direction.CW) invertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE) } @@ -163,10 +166,10 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { } // Draw the inverted rounded corners above the taskbar. - canvas.translate(0f, -leftCornerRadius) + canvas.translate(0f, -cornerRadius) canvas.drawPath(invertedLeftCornerPath, paint) - canvas.translate(0f, leftCornerRadius) - canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius) + canvas.translate(0f, cornerRadius) + canvas.translate(canvas.width - cornerRadius, -cornerRadius) canvas.drawPath(invertedRightCornerPath, paint) } @@ -236,6 +239,7 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { keyShadowDistance, setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha)) ) + strokePaint.alpha = (paint.alpha * strokeAlpha) / 255 lastDrawnTransientRect.set( transientBackgroundBounds.left + halfWidthDelta, @@ -247,6 +251,7 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { lastDrawnTransientRect.inset(horizontalInset, 0f) canvas.drawRoundRect(lastDrawnTransientRect, radius, radius, paint) + canvas.drawRoundRect(lastDrawnTransientRect, radius, radius, strokePaint) } /** @@ -259,5 +264,9 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { companion object { const val DEFAULT_ROUNDNESS = 1f + private const val DARK_THEME_STROKE_ALPHA = 51 + private const val LIGHT_THEME_STROKE_ALPHA = 41 + private const val DARK_THEME_SHADOW_ALPHA = 51f + private const val LIGHT_THEME_SHADOW_ALPHA = 25f } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index f9ddc3db2f..58c5e835c9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -180,8 +180,9 @@ public class TaskbarControllers { taskbarUnfoldAnimationController, taskbarKeyguardController, stashedHandleViewController, taskbarStashController, taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController, - voiceInteractionWindowController, taskbarTranslationController, - taskbarEduTooltipController, keyboardQuickSwitchController, taskbarPinningController + voiceInteractionWindowController, taskbarRecentAppsController, + taskbarTranslationController, taskbarEduTooltipController, + keyboardQuickSwitchController, taskbarPinningController, }; mBackgroundRendererControllers = new BackgroundRendererController[] { taskbarDragLayerController, taskbarScrimViewController, diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt index d25e644b5b..a635537907 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt @@ -69,8 +69,6 @@ constructor( private lateinit var dividerView: View - private val menuWidth = - resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_width) private val popupCornerRadius = Themes.getDialogCornerRadius(context) private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width) private val arrowHeight = resources.getDimension(R.dimen.popup_arrow_height) @@ -130,7 +128,7 @@ constructor( /** Orient object as usual and then center object horizontally. */ override fun orientAboutObject() { super.orientAboutObject() - x = mTempRect.centerX() - menuWidth / 2f + x = mTempRect.centerX() - measuredWidth / 2f } override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index 84874a9371..f703463117 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -18,6 +18,7 @@ package com.android.launcher3.taskbar; import static android.view.KeyEvent.ACTION_UP; import static android.view.KeyEvent.KEYCODE_BACK; +import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; import android.content.Context; @@ -41,6 +42,7 @@ import com.android.app.viewcapture.ViewCaptureFactory; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.views.BaseDragLayer; @@ -104,7 +106,10 @@ public class TaskbarDragLayer extends BaseDragLayer { mTaskbarBackgroundAlpha = new MultiPropertyFactory<>(this, BG_ALPHA, INDEX_COUNT, (a, b) -> a * b, 1f); mTaskbarBackgroundAlpha.get(INDEX_ALL_OTHER_STATES).setValue(0); - mTaskbarBackgroundAlpha.get(INDEX_STASH_ANIM).setValue(1); + mTaskbarBackgroundAlpha.get(INDEX_STASH_ANIM).setValue( + enableScalingRevealHomeAnimation() && DisplayController.isTransientTaskbar(context) + ? 0 + : 1); } public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index e1ddb6a951..2103ebbd10 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -53,6 +53,7 @@ import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController import com.android.launcher3.testing.shared.ResourceUtils import com.android.launcher3.util.DisplayController +import com.android.launcher3.util.Executors import java.io.PrintWriter import kotlin.jvm.optionals.getOrNull import kotlin.math.max @@ -76,6 +77,7 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas private val gestureNavSettingsObserver = GestureNavigationSettingsObserver( context.mainThreadHandler, + Executors.UI_HELPER_EXECUTOR.handler, context, this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged ) @@ -236,7 +238,8 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas provider.insetsSize = getInsetsForGravityWithCutout(contentHeight, gravity, endRotation) } else if (provider.type == mandatorySystemGestures()) { if (context.isThreeButtonNav) { - provider.insetsSize = Insets.of(0, 0, 0, 0) + provider.insetsSize = getInsetsForGravityWithCutout(contentHeight, gravity, + endRotation) } else { val gestureHeight = ResourceUtils.getNavbarSize( diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java index eac4eaa130..911140aa2d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java @@ -5,9 +5,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK; @@ -16,6 +13,7 @@ import static com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_A import android.app.KeyguardManager; import com.android.launcher3.AbstractFloatingView; +import com.android.quickstep.util.SystemUiFlagUtils; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; @@ -26,19 +24,6 @@ import java.io.PrintWriter; */ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTaskbarController { - private static final long KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING - | SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED - | SYSUI_STATE_BACK_DISABLED | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED - | SYSUI_STATE_WAKEFULNESS_MASK; - - // If any of these SysUi flags (via QuickstepContract) is set, the device to be considered - // locked. - public static final long MASK_ANY_SYSUI_LOCKED = SYSUI_STATE_BOUNCER_SHOWING - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED - | SYSUI_STATE_DEVICE_DREAMING; - private final TaskbarActivityContext mContext; private long mKeyguardSysuiFlags; private boolean mBouncerShowing; @@ -55,7 +40,7 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas } public void updateStateForSysuiFlags(@SystemUiStateFlags long systemUiStateFlags) { - long interestingKeyguardFlags = systemUiStateFlags & KEYGUARD_SYSUI_FLAGS; + long interestingKeyguardFlags = systemUiStateFlags & SystemUiFlagUtils.KEYGUARD_SYSUI_FLAGS; if (interestingKeyguardFlags == mKeyguardSysuiFlags) { // No change in keyguard relevant flags return; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index ead1a8a441..cb9f24ae80 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -16,7 +16,6 @@ package com.android.launcher3.taskbar; import static com.android.app.animation.Interpolators.EMPHASIZED; -import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW; import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE; @@ -51,6 +50,7 @@ import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; +import com.android.quickstep.util.SystemUiFlagUtils; import com.android.quickstep.views.RecentsView; import com.android.systemui.animation.ViewRootSync; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -343,8 +343,7 @@ public class TaskbarLauncherStateController { prevIsAwake && hasAnyFlag(FLAGS_LAUNCHER_ACTIVE)); } - boolean isDeviceLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED); - updateStateForFlag(FLAG_DEVICE_LOCKED, isDeviceLocked); + updateStateForFlag(FLAG_DEVICE_LOCKED, SystemUiFlagUtils.isLocked(systemUiStateFlags)); // Taskbar is hidden whenever the device is dreaming. The dreaming state includes the // interactive dreams, AoD, screen off. Since the SYSUI_STATE_DEVICE_DREAMING only kicks in diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index ec2cee2bd9..2a58db25df 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -611,7 +611,8 @@ public class TaskbarManager { } } - private void addTaskbarRootViewToWindow() { + @VisibleForTesting + void addTaskbarRootViewToWindow() { if (enableTaskbarNoRecreate() && !mAddedWindow && mTaskbarActivityContext != null) { mWindowManager.addView(mTaskbarRootLayout, mTaskbarActivityContext.getWindowLayoutParams()); @@ -619,7 +620,8 @@ public class TaskbarManager { } } - private void removeTaskbarRootViewFromWindow() { + @VisibleForTesting + void removeTaskbarRootViewFromWindow() { if (enableTaskbarNoRecreate() && mAddedWindow) { mWindowManager.removeViewImmediate(mTaskbarRootLayout); mAddedWindow = false; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 35e1c7baa9..2b0e1699cc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -79,7 +79,7 @@ public class TaskbarModelCallbacks implements public void init(TaskbarControllers controllers) { mControllers = controllers; - if (mControllers.taskbarRecentAppsController.isEnabled()) { + if (mControllers.taskbarRecentAppsController.getCanShowRunningApps()) { RecentsModel.INSTANCE.get(mContext).registerRunningTasksListener(this); if (shouldShowRunningAppsInDesktopMode()) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java deleted file mode 100644 index 606ba5b633..0000000000 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.taskbar; - -import static java.util.Collections.emptySet; - -import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; - -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.ItemInfo; - -import java.util.Set; - -/** - * Base class for providing recent apps functionality - */ -public class TaskbarRecentAppsController { - - public static final TaskbarRecentAppsController DEFAULT = new TaskbarRecentAppsController(); - - // Initialized in init. - protected TaskbarControllers mControllers; - - @CallSuper - protected void init(TaskbarControllers taskbarControllers) { - mControllers = taskbarControllers; - } - - @CallSuper - protected void onDestroy() { - mControllers = null; - } - - /** Stores the current {@link AppInfo} instances, no-op except in desktop environment. */ - protected void setApps(AppInfo[] apps) { - } - - /** - * Indicates whether recent apps functionality is enabled, should return false except in - * desktop environment. - */ - protected boolean isEnabled() { - return false; - } - - /** Called to update hotseatItems, no-op except in desktop environment. */ - protected ItemInfo[] updateHotseatItemInfos(@NonNull ItemInfo[] hotseatItems) { - return hotseatItems; - } - - /** Called to update the list of currently running apps, no-op except in desktop environment. */ - protected void updateRunningApps() {} - - /** Returns the currently running apps, or an empty Set if outside of Desktop environment. */ - public Set getRunningApps() { - return emptySet(); - } - - /** Returns the set of apps whose tasks are all minimized. */ - public Set getMinimizedApps() { - return emptySet(); - } -} diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt similarity index 61% rename from quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt rename to quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt index d4bef28bf8..b1fc9ccb02 100644 --- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt @@ -13,37 +13,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.launcher3.taskbar import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration -import android.util.Log -import android.util.SparseArray import androidx.annotation.VisibleForTesting -import androidx.core.util.valueIterator +import com.android.launcher3.Flags.enableRecentsInTaskbar import com.android.launcher3.model.data.AppInfo import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.statehandlers.DesktopVisibilityController +import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController import com.android.quickstep.RecentsModel -import kotlin.collections.filterNotNull +import com.android.window.flags.Flags.enableDesktopWindowingMode +import com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps +import java.io.PrintWriter /** - * Shows running apps when in Desktop Mode. - * - * Users can enter and exit Desktop Mode at run-time, meaning this class falls back to the default - * recent-apps behaviour when outside of Desktop Mode. - * - * This class should only be used if - * [com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps] is enabled. + * Provides recent apps functionality, when the Taskbar Recent Apps section is enabled. Behavior: + * - When in Fullscreen mode: show the N most recent Tasks + * - When in Desktop Mode: show the currently running (open) Tasks */ -class DesktopTaskbarRunningAppsController( +class TaskbarRecentAppsController( private val recentsModel: RecentsModel, // Pass a provider here instead of the actual DesktopVisibilityController instance since that // instance might not be available when this constructor is called. private val desktopVisibilityControllerProvider: () -> DesktopVisibilityController?, -) : TaskbarRecentAppsController() { +) : LoggableTaskbarController { + + // TODO(b/335401172): unify DesktopMode checks in Launcher. + val canShowRunningApps = + enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps() + + // TODO(b/343532825): Add a setting to disable Recents even when the flag is on. + var isEnabled: Boolean = enableRecentsInTaskbar() || canShowRunningApps + @VisibleForTesting + set(isEnabledFromTest){ + field = isEnabledFromTest + } + + // Initialized in init. + private lateinit var controllers: TaskbarControllers private var apps: Array? = null private var allRunningDesktopAppInfos: List? = null @@ -55,22 +65,40 @@ class DesktopTaskbarRunningAppsController( private val isInDesktopMode: Boolean get() = desktopVisibilityController?.areDesktopTasksVisible() ?: false - override fun onDestroy() { - super.onDestroy() + val runningApps: Set + get() { + if (!isEnabled || !isInDesktopMode) { + return emptySet() + } + return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet() + } + + val minimizedApps: Set + get() { + if (!isInDesktopMode) { + return emptySet() + } + return allMinimizedDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() + ?: emptySet() + } + + fun init(taskbarControllers: TaskbarControllers) { + controllers = taskbarControllers + } + + fun onDestroy() { apps = null } - @VisibleForTesting - public override fun setApps(apps: Array?) { + /** Stores the current [AppInfo] instances, no-op except in desktop environment. */ + fun setApps(apps: Array?) { this.apps = apps } - override fun isEnabled() = true - - @VisibleForTesting - public override fun updateHotseatItemInfos(hotseatItems: Array): Array { - if (!isInDesktopMode) { - Log.d(TAG, "updateHotseatItemInfos: not in Desktop Mode") + /** Called to update hotseatItems, in order to de-dupe them from Recent/Running tasks later. */ + // TODO(next CL): add new section of Tasks instead of changing Hotseat items + fun updateHotseatItemInfos(hotseatItems: Array): Array { + if (!isEnabled || !isInDesktopMode) { return hotseatItems } val newHotseatItemInfos = @@ -89,55 +117,6 @@ class DesktopTaskbarRunningAppsController( return newHotseatItemInfos.toTypedArray() } - override fun getRunningApps(): Set { - if (!isInDesktopMode) { - return emptySet() - } - return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet() - } - - override fun getMinimizedApps(): Set { - if (!isInDesktopMode) { - return emptySet() - } - return allMinimizedDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet() - } - - @VisibleForTesting - public override fun updateRunningApps() { - if (!isInDesktopMode) { - Log.d(TAG, "updateRunningApps: not in Desktop Mode") - mControllers.taskbarViewController.commitRunningAppsToUI() - return - } - val runningTasks = getDesktopRunningTasks() - val runningAppInfo = getAppInfosFromRunningTasks(runningTasks) - allRunningDesktopAppInfos = runningAppInfo - updateMinimizedApps(runningTasks, runningAppInfo) - mControllers.taskbarViewController.commitRunningAppsToUI() - } - - private fun updateMinimizedApps( - runningTasks: List, - runningAppInfo: List, - ) { - val allRunningAppTasks = - runningAppInfo - .mapNotNull { appInfo -> appInfo.targetPackage?.let { appInfo to it } } - .associate { (appInfo, targetPackage) -> - appInfo to - runningTasks - .filter { it.realActivity?.packageName == targetPackage } - .map { it.taskId } - } - val minimizedTaskIds = runningTasks.associate { it.taskId to !it.isVisible } - allMinimizedDesktopAppInfos = - allRunningAppTasks - .filterValues { taskIds -> taskIds.all { minimizedTaskIds[it] ?: false } } - .keys - .toList() - } - private fun getRunningDesktopAppInfosExceptHotseatApps( allRunningDesktopAppInfos: List, hotseatItems: List @@ -165,12 +144,43 @@ class DesktopTaskbarRunningAppsController( .filterNotNull() } - private fun getAppInfosFromRunningTask(task: RunningTaskInfo): AppInfo? = - apps?.firstOrNull { it.targetPackage == task.realActivity?.packageName } + /** Called to update the list of currently running apps, no-op except in desktop environment. */ + fun updateRunningApps() { + if (!isEnabled || !isInDesktopMode) { + return controllers.taskbarViewController.commitRunningAppsToUI() + } + val runningTasks = getDesktopRunningTasks() + val runningAppInfo = getAppInfosFromRunningTasks(runningTasks) + allRunningDesktopAppInfos = runningAppInfo + updateMinimizedApps(runningTasks, runningAppInfo) + controllers.taskbarViewController.commitRunningAppsToUI() + } - private fun SparseArray.toList(): List = valueIterator().asSequence().toList() + private fun updateMinimizedApps( + runningTasks: List, + runningAppInfo: List, + ) { + val allRunningAppTasks = + runningAppInfo + .mapNotNull { appInfo -> appInfo.targetPackage?.let { appInfo to it } } + .associate { (appInfo, targetPackage) -> + appInfo to + runningTasks + .filter { it.realActivity?.packageName == targetPackage } + .map { it.taskId } + } + val minimizedTaskIds = runningTasks.associate { it.taskId to !it.isVisible } + allMinimizedDesktopAppInfos = + allRunningAppTasks + .filterValues { taskIds -> taskIds.all { minimizedTaskIds[it] ?: false } } + .keys + .toList() + } - companion object { - private const val TAG = "TabletDesktopTaskbarRunningAppsController" + override fun dumpLogs(prefix: String, pw: PrintWriter) { + pw.println("$prefix TaskbarRecentAppsController:") + pw.println("$prefix\tisEnabled=$isEnabled") + pw.println("$prefix\tcanShowRunningApps=$canShowRunningApps") + // TODO(next CL): add more logs } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java index 92d9b23cb6..48d2bc2ff7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java @@ -103,14 +103,20 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa } private float getScrimAlpha() { + final boolean isPersistentTaskBarVisible = + mTaskbarVisible && !DisplayController.isTransientTaskbar(mScrimView.getContext()); final boolean manageMenuExpanded = (mSysUiStateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0; - return manageMenuExpanded - // When manage menu shows there's the first scrim and second scrim so figure out - // what the total transparency would be. - ? (BUBBLE_EXPANDED_SCRIM_ALPHA + (BUBBLE_EXPANDED_SCRIM_ALPHA - * (1 - BUBBLE_EXPANDED_SCRIM_ALPHA))) - : shouldShowScrim() ? BUBBLE_EXPANDED_SCRIM_ALPHA : 0; + if (isPersistentTaskBarVisible && manageMenuExpanded) { + // When manage menu shows for persistent task bar there's the first scrim and second + // scrim so figure out what the total transparency would be. + return BUBBLE_EXPANDED_SCRIM_ALPHA + + (BUBBLE_EXPANDED_SCRIM_ALPHA * (1 - BUBBLE_EXPANDED_SCRIM_ALPHA)); + } else if (shouldShowScrim()) { + return BUBBLE_EXPANDED_SCRIM_ALPHA; + } else { + return 0; + } } private void showScrim(boolean showScrim, float alpha, boolean skipAnim) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 8fdb4601ab..7ff887c7d2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -22,10 +22,10 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME; import static com.android.app.animation.Interpolators.INSTANT; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.internal.jank.InteractionJankMonitor.Configuration; +import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW; -import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.FlagDebugUtils.appendFlag; import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; @@ -35,11 +35,11 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.ValueAnimator; import android.app.RemoteAction; import android.graphics.drawable.Icon; import android.os.SystemClock; @@ -66,6 +66,7 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.util.SystemUiFlagUtils; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -242,6 +243,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private final Alarm mTimeoutAlarm = new Alarm(); private boolean mEnableBlockingTimeoutDuringTests = false; + private Animator mTaskbarBackgroundAlphaAnimator; + private long mTaskbarBackgroundDuration; + private boolean mIsGoingHome; + // Evaluate whether the handle should be stashed private final LongPredicate mIsStashedPredicate = flags -> { boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP); @@ -266,6 +271,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity); mAccessibilityManager = mActivity.getSystemService(AccessibilityManager.class); + mTaskbarBackgroundDuration = + activity.getResources().getInteger(R.integer.taskbar_background_duration); if (mActivity.isPhoneMode()) { mUnstashedHeight = mActivity.getResources().getDimensionPixelSize( R.dimen.taskbar_phone_size); @@ -760,9 +767,16 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba backgroundAndHandleAlphaStartDelay, backgroundAndHandleAlphaDuration, LINEAR); - play(as, mTaskbarBackgroundAlphaForStash.animateToValue(backgroundAlphaTarget), - backgroundAndHandleAlphaStartDelay, - backgroundAndHandleAlphaDuration, LINEAR); + if (enableScalingRevealHomeAnimation() && !isStashed) { + play(as, getTaskbarBackgroundAnimatorWhenNotGoingHome(duration), + 0, 0, LINEAR); + as.addListener(AnimatorListeners.forEndCallback( + () -> mTaskbarBackgroundAlphaForStash.setValue(backgroundAlphaTarget))); + } else { + play(as, mTaskbarBackgroundAlphaForStash.animateToValue(backgroundAlphaTarget), + backgroundAndHandleAlphaStartDelay, + backgroundAndHandleAlphaDuration, LINEAR); + } // The rest of the animations might be "skipped" in TRANSITION_HANDLE_FADE transitions. AnimatorSet skippable = as; @@ -805,6 +819,62 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba .setDuration(isStashed ? duration / 2 : duration)); } + private Animator getTaskbarBackgroundAnimatorWhenNotGoingHome(long duration) { + ValueAnimator a = ValueAnimator.ofFloat(0, 1); + a.setDuration(duration); + a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + // This value is arbitrary. + private static final float ANIMATED_FRACTION_THRESHOLD = 0.25f; + private boolean mTaskbarBgAlphaAnimationStarted = false; + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + if (mIsGoingHome) { + mTaskbarBgAlphaAnimationStarted = true; + } + if (mTaskbarBgAlphaAnimationStarted) { + return; + } + + if (valueAnimator.getAnimatedFraction() >= ANIMATED_FRACTION_THRESHOLD) { + if (!mIsGoingHome) { + playTaskbarBackgroundAlphaAnimation(); + setUserIsGoingHome(false); + mTaskbarBgAlphaAnimationStarted = true; + } + } + } + }); + return a; + } + + /** + * Sets whether the user is going home based on the current gesture. + */ + public void setUserIsGoingHome(boolean isGoingHome) { + mIsGoingHome = isGoingHome; + } + + /** + * Plays the taskbar background alpha animation if one is not currently playing. + */ + public void playTaskbarBackgroundAlphaAnimation() { + if (mTaskbarBackgroundAlphaAnimator != null + && mTaskbarBackgroundAlphaAnimator.isRunning()) { + return; + } + mTaskbarBackgroundAlphaAnimator = mTaskbarBackgroundAlphaForStash + .animateToValue(1f) + .setDuration(mTaskbarBackgroundDuration); + mTaskbarBackgroundAlphaAnimator.setInterpolator(LINEAR); + mTaskbarBackgroundAlphaAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mTaskbarBackgroundAlphaAnimator = null; + } + }); + mTaskbarBackgroundAlphaAnimator.start(); + } + private static void play(AnimatorSet as, @Nullable Animator a, long startDelay, long duration, Interpolator interpolator) { if (a == null) { @@ -950,9 +1020,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba && DisplayController.isTransientTaskbar(mActivity); updateStateForFlag(FLAG_STASHED_SYSUI, hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles); - boolean isLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED) - && !hasAnyFlag(systemUiStateFlags, SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY); - updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, isLocked); + updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, + SystemUiFlagUtils.isLocked(systemUiStateFlags)); mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING); mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 2b68b52932..593285f062 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -411,4 +411,11 @@ public class TaskbarUIController { public void setSkipNextRecentsAnimEnd() { // Overridden } + + /** + * Sets whether the user is going home based on the current gesture. + */ + public void setUserIsGoingHome(boolean isGoingHome) { + mControllers.taskbarStashController.setUserIsGoingHome(isGoingHome); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 23495adcf7..55745b557a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -347,6 +347,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar float allAppIconTranslateRange = mapRange(scale, transientTaskbarAllAppsOffset, persistentTaskbarAllAppsOffset); + // no x translation required when all apps button is the only icon in taskbar. + if (iconViews.length <= 1) { + allAppIconTranslateRange = 0f; + } + if (mIsRtl) { allAppIconTranslateRange *= -1; } @@ -377,7 +382,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar -finalMarginScale * (iconIndex - halfIconCount)); } - if (iconView.equals(mTaskbarView.getAllAppsButtonView()) && iconViews.length > 1) { + if (iconView.equals(mTaskbarView.getAllAppsButtonView())) { ((IconButtonView) iconView).setTranslationXForTaskbarAllAppsIcon( allAppIconTranslateRange); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt index a59e81b68e..25939e1eb5 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt @@ -19,7 +19,9 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.ColorFilter +import android.graphics.Matrix import android.graphics.Paint +import android.graphics.Path import android.graphics.PixelFormat import android.graphics.drawable.Drawable import com.android.app.animation.Interpolators @@ -28,28 +30,28 @@ import com.android.launcher3.Utilities import com.android.launcher3.Utilities.mapToRange import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound import com.android.launcher3.popup.RoundedArrowDrawable +import kotlin.math.max +import kotlin.math.min /** Drawable for the background of the bubble bar. */ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) : Drawable() { - private val DARK_THEME_SHADOW_ALPHA = 51f - private val LIGHT_THEME_SHADOW_ALPHA = 25f - - private val paint: Paint = Paint() - private val pointerWidth: Float - private val pointerHeight: Float - private val pointerTipRadius: Float - private val pointerVisibleHeight: Float + private val fillPaint: Paint = Paint() + private val strokePaint: Paint = Paint() + private val arrowWidth: Float + private val arrowHeight: Float + private val arrowTipRadius: Float + private val arrowVisibleHeight: Float private val shadowAlpha: Float private var shadowBlur = 0f private var keyShadowDistance = 0f + private var arrowHeightFraction = 1f var arrowPositionX: Float = 0f private set private var showingArrow: Boolean = false - private var arrowDrawable: RoundedArrowDrawable var width: Float = 0f @@ -70,34 +72,31 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) } init { - paint.color = context.getColor(R.color.taskbar_background) - paint.flags = Paint.ANTI_ALIAS_FLAG - paint.style = Paint.Style.FILL - val res = context.resources + // configure fill paint + fillPaint.color = context.getColor(R.color.taskbar_background) + fillPaint.flags = Paint.ANTI_ALIAS_FLAG + fillPaint.style = Paint.Style.FILL + // configure stroke paint + strokePaint.color = context.getColor(R.color.taskbar_stroke) + strokePaint.flags = Paint.ANTI_ALIAS_FLAG + strokePaint.style = Paint.Style.STROKE + strokePaint.strokeWidth = res.getDimension(R.dimen.transient_taskbar_stroke_width) + // apply theme alpha attributes + if (Utilities.isDarkTheme(context)) { + strokePaint.alpha = DARK_THEME_STROKE_ALPHA + shadowAlpha = DARK_THEME_SHADOW_ALPHA + } else { + strokePaint.alpha = LIGHT_THEME_STROKE_ALPHA + shadowAlpha = LIGHT_THEME_SHADOW_ALPHA + } + shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur) keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance) - pointerWidth = res.getDimension(R.dimen.bubblebar_pointer_width) - pointerHeight = res.getDimension(R.dimen.bubblebar_pointer_height) - pointerVisibleHeight = res.getDimension(R.dimen.bubblebar_pointer_visible_size) - pointerTipRadius = res.getDimension(R.dimen.bubblebar_pointer_radius) - - shadowAlpha = - if (Utilities.isDarkTheme(context)) { - DARK_THEME_SHADOW_ALPHA - } else { - LIGHT_THEME_SHADOW_ALPHA - } - - arrowDrawable = - RoundedArrowDrawable.createVerticalRoundedArrow( - pointerWidth, - pointerHeight, - pointerTipRadius, - /* isPointingUp= */ true, - context.getColor(R.color.taskbar_background) - ) - arrowDrawable.setBounds(0, 0, pointerWidth.toInt(), pointerHeight.toInt()) + arrowWidth = res.getDimension(R.dimen.bubblebar_pointer_width) + arrowHeight = res.getDimension(R.dimen.bubblebar_pointer_height) + arrowVisibleHeight = res.getDimension(R.dimen.bubblebar_pointer_visible_size) + arrowTipRadius = res.getDimension(R.dimen.bubblebar_pointer_radius) } fun showArrow(show: Boolean) { @@ -115,40 +114,54 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) // TODO (b/277359345): Should animate the alpha similar to taskbar (see TaskbarDragLayer) // Draw shadows. val newShadowAlpha = - mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR) - paint.setShadowLayer( + mapToRange(fillPaint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR) + fillPaint.setShadowLayer( shadowBlur, 0f, keyShadowDistance, setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha)) ) - arrowDrawable.setShadowLayer( - shadowBlur, - 0f, - keyShadowDistance, - setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha)) - ) - - // Draw background. + // Create background path + val backgroundPath = Path() + val topOffset = backgroundHeight - bounds.height().toFloat() val radius = backgroundHeight / 2f val left = bounds.left + (if (anchorLeft) 0f else bounds.width().toFloat() - width) val right = bounds.left + (if (anchorLeft) width else bounds.width().toFloat()) - val top = bounds.top + pointerVisibleHeight + val top = bounds.top - topOffset + arrowVisibleHeight + val bottom = bounds.top + bounds.height().toFloat() - canvas.drawRoundRect(left, top, right, bottom, radius, radius, paint) - - if (showingArrow) { - // Draw arrow. - val transX = bounds.left + arrowPositionX - pointerWidth / 2f - canvas.translate(transX, 0f) - arrowDrawable.draw(canvas) - } + backgroundPath.addRoundRect(left, top, right, bottom, radius, radius, Path.Direction.CW) + addArrowPathIfNeeded(backgroundPath, topOffset) + // Draw background. + canvas.drawPath(backgroundPath, fillPaint) + canvas.drawPath(backgroundPath, strokePaint) canvas.restore() } + private fun addArrowPathIfNeeded(sourcePath: Path, topOffset: Float) { + if (!showingArrow || arrowHeightFraction <= 0) return + val arrowPath = Path() + RoundedArrowDrawable.addDownPointingRoundedTriangleToPath( + arrowWidth, + arrowHeight, + arrowTipRadius, + arrowPath + ) + // flip it horizontally + val pathTransform = Matrix() + pathTransform.setRotate(180f, arrowWidth * 0.5f, arrowHeight * 0.5f) + arrowPath.transform(pathTransform) + // shift to arrow position + val arrowStart = bounds.left + arrowPositionX - (arrowWidth / 2f) + val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight - topOffset + arrowPath.offset(arrowStart, arrowTop) + // union with rectangle + sourcePath.op(arrowPath, Path.Op.UNION) + } + override fun getOpacity(): Int { - return when (paint.alpha) { + return when (fillPaint.alpha) { 255 -> PixelFormat.OPAQUE 0 -> PixelFormat.TRANSPARENT else -> PixelFormat.TRANSLUCENT @@ -156,24 +169,40 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) } override fun setAlpha(alpha: Int) { - paint.alpha = alpha - arrowDrawable.alpha = alpha + fillPaint.alpha = alpha invalidateSelf() } override fun getAlpha(): Int { - return paint.alpha + return fillPaint.alpha } override fun setColorFilter(colorFilter: ColorFilter?) { - paint.colorFilter = colorFilter + fillPaint.colorFilter = colorFilter } - fun setArrowAlpha(alpha: Int) { - arrowDrawable.alpha = alpha - } - - fun setHeight(newHeight: Float) { + fun setBackgroundHeight(newHeight: Float) { backgroundHeight = newHeight } + + /** + * Set fraction of the arrow height that should be displayed. Allowed values range are [0..1]. + * If value passed is out of range it will be converted to the closest value in tha allowed + * range. + */ + fun setArrowHeightFraction(arrowHeightFraction: Float) { + var newHeightFraction = arrowHeightFraction + if (newHeightFraction !in 0f..1f) { + newHeightFraction = min(max(newHeightFraction, 0f), 1f) + } + this.arrowHeightFraction = newHeightFraction + invalidateSelf() + } + + companion object { + private const val DARK_THEME_STROKE_ALPHA = 51 + private const val LIGHT_THEME_STROKE_ALPHA = 41 + private const val DARK_THEME_SHADOW_ALPHA = 51f + private const val LIGHT_THEME_SHADOW_ALPHA = 25f + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index 046f5b6afe..028df34c73 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -135,7 +135,6 @@ public class BubbleBarController extends IBubblesListener.Stub { private static final Executor BUBBLE_STATE_EXECUTOR = Executors.newSingleThreadExecutor( new SimpleThreadFactory("BubbleStateUpdates-", THREAD_PRIORITY_BACKGROUND)); - private final Executor mMainExecutor; private final LauncherApps mLauncherApps; private final BubbleIconFactory mIconFactory; private final SystemUiProxy mSystemUiProxy; @@ -198,7 +197,6 @@ public class BubbleBarController extends IBubblesListener.Stub { if (sBubbleBarEnabled) { mSystemUiProxy.setBubblesListener(this); } - mMainExecutor = MAIN_EXECUTOR; mLauncherApps = context.getSystemService(LauncherApps.class); mIconFactory = new BubbleIconFactory(context, context.getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size), @@ -241,7 +239,7 @@ public class BubbleBarController extends IBubblesListener.Stub { private void createAndAddOverflowIfNeeded() { if (mOverflowBubble == null) { BubbleBarOverflow overflow = createOverflow(mContext); - mMainExecutor.execute(() -> { + MAIN_EXECUTOR.execute(() -> { // we're on the main executor now, so check that the overflow hasn't been created // again to avoid races. if (mOverflowBubble == null) { @@ -303,12 +301,12 @@ public class BubbleBarController extends IBubblesListener.Stub { } viewUpdate.currentBubbles = currentBubbles; } - mMainExecutor.execute(() -> applyViewChanges(viewUpdate)); + MAIN_EXECUTOR.execute(() -> applyViewChanges(viewUpdate)); }); } else { // No bubbles to load, immediately apply the changes. BUBBLE_STATE_EXECUTOR.execute( - () -> mMainExecutor.execute(() -> applyViewChanges(viewUpdate))); + () -> MAIN_EXECUTOR.execute(() -> applyViewChanges(viewUpdate))); } } @@ -496,7 +494,7 @@ public class BubbleBarController extends IBubblesListener.Stub { @Override public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { - mMainExecutor.execute( + MAIN_EXECUTOR.execute( () -> mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation)); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 23e52e6f71..c7c63e8f86 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -24,6 +24,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.Context; @@ -44,6 +45,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.util.DisplayController; +import com.android.wm.shell.Flags; import com.android.wm.shell.common.bubbles.BubbleBarLocation; import java.util.List; @@ -85,6 +87,7 @@ public class BubbleBarView extends FrameLayout { private static final int MAX_VISIBLE_BUBBLES_COLLAPSED = 2; private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200; private static final int WIDTH_ANIMATION_DURATION_MS = 200; + private static final int SCALE_ANIMATION_DURATION_MS = 200; private static final long FADE_OUT_ANIM_ALPHA_DURATION_MS = 50L; private static final long FADE_OUT_ANIM_ALPHA_DELAY_MS = 50L; @@ -98,6 +101,8 @@ public class BubbleBarView extends FrameLayout { // During fade in animation we shift the bubble bar 1/60th of the screen width private static final float FADE_IN_ANIM_POSITION_SHIFT = 1 / 60f; + private static final int SCALE_IN_ANIMATION_DURATION_MS = 250; + /** * Custom property to set alpha value for the bar view while a bubble is being dragged. * Skips applying alpha to the dragged bubble. @@ -133,13 +138,12 @@ public class BubbleBarView extends FrameLayout { private float mBubbleBarPadding; // The size of a bubble in the bar private float mIconSize; + // The scale of bubble icons + private float mIconScale = 1f; // The elevation of the bubbles within the bar private final float mBubbleElevation; private final float mDragElevation; private final int mPointerSize; - - private final Rect mTempBackgroundBounds = new Rect(); - // Whether the bar is expanded (i.e. the bubble activity is being displayed). private boolean mIsBarExpanded = false; // The currently selected bubble view. @@ -157,6 +161,11 @@ public class BubbleBarView extends FrameLayout { // collapsed state and 1 to the fully expanded state. private final ValueAnimator mWidthAnimator = ValueAnimator.ofFloat(0, 1); + /** An animator used for scaling in a new bubble to the bubble bar while expanded. */ + @Nullable + private ValueAnimator mNewBubbleScaleInAnimator = null; + @Nullable + private ValueAnimator mScalePaddingAnimator; @Nullable private Animator mBubbleBarLocationAnimator = null; @@ -211,42 +220,74 @@ public class BubbleBarView extends FrameLayout { setBackgroundDrawable(mBubbleBarBackground); mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS); - mWidthAnimator.addUpdateListener(animation -> { - updateChildrenRenderNodeProperties(mBubbleBarLocation); - invalidate(); - }); - mWidthAnimator.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationCancel(Animator animation) { - } - @Override - public void onAnimationEnd(Animator animation) { - mBubbleBarBackground.showArrow(mIsBarExpanded); - if (!mIsBarExpanded && mReorderRunnable != null) { - mReorderRunnable.run(); - mReorderRunnable = null; - } - // If the bar was just collapsed and the overflow was the last bubble that was - // selected, set the first bubble as selected. - if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null - && mSelectedBubbleView != null - && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) { - BubbleView firstBubble = (BubbleView) getChildAt(0); - mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey()); - } - updateWidth(); - } + addAnimationCallBacks(mWidthAnimator, + /* onStart= */ () -> mBubbleBarBackground.showArrow(true), + /* onEnd= */ () -> { + mBubbleBarBackground.showArrow(mIsBarExpanded); + if (!mIsBarExpanded && mReorderRunnable != null) { + mReorderRunnable.run(); + mReorderRunnable = null; + } + // If the bar was just collapsed and the overflow was the last bubble that was + // selected, set the first bubble as selected. + if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null + && mSelectedBubbleView != null + && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) { + BubbleView firstBubble = (BubbleView) getChildAt(0); + mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey()); + } + updateWidth(); + }, + /* onUpdate= */ animator -> { + updateBubblesLayoutProperties(mBubbleBarLocation); + invalidate(); + }); + } - @Override - public void onAnimationRepeat(Animator animation) { - } - @Override - public void onAnimationStart(Animator animation) { - mBubbleBarBackground.showArrow(true); - } - }); + /** + * Animates icon sizes and spacing between icons and bubble bar borders. + * + * @param newIconSize new icon size + * @param newBubbleBarPadding spacing between icons and bubble bar borders. + */ + public void animateBubbleBarIconSize(float newIconSize, float newBubbleBarPadding) { + if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) { + return; + } + if (!Flags.animateBubbleSizeChange()) { + setIconSizeAndPadding(newIconSize, newBubbleBarPadding); + } + if (mScalePaddingAnimator != null && mScalePaddingAnimator.isRunning()) { + mScalePaddingAnimator.cancel(); + } + ValueAnimator scalePaddingAnimator = ValueAnimator.ofFloat(0f, 1f); + scalePaddingAnimator.setDuration(SCALE_ANIMATION_DURATION_MS); + boolean isPaddingUpdated = isPaddingUpdated(newBubbleBarPadding); + boolean isIconSizeUpdated = isIconSizeUpdated(newIconSize); + float initialScale = mIconScale; + float initialPadding = mBubbleBarPadding; + float targetScale = newIconSize / getScaledIconSize(); + + addAnimationCallBacks(scalePaddingAnimator, + /* onStart= */ null, + /* onEnd= */ () -> setIconSizeAndPadding(newIconSize, newBubbleBarPadding), + /* onUpdate= */ animator -> { + float transitionProgress = (float) animator.getAnimatedValue(); + if (isIconSizeUpdated) { + mIconScale = + initialScale + (targetScale - initialScale) * transitionProgress; + } + if (isPaddingUpdated) { + mBubbleBarPadding = initialPadding + + (newBubbleBarPadding - initialPadding) * transitionProgress; + } + updateBubblesLayoutProperties(mBubbleBarLocation); + invalidate(); + }); + scalePaddingAnimator.start(); + mScalePaddingAnimator = scalePaddingAnimator; } @Override @@ -261,28 +302,37 @@ public class BubbleBarView extends FrameLayout { } /** - * Sets new icon size and spacing between icons and bubble bar borders. + * Sets new icon sizes and newBubbleBarPadding between icons and bubble bar borders. * - * @param newIconSize new icon size - * @param spacing spacing between icons and bubble bar borders. + * @param newIconSize new icon size + * @param newBubbleBarPadding newBubbleBarPadding between icons and bubble bar borders. */ - // TODO(b/335575529): animate bubble bar icons size change - public void setIconSizeAndPadding(float newIconSize, float spacing) { + public void setIconSizeAndPadding(float newIconSize, float newBubbleBarPadding) { // TODO(b/335457839): handle new bubble animation during the size change - mBubbleBarPadding = spacing; + if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) { + return; + } + mIconScale = 1f; + mBubbleBarPadding = newBubbleBarPadding; mIconSize = newIconSize; int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); + childView.setScaleY(mIconScale); + childView.setScaleY(mIconScale); FrameLayout.LayoutParams params = (LayoutParams) childView.getLayoutParams(); params.height = (int) mIconSize; params.width = (int) mIconSize; childView.setLayoutParams(params); } - mBubbleBarBackground.setHeight(getBubbleBarExpandedHeight()); + mBubbleBarBackground.setBackgroundHeight(getBubbleBarHeight()); updateLayoutParams(); } + private float getScaledIconSize() { + return mIconSize * mIconScale; + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -298,7 +348,7 @@ public class BubbleBarView extends FrameLayout { if (!mDragging) { // Position the views when not dragging - updateChildrenRenderNodeProperties(mBubbleBarLocation); + updateBubblesLayoutProperties(mBubbleBarLocation); } } @@ -444,7 +494,7 @@ public class BubbleBarView extends FrameLayout { mBubbleBarLocationAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - updateChildrenRenderNodeProperties(bubbleBarLocation); + updateBubblesLayoutProperties(bubbleBarLocation); mBubbleBarBackground.setAnchorLeft(bubbleBarLocation.isOnLeft(isLayoutRtl())); // Animate it in @@ -613,6 +663,45 @@ public class BubbleBarView extends FrameLayout { mIsAnimatingNewBubble = false; } + /** Add a new bubble to the bubble bar. */ + public void addBubble(View bubble, FrameLayout.LayoutParams lp) { + if (isExpanded()) { + // if we're expanded scale the new bubble in + bubble.setScaleX(0f); + bubble.setScaleY(0f); + addView(bubble, 0, lp); + createNewBubbleScaleInAnimator(bubble); + mNewBubbleScaleInAnimator.start(); + } else { + addView(bubble, 0, lp); + } + } + + private void createNewBubbleScaleInAnimator(View bubble) { + mNewBubbleScaleInAnimator = ValueAnimator.ofFloat(0, 1); + mNewBubbleScaleInAnimator.setDuration(SCALE_IN_ANIMATION_DURATION_MS); + mNewBubbleScaleInAnimator.addUpdateListener(animation -> { + float animatedFraction = animation.getAnimatedFraction(); + bubble.setScaleX(animatedFraction); + bubble.setScaleY(animatedFraction); + updateBubblesLayoutProperties(mBubbleBarLocation); + invalidate(); + }); + mNewBubbleScaleInAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + bubble.setScaleX(1); + bubble.setScaleY(1); + } + + @Override + public void onAnimationEnd(Animator animation) { + updateWidth(); + mNewBubbleScaleInAnimator = null; + } + }); + } + // TODO: (b/280605790) animate it @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { @@ -623,6 +712,8 @@ public class BubbleBarView extends FrameLayout { } super.addView(child, index, params); updateWidth(); + updateBubbleAccessibilityStates(); + updateContentDescription(); } // TODO: (b/283309949) animate it @@ -634,6 +725,8 @@ public class BubbleBarView extends FrameLayout { mBubbleBarBackground.showArrow(false); } updateWidth(); + updateBubbleAccessibilityStates(); + updateContentDescription(); } private void updateWidth() { @@ -649,6 +742,11 @@ public class BubbleBarView extends FrameLayout { setLayoutParams(lp); } + private float getBubbleBarHeight() { + return mIsBarExpanded ? getBubbleBarExpandedHeight() + : getBubbleBarCollapsedHeight(); + } + /** @return the horizontal margin between the bubble bar and the edge of the screen. */ int getHorizontalMargin() { LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); @@ -659,13 +757,16 @@ public class BubbleBarView extends FrameLayout { * Updates the z order, positions, and badge visibility of the bubble views in the bar based * on the expanded state. */ - private void updateChildrenRenderNodeProperties(BubbleBarLocation bubbleBarLocation) { + private void updateBubblesLayoutProperties(BubbleBarLocation bubbleBarLocation) { final float widthState = (float) mWidthAnimator.getAnimatedValue(); final float currentWidth = getWidth(); final float expandedWidth = expandedWidth(); final float collapsedWidth = collapsedWidth(); int bubbleCount = getChildCount(); - final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f; + float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0); + float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight(); + // When translating X & Y the scale is ignored, so need to deduct it from the translations + final float ty = bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift(); final boolean animate = getVisibility() == VISIBLE; final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl()); // elevation state is opposite to widthState - when expanded all icons are flat @@ -680,8 +781,9 @@ public class BubbleBarView extends FrameLayout { bv.setDragTranslationX(0f); bv.setOffsetX(0f); + bv.setScaleX(mIconScale); + bv.setScaleY(mIconScale); bv.setTranslationY(ty); - // the position of the bubble when the bar is fully expanded final float expandedX = getExpandedBubbleTranslationX(i, bubbleCount, onLeft); // the position of the bubble when the bar is fully collapsed @@ -715,6 +817,8 @@ public class BubbleBarView extends FrameLayout { } else if (i == MAX_VISIBLE_BUBBLES_COLLAPSED - 1 && bubbleCount == MAX_VISIBLE_BUBBLES_COLLAPSED) { bv.setAlpha(0); + } else { + bv.setAlpha(1); } } } @@ -742,21 +846,74 @@ public class BubbleBarView extends FrameLayout { } } mBubbleBarBackground.setArrowPosition(arrowPosition); - mBubbleBarBackground.setArrowAlpha((int) (255 * widthState)); + mBubbleBarBackground.setArrowHeightFraction(widthState); mBubbleBarBackground.setWidth(interpolatedWidth); + mBubbleBarBackground.setBackgroundHeight(getBubbleBarExpandedHeight()); } - private float getExpandedBubbleTranslationX(int bubbleIndex, int bubbleCount, - boolean onLeft) { + private float getScaleIconShift() { + return (mIconSize - getScaledIconSize()) / 2; + } + + private float getExpandedBubbleTranslationX(int bubbleIndex, int bubbleCount, boolean onLeft) { if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) { return 0; } - if (onLeft) { - // If bar is on the left, bubbles are ordered right to left - return (bubbleCount - bubbleIndex - 1) * (mIconSize + mExpandedBarIconsSpacing); + final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing; + float translationX; + if (mNewBubbleScaleInAnimator != null && mNewBubbleScaleInAnimator.isRunning()) { + translationX = getExpandedBubbleTranslationXDuringScaleAnimation( + bubbleIndex, bubbleCount, onLeft); + } else if (onLeft) { + translationX = mBubbleBarPadding + (bubbleCount - bubbleIndex - 1) * iconAndSpacing; } else { - // Bubbles ordered left to right, don't move the first bubble - return bubbleIndex * (mIconSize + mExpandedBarIconsSpacing); + translationX = mBubbleBarPadding + bubbleIndex * iconAndSpacing; + } + return translationX - getScaleIconShift(); + } + + /** + * Returns the translation X for the bubble at index {@code bubbleIndex} when the bubble bar is + * expanded and a new bubble is animating in. + * + *

This method assumes that the animation is running so callers are expected to verify that + * before calling it. + */ + private float getExpandedBubbleTranslationXDuringScaleAnimation( + int bubbleIndex, int bubbleCount, boolean onLeft) { + // when the new bubble scale animation is running, a new bubble is animating in while the + // bubble bar is expanded, so we have at least 2 bubbles in the bubble bar - the expanded + // one, and the new one animating in. + + if (mNewBubbleScaleInAnimator == null) { + // callers of this method are expected to verify that the animation is running, but the + // compiler doesn't know that. + return 0; + } + final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing; + final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction(); + // the new bubble is scaling in from the center, so we need to adjust its translation so + // that the distance to the adjacent bubble scales at the same rate. + final float pivotAdjustment = -(1 - newBubbleScale) * getScaledIconSize() / 2f; + + if (onLeft) { + if (bubbleIndex == 0) { + // this is the animating bubble. use scaled spacing between it and the bubble to + // its left + return (bubbleCount - 1) * getScaledIconSize() + + (bubbleCount - 2) * mExpandedBarIconsSpacing + + newBubbleScale * mExpandedBarIconsSpacing + + pivotAdjustment; + } + // when the bubble bar is on the left, only the translation of the right-most bubble + // is affected by the scale animation. + return (bubbleCount - bubbleIndex - 1) * iconAndSpacing; + } else if (bubbleIndex == 0) { + // the bubble bar is on the right, and this is the animating bubble. it only needs + // to be adjusted for the scaling pivot. + return pivotAdjustment; + } else { + return iconAndSpacing * (bubbleIndex - 1 + newBubbleScale); } } @@ -765,13 +922,16 @@ public class BubbleBarView extends FrameLayout { if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) { return 0; } + float translationX; if (onLeft) { // Shift the first bubble only if there are more bubbles in addition to overflow - return bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED - ? mIconOverlapAmount : 0; + translationX = mBubbleBarPadding + ( + bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED + ? mIconOverlapAmount : 0); } else { - return bubbleIndex == 0 ? 0 : mIconOverlapAmount; + translationX = mBubbleBarPadding + (bubbleIndex == 0 ? 0 : mIconOverlapAmount); } + return translationX - getScaleIconShift(); } /** @@ -798,7 +958,8 @@ public class BubbleBarView extends FrameLayout { addViewInLayout(child, i, child.getLayoutParams()); } } - updateChildrenRenderNodeProperties(mBubbleBarLocation); + updateBubblesLayoutProperties(mBubbleBarLocation); + updateContentDescription(); } } @@ -876,15 +1037,9 @@ public class BubbleBarView extends FrameLayout { private float arrowPositionForSelectedWhenExpanded(BubbleBarLocation bubbleBarLocation) { final int index = indexOfChild(mSelectedBubbleView); - final int bubblePosition; - if (bubbleBarLocation.isOnLeft(isLayoutRtl())) { - // Bubble positions are reversed. First bubble is on the right. - bubblePosition = getChildCount() - index - 1; - } else { - bubblePosition = index; - } - return getPaddingStart() + bubblePosition * (mIconSize + mExpandedBarIconsSpacing) - + mIconSize / 2f; + final float selectedBubbleTranslationX = getExpandedBubbleTranslationX( + index, getChildCount(), bubbleBarLocation.isOnLeft(isLayoutRtl())); + return selectedBubbleTranslationX + mIconSize / 2f; } private float arrowPositionForSelectedWhenCollapsed(BubbleBarLocation bubbleBarLocation) { @@ -895,9 +1050,10 @@ public class BubbleBarView extends FrameLayout { // bubbles than the current bubble and overflow. bubblePosition = index == 0 && getChildCount() > MAX_VISIBLE_BUBBLES_COLLAPSED ? 1 : 0; } else { - bubblePosition = index; + bubblePosition = index >= MAX_VISIBLE_BUBBLES_COLLAPSED + ? MAX_VISIBLE_BUBBLES_COLLAPSED - 1 : index; } - return getPaddingStart() + bubblePosition * (mIconOverlapAmount) + mIconSize / 2f; + return mBubbleBarPadding + bubblePosition * (mIconOverlapAmount) + getScaledIconSize() / 2f; } @Override @@ -927,6 +1083,7 @@ public class BubbleBarView extends FrameLayout { } else { mWidthAnimator.reverse(); } + updateBubbleAccessibilityStates(); } } @@ -944,20 +1101,30 @@ public class BubbleBarView extends FrameLayout { */ public float expandedWidth() { final int childCount = getChildCount(); - final int horizontalPadding = getPaddingStart() + getPaddingEnd(); // spaces amount is less than child count by 1, or 0 if no child views - int spacesCount = Math.max(childCount - 1, 0); - return childCount * mIconSize + spacesCount * mExpandedBarIconsSpacing + horizontalPadding; + final float totalSpace; + final float totalIconSize; + if (mNewBubbleScaleInAnimator != null && mNewBubbleScaleInAnimator.isRunning()) { + // when this animation is running, a new bubble is animating in while the bubble bar is + // expanded, so we have at least 2 bubbles in the bubble bar. + final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction(); + totalSpace = (childCount - 2 + newBubbleScale) * mExpandedBarIconsSpacing; + totalIconSize = (childCount - 1 + newBubbleScale) * getScaledIconSize(); + } else { + totalSpace = Math.max(childCount - 1, 0) * mExpandedBarIconsSpacing; + totalIconSize = childCount * getScaledIconSize(); + } + return totalIconSize + totalSpace + 2 * mBubbleBarPadding; } private float collapsedWidth() { final int childCount = getChildCount(); - final int horizontalPadding = getPaddingStart() + getPaddingEnd(); + final float horizontalPadding = 2 * mBubbleBarPadding; // If there are more than 2 bubbles, the first 2 should be visible when collapsed. // Otherwise just the first bubble should be visible because we don't show the overflow. return childCount > MAX_VISIBLE_BUBBLES_COLLAPSED - ? mIconSize + mIconOverlapAmount + horizontalPadding - : mIconSize + horizontalPadding; + ? getScaledIconSize() + mIconOverlapAmount + horizontalPadding + : getScaledIconSize() + horizontalPadding; } private float getBubbleBarExpandedHeight() { @@ -966,7 +1133,7 @@ public class BubbleBarView extends FrameLayout { float getBubbleBarCollapsedHeight() { // the pointer is invisible when collapsed - return mIconSize + mBubbleBarPadding * 2; + return getScaledIconSize() + mBubbleBarPadding * 2; } /** @@ -998,6 +1165,88 @@ public class BubbleBarView extends FrameLayout { return mIsAnimatingNewBubble; } + + private boolean hasOverview() { + // Overview is always the last bubble + View lastChild = getChildAt(getChildCount() - 1); + if (lastChild instanceof BubbleView bubbleView) { + return bubbleView.getBubble() instanceof BubbleBarOverflow; + } + return false; + } + + private void updateBubbleAccessibilityStates() { + final int childA11y; + if (mIsBarExpanded) { + // Bar is expanded, focus on the bubbles + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + childA11y = View.IMPORTANT_FOR_ACCESSIBILITY_YES; + } else { + // Bar is collapsed, only focus on the bar + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + childA11y = View.IMPORTANT_FOR_ACCESSIBILITY_NO; + } + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).setImportantForAccessibility(childA11y); + // Only allowing focusing on bubbles when bar is expanded. Otherwise, in talkback mode, + // bubbles can be navigates to in collapsed mode. + getChildAt(i).setFocusable(mIsBarExpanded); + } + } + + private void updateContentDescription() { + View firstChild = getChildAt(0); + CharSequence contentDesc = firstChild != null ? firstChild.getContentDescription() : ""; + + // Don't count overflow if it exists + int bubbleCount = getChildCount() - (hasOverview() ? 1 : 0); + if (bubbleCount > 1) { + contentDesc = getResources().getString(R.string.bubble_bar_description_multiple_bubbles, + contentDesc, bubbleCount - 1); + } + setContentDescription(contentDesc); + } + + private boolean isIconSizeOrPaddingUpdated(float newIconSize, float newBubbleBarPadding) { + return isIconSizeUpdated(newIconSize) || isPaddingUpdated(newBubbleBarPadding); + } + + private boolean isIconSizeUpdated(float newIconSize) { + return Float.compare(mIconSize, newIconSize) != 0; + } + + private boolean isPaddingUpdated(float newBubbleBarPadding) { + return Float.compare(mBubbleBarPadding, newBubbleBarPadding) != 0; + } + + private void addAnimationCallBacks(@NonNull ValueAnimator animator, + @Nullable Runnable onStart, + @Nullable Runnable onEnd, + @Nullable ValueAnimator.AnimatorUpdateListener onUpdate) { + if (onUpdate != null) animator.addUpdateListener(onUpdate); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationCancel(Animator animator) { + + } + + @Override + public void onAnimationStart(Animator animator) { + if (onStart != null) onStart.run(); + } + + @Override + public void onAnimationEnd(Animator animator) { + if (onEnd != null) onEnd.run(); + } + + @Override + public void onAnimationRepeat(Animator animator) { + + } + }); + } + /** Interface for BubbleBarView to communicate with its controller. */ interface Controller { diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index f614dc66ea..eec095df56 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -98,7 +98,6 @@ public class BubbleBarViewController { mBarView = barView; mSystemUiProxy = SystemUiProxy.INSTANCE.get(mActivity); mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */); - mBubbleBarAlpha.setUpdateVisibility(true); mIconSize = activity.getResources().getDimensionPixelSize( R.dimen.bubblebar_icon_size); } @@ -109,9 +108,11 @@ public class BubbleBarViewController { mBubbleDragController = bubbleControllers.bubbleDragController; mTaskbarStashController = controllers.taskbarStashController; mTaskbarInsetsController = controllers.taskbarInsetsController; + mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); - mActivity.addOnDeviceProfileChangeListener(dp -> setBubbleBarIconSize(dp.taskbarIconSize)); - setBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize); + mActivity.addOnDeviceProfileChangeListener( + dp -> updateBubbleBarIconSize(dp.taskbarIconSize, /* animate= */ true)); + updateBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize, /* animate= */ false); mBubbleBarScale.updateValue(1f); mBubbleClickListener = v -> onBubbleClicked(v); mBubbleBarClickListener = v -> onBubbleBarClicked(); @@ -124,8 +125,6 @@ public class BubbleBarViewController { mBoundsChangeListener.onBoundsChanged(); } }); - - mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); mBarView.setController(new BubbleBarView.Controller() { @Override public float getBubbleBarTranslationY() { @@ -300,33 +299,6 @@ public class BubbleBarViewController { } } - private void setBubbleBarIconSize(int newIconSize) { - if (newIconSize == mIconSize) { - return; - } - Resources res = mActivity.getResources(); - DisplayMetrics dm = res.getDisplayMetrics(); - float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - APP_ICON_SMALL_DP, dm); - float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - APP_ICON_MEDIUM_DP, dm); - float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - APP_ICON_LARGE_DP, dm); - float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f; - float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f; - mIconSize = newIconSize <= smallMediumThreshold - ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) : - res.getDimensionPixelSize(R.dimen.bubblebar_icon_size); - float bubbleBarPadding = newIconSize >= mediumLargeThreshold - ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) : - res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); - - mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding); - mBarView.setPadding((int) bubbleBarPadding, mBarView.getPaddingTop(), - (int) bubbleBarPadding, - mBarView.getPaddingBottom()); - } - /** Sets a callback that updates the selected bubble after the bubble bar collapses. */ public void setUpdateSelectedBubbleAfterCollapse( Consumer updateSelectedBubbleAfterCollapse) { @@ -361,6 +333,30 @@ public class BubbleBarViewController { // Modifying view related properties. // + private void updateBubbleBarIconSize(int newIconSize, boolean animate) { + Resources res = mActivity.getResources(); + DisplayMetrics dm = res.getDisplayMetrics(); + float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + APP_ICON_SMALL_DP, dm); + float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + APP_ICON_MEDIUM_DP, dm); + float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + APP_ICON_LARGE_DP, dm); + float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f; + float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f; + mIconSize = newIconSize <= smallMediumThreshold + ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) : + res.getDimensionPixelSize(R.dimen.bubblebar_icon_size); + float bubbleBarPadding = newIconSize >= mediumLargeThreshold + ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) : + res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); + if (animate) { + mBarView.animateBubbleBarIconSize(mIconSize, bubbleBarPadding); + } else { + mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding); + } + } + /** * Sets the translation of the bubble bar during the swipe up gesture. */ @@ -403,12 +399,24 @@ public class BubbleBarViewController { */ public void addBubble(BubbleBarItem b, boolean isExpanding, boolean suppressAnimation) { if (b != null) { - mBarView.addView(b.getView(), 0, - new FrameLayout.LayoutParams(mIconSize, mIconSize, Gravity.LEFT)); + mBarView.addBubble( + b.getView(), new FrameLayout.LayoutParams(mIconSize, mIconSize, Gravity.LEFT)); b.getView().setOnClickListener(mBubbleClickListener); mBubbleDragController.setupBubbleView(b.getView()); + if (b instanceof BubbleBarOverflow) { + return; + } + if (suppressAnimation || !(b instanceof BubbleBarBubble bubble)) { + // the bubble bar and handle are initialized as part of the first bubble animation. + // if the animation is suppressed, immediately stash or show the bubble bar to + // ensure they've been initialized. + if (mTaskbarStashController.isInApp()) { + mBubbleStashController.stashBubbleBarImmediate(); + } else { + mBubbleStashController.showBubbleBarImmediate(); + } return; } animateBubbleNotification(bubble, isExpanding); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java index 0e6fa3c5e8..a6096e229c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java @@ -169,7 +169,8 @@ public class BubbleDismissController { private void setupMagnetizedObject(@NonNull View magnetizedView) { mMagnetizedObject = new MagnetizedObject<>(mActivity.getApplicationContext(), - magnetizedView, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y) { + magnetizedView, BubbleDragController.DRAG_TRANSLATION_X, + DynamicAnimation.TRANSLATION_Y) { @Override public float getWidth(@NonNull View underlyingObject) { return underlyingObject.getWidth() * underlyingObject.getScaleX(); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java index 287e9067bf..7aed2d2abe 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java @@ -60,7 +60,6 @@ public class BubbleDragAnimator { private final float mBubbleFocusedScale; private final float mBubbleCapturedScale; private final float mDismissCapturedScale; - private final FloatPropertyCompat mTranslationXProperty; /** * Should be initialised for each dragged view @@ -82,28 +81,9 @@ public class BubbleDragAnimator { if (view instanceof BubbleBarView) { mBubbleFocusedScale = SCALE_BUBBLE_BAR_FOCUSED; mBubbleCapturedScale = mDismissCapturedScale; - mTranslationXProperty = DynamicAnimation.TRANSLATION_X; } else { mBubbleFocusedScale = SCALE_BUBBLE_FOCUSED; mBubbleCapturedScale = SCALE_BUBBLE_CAPTURED; - // Wrap BubbleView.DRAG_TRANSLATION_X as it can't be cast to FloatPropertyCompat - mTranslationXProperty = new FloatPropertyCompat<>( - BubbleView.DRAG_TRANSLATION_X.getName()) { - @Override - public float getValue(View object) { - if (object instanceof BubbleView bubbleView) { - return BubbleView.DRAG_TRANSLATION_X.get(bubbleView); - } - return 0; - } - - @Override - public void setValue(View object, float value) { - if (object instanceof BubbleView bubbleView) { - BubbleView.DRAG_TRANSLATION_X.setValue(bubbleView, value); - } - } - }; } } @@ -140,7 +120,7 @@ public class BubbleDragAnimator { mBubbleAnimator .spring(DynamicAnimation.SCALE_X, 1f) .spring(DynamicAnimation.SCALE_Y, 1f) - .spring(mTranslationXProperty, restingPosition.x, velocity.x, + .spring(BubbleDragController.DRAG_TRANSLATION_X, restingPosition.x, velocity.x, mTranslationConfig) .spring(DynamicAnimation.TRANSLATION_Y, restingPosition.y, velocity.y, mTranslationConfig) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java index e04c1b1020..efc747c725 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java @@ -24,6 +24,7 @@ import android.view.ViewConfiguration; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.dynamicanimation.animation.FloatPropertyCompat; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener; @@ -38,6 +39,37 @@ import com.android.wm.shell.common.bubbles.BubbleBarLocation; * Restores initial position of dragged view if released outside of the dismiss target. */ public class BubbleDragController { + + /** + * Property to update dragged bubble x-translation value. + *

+ * When applied to {@link BubbleView}, will use set the translation through + * {@link BubbleView#getDragTranslationX()} and {@link BubbleView#setDragTranslationX(float)} + * methods. + *

+ * When applied to {@link BubbleBarView}, will use {@link View#getTranslationX()} and + * {@link View#setTranslationX(float)}. + */ + public static final FloatPropertyCompat DRAG_TRANSLATION_X = new FloatPropertyCompat<>( + "dragTranslationX") { + @Override + public float getValue(View view) { + if (view instanceof BubbleView bubbleView) { + return bubbleView.getDragTranslationX(); + } + return view.getTranslationX(); + } + + @Override + public void setValue(View view, float value) { + if (view instanceof BubbleView bubbleView) { + bubbleView.setDragTranslationX(value); + } else { + view.setTranslationX(value); + } + } + }; + private final TaskbarActivityContext mActivity; private BubbleBarController mBubbleBarController; private BubbleBarViewController mBubbleBarViewController; @@ -121,6 +153,7 @@ public class BubbleDragController { @Override protected void onDragDismiss() { mBubblePinController.onDragEnd(); + mBubbleBarViewController.onBubbleDragEnd(); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java index 5d01b9bd08..74ddf90afc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java @@ -418,6 +418,7 @@ public class BubbleStashController { /** Stashes the bubble bar immediately without animation. */ public void stashBubbleBarImmediate() { mHandleViewController.setTranslationYForSwipe(0); + mBubbleStashedHandleAlpha.setValue(1); mIconAlphaForStash.setValue(0); mIconTranslationYForStash.updateValue(getStashTranslation()); mIconScaleForStash.updateValue(STASHED_BAR_SCALE); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java index 61a6bceca1..2f92fbbaba 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java @@ -21,14 +21,13 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.Rect; +import android.text.TextUtils; import android.util.AttributeSet; -import android.util.FloatProperty; import android.view.LayoutInflater; import android.view.View; import android.view.ViewOutlineProvider; import android.widget.ImageView; -import androidx.annotation.NonNull; import androidx.constraintlayout.widget.ConstraintLayout; import com.android.launcher3.R; @@ -48,25 +47,6 @@ public class BubbleView extends ConstraintLayout { public static final int DEFAULT_PATH_SIZE = 100; - /** - * Property to update drag translation value. - * - * @see BubbleView#getDragTranslationX() - * @see BubbleView#setDragTranslationX(float) - */ - public static final FloatProperty DRAG_TRANSLATION_X = new FloatProperty<>( - "dragTranslationX") { - @Override - public void setValue(@NonNull BubbleView bubbleView, float value) { - bubbleView.setDragTranslationX(value); - } - - @Override - public Float get(BubbleView bubbleView) { - return bubbleView.getDragTranslationX(); - } - }; - /** * Flags that suppress the visibility of the 'new' dot or the app badge, for one reason or * another. If any of these flags are set, the dot will not be shown. @@ -208,6 +188,16 @@ public class BubbleView extends ConstraintLayout { mAppIcon.setImageBitmap(bubble.getBadge()); mDotColor = bubble.getDotColor(); mDotRenderer = new DotRenderer(mBubbleSize, bubble.getDotPath(), DEFAULT_PATH_SIZE); + String contentDesc = bubble.getInfo().getTitle(); + if (TextUtils.isEmpty(contentDesc)) { + contentDesc = getResources().getString(R.string.bubble_bar_bubble_fallback_description); + } + String appName = bubble.getInfo().getAppName(); + if (!TextUtils.isEmpty(appName)) { + contentDesc = getResources().getString(R.string.bubble_bar_bubble_description, + contentDesc, appName); + } + setContentDescription(contentDesc); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainer.kt new file mode 100644 index 0000000000..3c4b63a1d7 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainer.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.taskbar.customization + +/** Enums for all feature container that taskbar supports. */ +enum class TaskbarContainer { + ALL_APPS, + DIVIDER, + APP_ICONS, + RECENTS, + NAV_BUTTONS, + BUBBLES, +} diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt new file mode 100644 index 0000000000..668a87d6d9 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.taskbar.customization + +import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning +import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.taskbar.TaskbarControllers +import com.android.launcher3.util.DisplayController + +/** Evaluates all the features taskbar can have. */ +class TaskbarFeatureEvaluator( + private val taskbarActivityContext: TaskbarActivityContext, + private val taskbarControllers: TaskbarControllers, +) { + + val hasAllApps = true + val hasAppIcons = true + val hasBubbles = false + val hasNavButtons = taskbarActivityContext.isThreeButtonNav + + val hasRecents: Boolean + get() = taskbarControllers.taskbarRecentAppsController.isEnabled + + val hasDivider: Boolean + get() = enableTaskbarPinning() || hasRecents + + val isTransient: Boolean + get() = DisplayController.isTransientTaskbar(taskbarActivityContext) + + val isLandscape: Boolean + get() = taskbarActivityContext.deviceProfile.isLandscape +} diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt new file mode 100644 index 0000000000..67bbcce7f6 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.taskbar.customization + +/** Taskbar Icon Specs */ +object TaskbarIconSpecs { + + val iconSize40dp = TaskbarIconSize(40) + val iconSize44dp = TaskbarIconSize(44) + val iconSize48dp = TaskbarIconSize(48) + val iconSize52dp = TaskbarIconSize(52) + + val transientTaskbarIconSizes = arrayOf(iconSize44dp, iconSize48dp, iconSize52dp) + + val defaultPersistentIconSize = iconSize40dp + val defaultTransientIconSize = iconSize44dp + + val transientTaskbarIconSizeByGridSize = + mapOf( + TransientTaskbarIconSizeKey(6, 5, false) to iconSize52dp, + TransientTaskbarIconSizeKey(6, 5, true) to iconSize52dp, + TransientTaskbarIconSizeKey(4, 4, false) to iconSize48dp, + TransientTaskbarIconSizeKey(4, 4, true) to iconSize52dp, + TransientTaskbarIconSizeKey(4, 5, false) to iconSize48dp, + TransientTaskbarIconSizeKey(4, 5, true) to iconSize48dp, + TransientTaskbarIconSizeKey(5, 5, false) to iconSize44dp, + TransientTaskbarIconSizeKey(5, 5, true) to iconSize44dp, + ) +} diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt new file mode 100644 index 0000000000..0b7be40826 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.taskbar.customization + +/** Evaluates the taskbar specs based on the taskbar grid size and the taskbar icon size. */ +class TaskbarSpecsEvaluator(private val taskbarFeatureEvaluator: TaskbarFeatureEvaluator) { + + fun getIconSizeByGrid(row: Int, column: Int): TaskbarIconSize { + return if (taskbarFeatureEvaluator.isTransient) { + TaskbarIconSpecs.transientTaskbarIconSizeByGridSize.getOrDefault( + TransientTaskbarIconSizeKey(row, column, taskbarFeatureEvaluator.isLandscape), + TaskbarIconSpecs.defaultTransientIconSize, + ) + } else { + TaskbarIconSpecs.defaultPersistentIconSize + } + } + + fun getIconSizeStepDown(iconSize: TaskbarIconSize): TaskbarIconSize { + if (!taskbarFeatureEvaluator.isTransient) return TaskbarIconSpecs.defaultPersistentIconSize + + val currentIconSizeIndex = TaskbarIconSpecs.transientTaskbarIconSizes.indexOf(iconSize) + // return the current icon size if supplied icon size is unknown or we have reached the + // min icon size. + return if (currentIconSizeIndex == -1 || currentIconSizeIndex == 0) iconSize + else TaskbarIconSpecs.transientTaskbarIconSizes[currentIconSizeIndex - 1] + } + + fun getIconSizeStepUp(iconSize: TaskbarIconSize): TaskbarIconSize { + if (!taskbarFeatureEvaluator.isTransient) return TaskbarIconSpecs.defaultPersistentIconSize + + val currentIconSizeIndex = TaskbarIconSpecs.transientTaskbarIconSizes.indexOf(iconSize) + // return the current icon size if supplied icon size is unknown or we have reached the + // max icon size. + return if ( + currentIconSizeIndex == -1 || + currentIconSizeIndex == TaskbarIconSpecs.transientTaskbarIconSizes.size - 1 + ) { + iconSize + } else { + TaskbarIconSpecs.transientTaskbarIconSizes.get(currentIconSizeIndex + 1) + } + } +} + +data class TaskbarIconSize(val size: Int) + +data class TransientTaskbarIconSizeKey(val row: Int, val column: Int, val isLandscape: Boolean) diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java index adbec65ad3..7eb34a51c8 100644 --- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java +++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java @@ -133,16 +133,19 @@ public final class TaskbarOverlayController { *

* This method should be called after an exit animation finishes, if applicable. */ - @SuppressLint("WrongConstant") void maybeCloseWindow() { - if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL) - || mOverlayContext.getDragController().isSystemDragInProgress())) { - return; - } + if (!canCloseWindow()) return; mProxyView.close(false); onDestroy(); } + @SuppressLint("WrongConstant") + private boolean canCloseWindow() { + if (mOverlayContext == null) return true; + if (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)) return false; + return !mOverlayContext.getDragController().isSystemDragInProgress(); + } + /** Destroys the controller and any overlay window if present. */ public void onDestroy() { TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); @@ -212,10 +215,17 @@ public final class TaskbarOverlayController { @Override protected void handleClose(boolean animate) { - if (mIsOpen) { - mTaskbarContext.getDragLayer().removeView(this); - Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate)); - } + if (!mIsOpen) return; + mTaskbarContext.getDragLayer().removeView(this); + Optional.ofNullable(mOverlayContext).ifPresent(c -> { + if (canCloseWindow()) { + onDestroy(); // Window is already ready to be destroyed. + } else { + // Close window's AFVs before destroying it. Its drag layer will attempt to + // close the proxy view again once its children are removed. + closeAllOpenViews(c, animate); + } + }); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index e3a2bab824..2168f7a318 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -1118,7 +1118,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer } @Override - protected void collectStateHandlers(List out) { + public void collectStateHandlers(List> out) { super.collectStateHandlers(out); out.add(getDepthController()); out.add(new RecentsViewStateController(this)); diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt index 146ff3da50..04696364e0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt +++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt @@ -17,24 +17,32 @@ package com.android.launcher3.uioverrides import android.app.ActivityOptions import android.app.PendingIntent +import android.app.role.RoleManager import android.content.Context +import android.content.IIntentReceiver +import android.content.IIntentSender import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.LauncherActivityInfo import android.content.pm.LauncherApps import android.content.pm.ShortcutInfo +import android.os.Bundle import android.os.Flags.allowPrivateProfile +import android.os.IBinder import android.os.UserHandle import android.os.UserManager import android.util.ArrayMap +import android.widget.Toast import android.window.RemoteTransition import com.android.launcher3.Flags.enablePrivateSpace import com.android.launcher3.Flags.enablePrivateSpaceInstallShortcut import com.android.launcher3.Flags.privateSpaceAppInstallerButton import com.android.launcher3.Flags.privateSpaceSysAppsSeparation +import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.proxy.ProxyActivityStarter import com.android.launcher3.util.ApiWrapper +import com.android.launcher3.util.Executors import com.android.launcher3.util.StartActivityParams import com.android.launcher3.util.UserIconInfo import com.android.quickstep.util.FadeOutRemoteTransition @@ -115,8 +123,7 @@ open class SystemApiWrapper(context: Context?) : ApiWrapper(context) { intentSender = mContext .getSystemService(LauncherApps::class.java) - ?.privateSpaceSettingsIntent - ?: return null + ?.privateSpaceSettingsIntent ?: return null options = ActivityOptions.makeBasic() .setPendingIntentBackgroundActivityStartMode( @@ -130,4 +137,50 @@ open class SystemApiWrapper(context: Context?) : ApiWrapper(context) { override fun isNonResizeableActivity(lai: LauncherActivityInfo) = lai.activityInfo.resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE + + /** + * Starts an Activity which can be used to set this Launcher as the HOME app, via a consent + * screen. In case the consent screen cannot be shown, or the user does not set current Launcher + * as HOME app, a toast asking the user to do the latter is shown. + */ + override fun assignDefaultHomeRole(context: Context) { + val roleManager = context.getSystemService(RoleManager::class.java) + if ( + (roleManager!!.isRoleAvailable(RoleManager.ROLE_HOME) && + !roleManager.isRoleHeld(RoleManager.ROLE_HOME)) + ) { + val roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME) + val pendingIntent = + PendingIntent( + object : IIntentSender.Stub() { + override fun send( + code: Int, + intent: Intent, + resolvedType: String?, + allowlistToken: IBinder?, + finishedReceiver: IIntentReceiver?, + requiredPermission: String?, + options: Bundle? + ) { + if (code != -1) { + Executors.MAIN_EXECUTOR.execute { + Toast.makeText( + context, + context.getString( + R.string.set_default_home_app, + context.getString(R.string.derived_app_name) + ), + Toast.LENGTH_LONG + ) + .show() + } + } + } + } + ) + val params = StartActivityParams(pendingIntent, 0) + params.intent = roleRequestIntent + context.startActivity(ProxyActivityStarter.getLaunchIntent(context, params)) + } + } } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 463222dfd0..1acafab217 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -32,6 +32,7 @@ import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.Flags.enableAdditionalHomeAnimations; import static com.android.launcher3.Flags.enableGridOnlyOverview; +import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.PagedView.INVALID_PAGE; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; @@ -1193,6 +1194,10 @@ public abstract class AbsSwipeUpHandler