Merge "Wait in ReorderWidgets test for the workapce to load after changing it." into tm-qpr-dev
diff --git a/Android.bp b/Android.bp
index 6267e9f..0bbb3d2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -153,6 +153,7 @@
"androidx.cardview_cardview",
"com.google.android.material_material",
"iconloader_base",
+ "view_capture"
],
manifest: "AndroidManifest-common.xml",
sdk_version: "current",
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 02b83fe..951be4e 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -43,7 +43,8 @@
<!-- for rotating surface by arbitrary degree -->
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
-
+ <uses-permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA" />
+
<!--
Permissions required for read/write access to the workspace data. These permission name
should not conflict with that defined in other apps, as such an app should embed its package
diff --git a/protos/view_capture.proto b/protos/view_capture.proto
deleted file mode 100644
index f363f36..0000000
--- a/protos/view_capture.proto
+++ /dev/null
@@ -1,59 +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.
- */
-
-syntax = "proto2";
-
-package com.android.launcher3.view;
-
-option java_outer_classname = "ViewCaptureData";
-
-message ExportedData {
-
- repeated FrameData frameData = 1;
- repeated string classname = 2;
-}
-
-message FrameData {
- optional int64 timestamp = 1;
- optional ViewNode node = 2;
-}
-
-message ViewNode {
- optional int32 classname_index = 1;
- optional int32 hashcode = 2;
-
- repeated ViewNode children = 3;
-
- optional string id = 4;
- optional int32 left = 5;
- optional int32 top = 6;
- optional int32 width = 7;
- optional int32 height = 8;
- optional int32 scrollX = 9;
- optional int32 scrollY = 10;
-
- optional float translationX = 11;
- optional float translationY = 12;
- optional float scaleX = 13 [default = 1];
- optional float scaleY = 14 [default = 1];
- optional float alpha = 15 [default = 1];
-
- optional bool willNotDraw = 16;
- optional bool clipChildren = 17;
- optional int32 visibility = 18;
-
- optional float elevation = 19;
-}
diff --git a/quickstep/res/drawable/ic_floating_task_button.xml b/quickstep/res/drawable/ic_floating_task_button.xml
deleted file mode 100644
index e50f65c..0000000
--- a/quickstep/res/drawable/ic_floating_task_button.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z"
- android:fillColor="#636C6F"/>
- <path
- android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z"
- android:fillColor="#636C6F"
- android:fillType="evenOdd"/>
-</vector>
diff --git a/quickstep/res/layout-sw600dp-land/allset_navigation_and_hint.xml b/quickstep/res/layout-sw600dp-land/allset_navigation_and_hint.xml
new file mode 100644
index 0000000..3bfa6da
--- /dev/null
+++ b/quickstep/res/layout-sw600dp-land/allset_navigation_and_hint.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <TextView
+ android:id="@+id/navigation_settings"
+ style="@style/TextAppearance.GestureTutorial.LinkText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="32dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:minHeight="48dp"
+ android:text="@string/allset_navigation_settings"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/subtitle" />
+
+ <TextView
+ android:id="@+id/hint"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/allset_page_margin_bottom"
+ android:text="@string/allset_hint"
+ android:textSize="@dimen/allset_page_swipe_up_text_size"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml b/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
new file mode 100644
index 0000000..9559072
--- /dev/null
+++ b/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <TextView
+ android:id="@+id/navigation_settings"
+ style="@style/TextAppearance.GestureTutorial.LinkText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="96dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:minHeight="48dp"
+ android:text="@string/allset_navigation_settings"
+ app:layout_constraintBottom_toTopOf="@id/hint"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/hint"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/allset_page_margin_bottom"
+ android:text="@string/allset_hint"
+ android:textSize="@dimen/allset_page_swipe_up_text_size"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 56e1d16..f08cabe 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -34,8 +34,6 @@
android:layout_height="match_parent"
android:gravity="center"
android:scaleType="centerCrop"
-
- app:lottie_rawRes="@raw/all_set_page_bg"
app:lottie_autoPlay="true"
app:lottie_loop="true" />
@@ -79,42 +77,8 @@
app:layout_constraintStart_toStartOf="parent"
android:gravity="start"/>
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/navigation_settings_guideline_bottom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- app:layout_constraintGuide_percent="0.83" />
+ <include layout="@layout/allset_navigation_and_hint"/>
- <TextView
- android:id="@+id/navigation_settings"
- style="@style/TextAppearance.GestureTutorial.LinkText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
- android:minHeight="48dp"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/allset_navigation_settings" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/hint_guideline_bottom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- app:layout_constraintGuide_percent="0.94" />
-
- <TextView
- android:id="@+id/hint"
- style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
- android:textSize="14sp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
- android:text="@string/allset_hint"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/quickstep/res/layout/allset_navigation_and_hint.xml b/quickstep/res/layout/allset_navigation_and_hint.xml
new file mode 100644
index 0000000..4d5cf01
--- /dev/null
+++ b/quickstep/res/layout/allset_navigation_and_hint.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/navigation_settings_guideline_bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.83" />
+
+ <TextView
+ android:id="@+id/navigation_settings"
+ style="@style/TextAppearance.GestureTutorial.LinkText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:minHeight="48dp"
+ android:text="@string/allset_navigation_settings"
+ app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/hint_guideline_bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.94" />
+
+ <TextView
+ android:id="@+id/hint"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/allset_hint"
+ android:textSize="@dimen/allset_page_swipe_up_text_size"
+ app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index c4642e4..d5e3670 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -25,4 +25,6 @@
android:gravity="center"
android:importantForAccessibility="noHideDescendants"
android:textColor="?priv-android:attr/textColorOnAccent"
- android:textSize="14sp"/>
\ No newline at end of file
+ android:textSize="14sp"
+ android:autoSizeTextType="uniform"
+ android:autoSizeMaxTextSize="14sp"/>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_all_apps_button.xml b/quickstep/res/layout/taskbar_all_apps_button.xml
new file mode 100644
index 0000000..b275305
--- /dev/null
+++ b/quickstep/res/layout/taskbar_all_apps_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<com.android.launcher3.views.IconButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/taskbar_icon_touch_size"
+ android:layout_height="@dimen/taskbar_icon_touch_size"
+ android:contentDescription="@string/all_apps_button_label"
+ android:backgroundTint="@color/all_apps_button_bg_color"
+ android:icon="@drawable/ic_all_apps_button"
+ />
diff --git a/quickstep/res/layout/transient_taskbar.xml b/quickstep/res/layout/transient_taskbar.xml
new file mode 100644
index 0000000..f9ece84
--- /dev/null
+++ b/quickstep/res/layout/transient_taskbar.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<com.android.launcher3.taskbar.TaskbarDragLayer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/taskbar_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false">
+
+ <com.android.launcher3.taskbar.TaskbarView
+ android:id="@+id/taskbar_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:forceHasOverlappingRendering="false"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="@dimen/transient_taskbar_margin"
+ android:clipChildren="false" />
+
+ <com.android.launcher3.taskbar.TaskbarScrimView
+ android:id="@+id/taskbar_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <FrameLayout
+ android:id="@+id/navbuttons_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" >
+
+ <FrameLayout
+ android:id="@+id/start_contextual_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/taskbar_contextual_button_padding"
+ android:paddingEnd="@dimen/taskbar_contextual_button_padding"
+ android:paddingTop="@dimen/taskbar_contextual_padding_top"
+ android:gravity="center_vertical"
+ android:layout_gravity="start"/>
+
+ <LinearLayout
+ android:id="@+id/end_nav_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_gravity="end"/>
+
+ <FrameLayout
+ android:id="@+id/end_contextual_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/taskbar_contextual_padding_top"
+ android:gravity="center_vertical"
+ android:layout_gravity="end"/>
+ </FrameLayout>
+
+ <com.android.launcher3.taskbar.StashedHandleView
+ android:id="@+id/stashed_handle"
+ tools:comment1="The actual size and shape will be set as a ViewOutlineProvider at runtime"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/taskbar_stashed_handle_dark_color"
+ android:clipToOutline="true"
+ android:layout_gravity="bottom"/>
+
+</com.android.launcher3.taskbar.TaskbarDragLayer>
\ No newline at end of file
diff --git a/quickstep/res/raw-sw600dp-land/all_set_page_bg.json b/quickstep/res/raw-sw600dp-land/all_set_page_bg.json
new file mode 100644
index 0000000..0863c31
--- /dev/null
+++ b/quickstep/res/raw-sw600dp-land/all_set_page_bg.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":180,"w":1280,"h":800,"nm":"3Second_MainWelcomeScreen_Tablet_Landscape_V02","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[288,540,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[25,25,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"colorAccentPrimaryVariant","parent":1,"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":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[231.832,-1174.545,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":95,"s":[231.832,-1979,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[231.832,-1174.545,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[110,110,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":720,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"colorAccentPrimary","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":95,"s":[-38]},{"t":180,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[138]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":95,"s":[-38]},{"t":180,"s":[138]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1535]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":95,"s":[1338]},{"t":180,"s":[1535]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/quickstep/res/raw-sw600dp/all_set_page_bg.json b/quickstep/res/raw-sw600dp/all_set_page_bg.json
new file mode 100644
index 0000000..14e8933
--- /dev/null
+++ b/quickstep/res/raw-sw600dp/all_set_page_bg.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":180,"w":800,"h":1280,"nm":"3Second_MainWelcomeScreen_Tablet_Portrait_V02","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Null 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[288,528,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[25,25,100],"ix":6,"l":2}},"ao":0,"sw":100,"sh":100,"sc":"#ffffff","ip":600,"op":600,"st":0,"bm":0,"hidden":0},{"ddd":0,"ind":2,"ty":4,"nm":".colorAccentPrimaryVariant","cl":"colorAccentPrimaryVariant","parent":1,"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":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[375.832,-1366.545,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":95,"s":[375.832,-2171,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[375.832,-1366.545,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[135,135,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":720,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":95,"s":[-38]},{"t":180,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[138]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":95,"s":[-38]},{"t":180,"s":[138]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1535]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":95,"s":[1338]},{"t":180,"s":[1535]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/res/raw/all_set_page_bg.json b/quickstep/res/raw/all_set_page_bg.json
similarity index 99%
rename from res/raw/all_set_page_bg.json
rename to quickstep/res/raw/all_set_page_bg.json
index 9705837..859d356 100644
--- a/res/raw/all_set_page_bg.json
+++ b/quickstep/res/raw/all_set_page_bg.json
@@ -1 +1 @@
-{"v":"5.7.8","fr":24,"ip":0,"op":72,"w":2472,"h":5352,"nm":"3Second_MAIN_Welcome","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 60","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1508,1364,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,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"PinkFlower","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":72,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[1505.832,1379.455,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":38,"s":[1505.832,575,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[1505.832,1379.455,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,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":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.839215746113,0.439215716194,0.388235324037,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":288,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse_Bottom","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":38,"s":[-38]},{"t":72,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1720]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":38,"s":[1544]},{"t":72,"s":[1720]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[4069]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":38,"s":[3872]},{"t":72,"s":[4069]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.882353001015,0.894118006089,0.886274988511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":240,"st":0,"bm":0}],"markers":[]}
+{"v":"5.7.8","fr":24,"ip":0,"op":72,"w":2472,"h":5352,"nm":"3Second_MAIN_Welcome","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 60","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1508,1364,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,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"PinkFlower","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":72,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[1505.832,1379.455,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":38,"s":[1505.832,575,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[1505.832,1379.455,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,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":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.839215746113,0.439215716194,0.388235324037,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":288,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse_Bottom","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":38,"s":[-38]},{"t":72,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1720]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":38,"s":[1544]},{"t":72,"s":[1720]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[4069]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":38,"s":[3872]},{"t":72,"s":[4069]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.882353001015,0.894118006089,0.886274988511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":240,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 0cd9b2b..5507fcf 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -17,4 +17,9 @@
<resources>
<!-- Overview actions -->
<dimen name="overview_actions_top_margin">12dp</dimen>
+
+ <!-- All Set page -->
+ <dimen name="allset_page_margin_horizontal">48dp</dimen>
+ <dimen name="allset_page_margin_bottom">24dp</dimen>
+
</resources>
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index cfbbf8d..c96ad11 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -33,4 +33,11 @@
<dimen name="overview_page_spacing">36dp</dimen>
<!-- The space to the left and to the right of the "Clear all" button -->
<dimen name="overview_grid_side_margin">64dp</dimen>
+
+ <!-- All Set page -->
+ <dimen name="allset_page_margin_horizontal">120dp</dimen>
+ <dimen name="allset_page_margin_bottom">24dp</dimen>
+ <dimen name="allset_page_allset_text_size">38sp</dimen>
+ <dimen name="allset_page_swipe_up_text_size">15sp</dimen>
+
</resources>
diff --git a/quickstep/res/values-sw720dp-land/dimens.xml b/quickstep/res/values-sw720dp-land/dimens.xml
index 02d1189..4bc8bf3 100644
--- a/quickstep/res/values-sw720dp-land/dimens.xml
+++ b/quickstep/res/values-sw720dp-land/dimens.xml
@@ -17,4 +17,7 @@
<resources>
<!-- Overview actions -->
<dimen name="overview_actions_top_margin">20dp</dimen>
+
+ <!-- All Set page-->
+ <dimen name="allset_page_margin_bottom">24dp</dimen>
</resources>
diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml
index 284ce11..a84b939 100644
--- a/quickstep/res/values-sw720dp/dimens.xml
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -33,4 +33,9 @@
<dimen name="overview_page_spacing">44dp</dimen>
<!-- The space to the left and to the right of the "Clear all" button -->
<dimen name="overview_grid_side_margin">64dp</dimen>
+
+ <!-- All Set page-->
+ <dimen name="allset_page_margin_bottom">0dp</dimen>
+ <dimen name="allset_page_allset_text_size">42sp</dimen>
+ <dimen name="allset_page_swipe_up_text_size">16sp</dimen>
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3add4dc..3225078 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -202,6 +202,10 @@
<!-- All Set page -->
<dimen name="allset_page_margin_horizontal">40dp</dimen>
+ <dimen name="allset_page_margin_bottom">0dp</dimen>
+ <dimen name="allset_page_allset_text_size">36sp</dimen>
+ <dimen name="allset_page_swipe_up_text_size">14sp</dimen>
+
<dimen name="allset_title_margin_top">24dp</dimen>
<dimen name="allset_title_icon_margin_top">32dp</dimen>
<dimen name="allset_subtitle_margin_top">24dp</dimen>
@@ -279,6 +283,17 @@
<dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
<dimen name="taskbar_icon_size_kids">32dp</dimen>
+ <!-- Transient taskbar -->
+ <dimen name="transient_taskbar_size">76dp</dimen>
+ <dimen name="transient_taskbar_margin">24dp</dimen>
+ <dimen name="transient_taskbar_shadow_blur">40dp</dimen>
+ <dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
+ <!-- Taskbar swipe up thresholds -->
+ <dimen name="taskbar_app_window_threshold">150dp</dimen>
+ <dimen name="taskbar_home_overview_threshold">225dp</dimen>
+ <dimen name="taskbar_catch_up_threshold">300dp</dimen>
+ <dimen name="taskbar_nav_threshold">40dp</dimen>
+
<!-- Taskbar 3 button spacing -->
<dimen name="taskbar_button_space_inbetween">24dp</dimen>
<dimen name="taskbar_button_space_inbetween_phone">40dp</dimen>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 7225220..868d38b 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -51,6 +51,7 @@
parent="TextAppearance.GestureTutorial.Feedback.Title">
<item name="android:letterSpacing">0.03</item>
<item name="android:lineHeight">44sp</item>
+ <item name="android:textSize">@dimen/allset_page_allset_text_size</item>
</style>
<style name="TextAppearance.GestureTutorial.Dialog.Title"
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 62603e9..95a94ec 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -28,13 +28,13 @@
import android.content.Context;
import android.os.Build;
import android.os.Handler;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.ref.WeakReference;
@@ -55,7 +55,7 @@
* reference to the runner, leaving only the weak ref from the runner.
*/
@TargetApi(Build.VERSION_CODES.P)
-public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat {
private static final RemoteAnimationFactory DEFAULT_FACTORY =
(transit, appTargets, wallpaperTargets, nonAppTargets, result) ->
@@ -82,9 +82,9 @@
@BinderThread
public void onAnimationStart(
int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
Runnable runnable) {
Runnable r = () -> {
finishExistingAnimation();
@@ -99,22 +99,6 @@
}
}
- // Called only in R platform
- @BinderThread
- public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
- onAnimationStart(0 /* transit */, appTargets, wallpaperTargets,
- new RemoteAnimationTargetCompat[0], runnable);
- }
-
- // Called only in Q platform
- @BinderThread
- @Deprecated
- public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable) {
- onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable);
- }
-
-
private RemoteAnimationFactory getFactory() {
RemoteAnimationFactory factory = mFactory.get();
return factory != null ? factory : DEFAULT_FACTORY;
@@ -133,7 +117,7 @@
*/
@BinderThread
@Override
- public void onAnimationCancelled() {
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
postAsyncCallback(mHandler, () -> {
finishExistingAnimation();
getFactory().onAnimationCancelled();
@@ -229,9 +213,9 @@
* call {@link AnimationResult#setAnimation} with the target animation to be run.
*/
void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result);
/**
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index c4e85f6..28bd701 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -19,11 +19,11 @@
import android.annotation.TargetApi;
import android.os.Build;
import android.os.CancellationSignal;
+import android.view.RemoteAnimationTarget;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate;
@@ -52,8 +52,8 @@
CancellationSignal cancellationSignal = new CancellationSignal();
appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
@Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
// On the first call clear the reference.
cancellationSignal.cancel();
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index d6d31c4..1b47939 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -16,9 +16,19 @@
package com.android.launcher3;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
@@ -42,15 +52,13 @@
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
-import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -81,6 +89,9 @@
import android.util.Pair;
import android.util.Size;
import android.view.CrossWindowBlurListeners;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
@@ -89,6 +100,8 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.window.RemoteTransition;
+import android.window.TransitionFilter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -108,6 +121,7 @@
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.RunnableList;
@@ -123,6 +137,8 @@
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.FloatingWidgetView;
@@ -130,12 +146,7 @@
import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.util.ArrayList;
@@ -214,7 +225,7 @@
private RemoteAnimationFactory mKeyguardGoingAwayRunner;
private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
- private RemoteTransitionCompat mLauncherOpenTransition;
+ private RemoteTransition mLauncherOpenTransition;
private LauncherBackAnimationController mBackAnimationController;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@@ -291,12 +302,10 @@
long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
- STATUS_BAR_TRANSITION_PRE_DELAY;
- RemoteAnimationAdapterCompat adapterCompat =
- new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay,
- mLauncher.getIApplicationThread());
ActivityOptions options = ActivityOptions.makeRemoteAnimation(
- adapterCompat.getWrapped(),
- adapterCompat.getRemoteTransition().getTransition());
+ new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
+ new RemoteTransition(runner.toRemoteTransition(),
+ mLauncher.getIApplicationThread()));
return new ActivityOptionsWrapper(options, onEndCallback);
}
@@ -311,7 +320,7 @@
* @return true if the app is launching from recents, false if it most likely is not
*/
protected boolean isLaunchingFromRecents(@NonNull View v,
- @Nullable RemoteAnimationTargetCompat[] targets) {
+ @Nullable RemoteAnimationTarget[] targets) {
return mLauncher.getStateManager().getState().overviewUi
&& findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
}
@@ -325,18 +334,18 @@
* @param launcherClosing true if the launcher app is closing
*/
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing) {
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) {
TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
nonAppTargets, launcherClosing, mLauncher.getStateManager(),
mLauncher.getOverviewPanel(), mLauncher.getDepthController());
}
- private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
+ private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTarget[] targets) {
boolean isAllOpeningTargetTrs = true;
for (int i = 0; i < targets.length; i++) {
- RemoteAnimationTargetCompat target = targets[i];
+ RemoteAnimationTarget target = targets[i];
if (target.mode == MODE_OPENING) {
isAllOpeningTargetTrs &= target.isTranslucent;
}
@@ -354,21 +363,18 @@
* @param launcherClosing true if launcher is closing
*/
private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
boolean launcherClosing) {
// Set the state animation first so that any state listeners are called
// before our internal listeners.
mLauncher.getStateManager().setCurrentAnimation(anim);
- final int rotationChange = getRotationChange(appTargets);
// Note: the targetBounds are relative to the launcher
int startDelay = getSingleFrameMs(mLauncher);
- Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
- Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets,
- nonAppTargets, windowTargetBounds, areAllTargetsTranslucent(appTargets),
- rotationChange);
+ Animator windowAnimator = getOpeningWindowAnimators(
+ v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);
windowAnimator.setStartDelay(startDelay);
anim.play(windowAnimator);
if (launcherClosing) {
@@ -382,40 +388,19 @@
launcherContentAnimator.second.run();
}
});
- } else {
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mLauncher.addOnResumeCallback(() ->
- ObjectAnimator.ofFloat(mLauncher.getDepthController(), STATE_DEPTH,
- mLauncher.getStateManager().getState().getDepth(
- mLauncher)).start());
- }
- });
}
}
private void composeWidgetLaunchAnimator(
@NonNull AnimatorSet anim,
@NonNull LauncherAppWidgetHostView v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets) {
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
+ boolean launcherClosing) {
mLauncher.getStateManager().setCurrentAnimation(anim);
-
- Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
- anim.play(getOpeningWindowAnimatorsForWidget(v, appTargets, wallpaperTargets, nonAppTargets,
- windowTargetBounds, areAllTargetsTranslucent(appTargets)));
-
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mLauncher.addOnResumeCallback(() ->
- ObjectAnimator.ofFloat(mLauncher.getDepthController(), STATE_DEPTH,
- mLauncher.getStateManager().getState().getDepth(
- mLauncher)).start());
- }
- });
+ anim.play(getOpeningWindowAnimatorsForWidget(
+ v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing));
}
/**
@@ -423,10 +408,10 @@
* In multiwindow mode, we need to get the final size of the opening app window target to help
* figure out where the floating view should animate to.
*/
- private Rect getWindowTargetBounds(@NonNull RemoteAnimationTargetCompat[] appTargets,
+ private Rect getWindowTargetBounds(@NonNull RemoteAnimationTarget[] appTargets,
int rotationChange) {
- RemoteAnimationTargetCompat target = null;
- for (RemoteAnimationTargetCompat t : appTargets) {
+ RemoteAnimationTarget target = null;
+ for (RemoteAnimationTarget t : appTargets) {
if (t.mode != MODE_OPENING) continue;
target = t;
break;
@@ -448,7 +433,9 @@
4 - rotationChange);
}
}
- if (mDeviceProfile.isTaskbarPresentInApps && !target.willShowImeOnTarget) {
+ if (mDeviceProfile.isTaskbarPresentInApps
+ && !target.willShowImeOnTarget
+ && !DisplayController.isTransientTaskbar(mLauncher)) {
// Animate to above the taskbar.
bounds.bottom -= target.contentInsets.bottom;
}
@@ -648,10 +635,14 @@
* @return Animator that controls the window of the opening targets from app icons.
*/
private Animator getOpeningWindowAnimators(View v,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
- Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
+ boolean launcherClosing) {
+ int rotationChange = getRotationChange(appTargets);
+ Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
+ boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);
+
RectF launcherIconBounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
!appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
@@ -663,7 +654,7 @@
SurfaceTransactionApplier surfaceApplier =
new SurfaceTransactionApplier(floatingView);
openingTargets.addReleaseCheck(surfaceApplier);
- RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+ RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
int[] dragLayerBounds = new int[2];
mDragLayer.getLocationOnScreen(dragLayerBounds);
@@ -815,10 +806,11 @@
return;
}
- ArrayList<SurfaceParams> params = new ArrayList<>();
+ SurfaceTransaction transaction = new SurfaceTransaction();
+
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = appTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale);
@@ -839,14 +831,13 @@
floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f - mIconAlpha.value)
- .withCornerRadius(mWindowRadius.value)
- .withShadowRadius(mShadowRadius.value);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f - mIconAlpha.value)
+ .setCornerRadius(mWindowRadius.value)
+ .setShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_CLOSING) {
if (target.localBounds != null) {
- final Rect localBounds = target.localBounds;
tmpPos.set(target.localBounds.left, target.localBounds.top);
} else {
tmpPos.set(target.position.x, target.position.y);
@@ -863,29 +854,26 @@
tmpPos.y = tmp;
}
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f);
}
- params.add(builder.build());
}
if (navBarTarget != null) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+ SurfaceProperties navBuilder =
+ transaction.forSurface(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
matrix.setScale(scale, scale);
matrix.postTranslate(windowTransX0, windowTransY0);
- navBuilder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- params.add(navBuilder.build());
}
-
- surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+ surfaceApplier.scheduleApply(transaction);
}
};
appAnimator.addUpdateListener(listener);
@@ -894,7 +882,7 @@
// If app targets are translucent, do not animate the background as it causes a visible
// flicker when it resets itself at the end of its animation.
- if (appTargetsAreTranslucent) {
+ if (appTargetsAreTranslucent || !launcherClosing) {
animatorSet.play(appAnimator);
} else {
animatorSet.playTogether(appAnimator, getBackgroundAnimator());
@@ -903,17 +891,19 @@
}
private Animator getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, Rect windowTargetBounds,
- boolean appTargetsAreTranslucent) {
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) {
+ Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
+ boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);
+
final RectF widgetBackgroundBounds = new RectF();
final Rect appWindowCrop = new Rect();
final Matrix matrix = new Matrix();
RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
wallpaperTargets, nonAppTargets, MODE_OPENING);
- RemoteAnimationTargetCompat openingTarget = openingTargets.getFirstAppTarget();
+ RemoteAnimationTarget openingTarget = openingTargets.getFirstAppTarget();
int fallbackBackgroundColor = 0;
if (openingTarget != null && supportsSSplashScreen()) {
fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
@@ -937,7 +927,7 @@
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
openingTargets.addReleaseCheck(surfaceApplier);
- RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+ RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
AnimatorSet animatorSet = new AnimatorSet();
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
@@ -1001,43 +991,39 @@
matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
widgetBackgroundBounds.top);
- ArrayList<SurfaceParams> params = new ArrayList<>();
+ SurfaceTransaction transaction = new SurfaceTransaction();
float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = appTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.mode == MODE_OPENING) {
floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
mCornerRadiusProgress.value);
- builder.withMatrix(matrix)
- .withWindowCrop(appWindowCrop)
- .withAlpha(mPreviewAlpha.value)
- .withCornerRadius(mWindowRadius.value / mAppWindowScale);
+ builder.setMatrix(matrix)
+ .setWindowCrop(appWindowCrop)
+ .setAlpha(mPreviewAlpha.value)
+ .setCornerRadius(mWindowRadius.value / mAppWindowScale);
}
- params.add(builder.build());
}
if (navBarTarget != null) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+ SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
- navBuilder.withMatrix(matrix)
- .withWindowCrop(appWindowCrop)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(matrix)
+ .setWindowCrop(appWindowCrop)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- params.add(navBuilder.build());
}
-
- surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+ surfaceApplier.scheduleApply(transaction);
}
});
// If app targets are translucent, do not animate the background as it causes a visible
// flicker when it resets itself at the end of its animation.
- if (appTargetsAreTranslucent) {
+ if (appTargetsAreTranslucent || !launcherClosing) {
animatorSet.play(appAnimator);
} else {
animatorSet.playTogether(appAnimator, getBackgroundAnimator());
@@ -1055,8 +1041,8 @@
&& BlurUtils.supportsBlursOnWindows();
MyDepthController depthController = new MyDepthController(mLauncher);
- ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, STATE_DEPTH,
- BACKGROUND_APP.getDepth(mLauncher))
+ ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth,
+ MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher))
.setDuration(APP_LAUNCH_DURATION);
if (allowBlurringLauncher) {
@@ -1095,28 +1081,26 @@
if (hasControlRemoteAppTransitionPermission()) {
mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
- RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
definition.addRemoteAnimation(WindowManager.TRANSIT_OLD_WALLPAPER_OPEN,
WindowConfiguration.ACTIVITY_TYPE_STANDARD,
- new RemoteAnimationAdapterCompat(
+ new RemoteAnimationAdapter(
new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
false /* startAtFrontOfQueue */),
- CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
- mLauncher.getIApplicationThread()));
+ CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
if (KEYGUARD_ANIMATION.get()) {
mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
definition.addRemoteAnimation(
WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
- new RemoteAnimationAdapterCompat(
+ new RemoteAnimationAdapter(
new LauncherAnimationRunner(
mHandler, mKeyguardGoingAwayRunner,
true /* startAtFrontOfQueue */),
- CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
- mLauncher.getIApplicationThread()));
+ CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
}
- mLauncher.registerRemoteAnimations(definition.getWrapped());
+ mLauncher.registerRemoteAnimations(definition);
}
}
@@ -1129,11 +1113,25 @@
}
if (hasControlRemoteAppTransitionPermission()) {
mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
- mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
+ mLauncherOpenTransition = new RemoteTransition(
new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
- false /* startAtFrontOfQueue */), mLauncher.getIApplicationThread());
- mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
- SystemUiProxy.INSTANCE.get(mLauncher).registerRemoteTransition(mLauncherOpenTransition);
+ false /* startAtFrontOfQueue */).toRemoteTransition(),
+ mLauncher.getIApplicationThread());
+
+ TransitionFilter homeCheck = new TransitionFilter();
+ // No need to handle the transition that also dismisses keyguard.
+ homeCheck.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+ homeCheck.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
+ new TransitionFilter.Requirement()};
+ homeCheck.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
+ homeCheck.mRequirements[0].mTopActivity = mLauncher.getComponentName();
+ homeCheck.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ homeCheck.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
+ homeCheck.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
+ homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ SystemUiProxy.INSTANCE.get(mLauncher)
+ .registerRemoteTransition(mLauncherOpenTransition, homeCheck);
}
if (mBackAnimationController != null) {
mBackAnimationController.registerBackCallbacks(mHandler);
@@ -1179,8 +1177,8 @@
}
}
- private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
- for (RemoteAnimationTargetCompat target : targets) {
+ private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == mode && target.taskInfo != null
// Compare component name instead of task-id because transitions will promote
// the target up to the root task while getTaskId returns the leaf.
@@ -1192,9 +1190,9 @@
return false;
}
- private boolean hasMultipleTargetsWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
+ private boolean hasMultipleTargetsWithMode(RemoteAnimationTarget[] targets, int mode) {
int numTargets = 0;
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == mode) {
numTargets++;
}
@@ -1216,8 +1214,8 @@
/**
* Animator that controls the transformations of the windows when unlocking the device.
*/
- private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ private Animator getUnlockWindowAnimator(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
@@ -1226,24 +1224,23 @@
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- params[i] = new SurfaceParams.Builder(target.leash)
- .withAlpha(1f)
- .withWindowCrop(target.screenSpaceBounds)
- .withCornerRadius(cornerRadius)
- .build();
+ RemoteAnimationTarget target = appTargets[i];
+ transaction.forSurface(target.leash)
+ .setAlpha(1f)
+ .setWindowCrop(target.screenSpaceBounds)
+ .setCornerRadius(cornerRadius);
}
- surfaceApplier.scheduleApply(params);
+ surfaceApplier.scheduleApply(transaction);
}
});
return unlockAnimator;
}
- private static int getRotationChange(RemoteAnimationTargetCompat[] appTargets) {
+ private static int getRotationChange(RemoteAnimationTarget[] appTargets) {
int rotationChange = 0;
- for (RemoteAnimationTargetCompat target : appTargets) {
+ for (RemoteAnimationTarget target : appTargets) {
if (Math.abs(target.rotationChange) > Math.abs(rotationChange)) {
rotationChange = target.rotationChange;
}
@@ -1254,8 +1251,8 @@
/**
* Returns view on launcher that corresponds to the closing app in the list of app targets
*/
- private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) {
- for (RemoteAnimationTargetCompat appTarget : appTargets) {
+ private @Nullable View findLauncherView(RemoteAnimationTarget[] appTargets) {
+ for (RemoteAnimationTarget appTarget : appTargets) {
if (appTarget.mode == MODE_CLOSING) {
View launcherView = findLauncherView(appTarget);
if (launcherView != null) {
@@ -1269,7 +1266,7 @@
/**
* Returns view on launcher that corresponds to the {@param runningTaskTarget}.
*/
- private @Nullable View findLauncherView(RemoteAnimationTargetCompat runningTaskTarget) {
+ private @Nullable View findLauncherView(RemoteAnimationTarget runningTaskTarget) {
if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
return null;
}
@@ -1330,15 +1327,15 @@
* Closing animator that animates the window into its final location on the workspace.
*/
private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
- RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
+ RemoteAnimationTarget[] targets, View launcherView, PointF velocityPxPerS,
RectF closingWindowStartRect, float startWindowCornerRadius) {
FloatingIconView floatingIconView = null;
FloatingWidgetView floatingWidget = null;
RectF targetRect = new RectF();
- RemoteAnimationTargetCompat runningTaskTarget = null;
+ RemoteAnimationTarget runningTaskTarget = null;
boolean isTransluscent = false;
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == MODE_CLOSING) {
runningTaskTarget = target;
isTransluscent = runningTaskTarget.isTranslucent;
@@ -1432,7 +1429,7 @@
/**
* Closing window animator that moves the window down and offscreen.
*/
- private Animator getFallbackClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets) {
+ private Animator getFallbackClosingWindowAnimators(RemoteAnimationTarget[] appTargets) {
final int rotationChange = getRotationChange(appTargets);
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
Matrix matrix = new Matrix();
@@ -1453,10 +1450,10 @@
@Override
public void onUpdate(float percent, boolean initOnly) {
- SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = appTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.localBounds != null) {
tmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1478,20 +1475,19 @@
tmpRect.centerY());
matrix.postTranslate(0, mDy.value);
matrix.postTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(mAlpha.value)
- .withCornerRadius(windowCornerRadius)
- .withShadowRadius(mShadowRadius.value);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(mAlpha.value)
+ .setCornerRadius(windowCornerRadius)
+ .setShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_OPENING) {
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f);
}
- params[i] = builder.build();
}
- surfaceApplier.scheduleApply(params);
+ surfaceApplier.scheduleApply(transaction);
}
});
@@ -1555,8 +1551,8 @@
* the transition.
*/
public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
boolean fromUnlock,
RectF startRect,
float startWindowCornerRadius) {
@@ -1598,8 +1594,8 @@
true /* animateOverviewScrim */, launcherView).getAnimators());
if (!areAllTargetsTranslucent(appTargets)) {
- anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController(),
- STATE_DEPTH,
+ anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController().stateDepth,
+ MULTI_PROPERTY_VALUE,
BACKGROUND_APP.getDepth(mLauncher), NORMAL.getDepth(mLauncher)));
}
@@ -1665,9 +1661,9 @@
@Override
public void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
if (mLauncher.isDestroyed()) {
AnimatorSet anim = new AnimatorSet();
@@ -1706,9 +1702,9 @@
@Override
public void onCreateAnimation(int transit,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
@@ -1719,7 +1715,7 @@
final boolean skipFirstFrame;
if (launchingFromWidget) {
composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
- wallpaperTargets, nonAppTargets);
+ wallpaperTargets, nonAppTargets, launcherClosing);
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);
skipFirstFrame = true;
@@ -1835,7 +1831,7 @@
* RectFSpringAnim update listener to be used for app to home animation.
*/
private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener {
- private final RemoteAnimationTargetCompat[] mAppTargets;
+ private final RemoteAnimationTarget[] mAppTargets;
private final Matrix mMatrix = new Matrix();
private final Point mTmpPos = new Point();
private final Rect mCurrentRect = new Rect();
@@ -1846,7 +1842,7 @@
private final Rect mTmpRect = new Rect();
- SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect,
+ SpringAnimRunner(RemoteAnimationTarget[] appTargets, RectF targetRect,
Rect windowTargetBounds, float startWindowCornerRadius) {
mAppTargets = appTargets;
mStartRadius = startWindowCornerRadius;
@@ -1861,10 +1857,10 @@
@Override
public void onUpdate(RectF currentRectF, float progress) {
- SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = mAppTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = mAppTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ RemoteAnimationTarget target = mAppTargets[i];
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.localBounds != null) {
mTmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1899,18 +1895,17 @@
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpRect)
- .withAlpha(getWindowAlpha(progress))
- .withCornerRadius(getCornerRadius(progress) / scale);
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mTmpRect)
+ .setAlpha(getWindowAlpha(progress))
+ .setCornerRadius(getCornerRadius(progress) / scale);
} else if (target.mode == MODE_OPENING) {
mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
- builder.withMatrix(mMatrix)
- .withAlpha(1f);
+ builder.setMatrix(mMatrix)
+ .setAlpha(1f);
}
- params[i] = builder.build();
}
- mSurfaceApplier.scheduleApply(params);
+ mSurfaceApplier.scheduleApply(transaction);
}
protected float getWindowAlpha(float progress) {
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 7a483a8..1beabf1 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -18,22 +18,22 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import android.app.prediction.AppTarget;
-import android.content.ComponentName;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/** Task to update model as a result of predicted widgets update */
@@ -59,50 +59,43 @@
Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
widget -> new ComponentKey(widget.providerName, widget.user)).collect(
Collectors.toSet());
+ Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
Map<PackageUserKey, List<WidgetItem>> allWidgets =
dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
- FixedContainerItems fixedContainerItems =
- new FixedContainerItems(mPredictorState.containerId);
+ List<WidgetItem> servicePredictedItems = new ArrayList<>();
+ List<WidgetItem> localFilteredWidgets = new ArrayList<>();
- if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
- for (AppTarget app : mTargets) {
- PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(),
- app.getUser());
- if (allWidgets.containsKey(packageUserKey)) {
- List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
- .filter(item ->
- !widgetsInWorkspace.contains(
- new ComponentKey(item.componentName, item.user)))
- .collect(Collectors.toList());
- if (notAddedWidgets.size() > 0) {
- // Even an apps have more than one widgets, we only include one widget.
- fixedContainerItems.items.add(
- new PendingAddWidgetInfo(
- notAddedWidgets.get(0).widgetInfo,
- CONTAINER_WIDGETS_PREDICTION));
- }
- }
+ for (AppTarget app : mTargets) {
+ PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser());
+ List<WidgetItem> widgets = allWidgets.get(packageUserKey);
+ if (widgets == null || widgets.isEmpty()) {
+ continue;
}
- } else {
- Map<ComponentKey, WidgetItem> widgetItems =
- allWidgets.values().stream().flatMap(List::stream).distinct()
- .collect(Collectors.toMap(widget -> (ComponentKey) widget,
- widget -> widget));
- for (AppTarget app : mTargets) {
- if (TextUtils.isEmpty(app.getClassName())) {
+ 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;
}
- ComponentKey targetWidget = new ComponentKey(
- new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
- if (widgetItems.containsKey(targetWidget)) {
- fixedContainerItems.items.add(
- new PendingAddWidgetInfo(widgetItems.get(
- targetWidget).widgetInfo,
- CONTAINER_WIDGETS_PREDICTION));
- }
}
+ // No widget was added by the service, try local filtering
+ widgets.stream().filter(notOnWorkspace).findFirst()
+ .ifPresent(localFilteredWidgets::add);
}
+ if (servicePredictedItems.isEmpty()) {
+ servicePredictedItems.addAll(localFilteredWidgets);
+ }
+ FixedContainerItems fixedContainerItems =
+ new FixedContainerItems(mPredictorState.containerId);
+ servicePredictedItems.forEach(w -> fixedContainerItems.items.add(
+ new PendingAddWidgetInfo(w.widgetInfo, CONTAINER_WIDGETS_PREDICTION)));
+
dataModel.extraItems.put(mPredictorState.containerId, fixedContainerItems);
bindExtraContainerItems(fixedContainerItems);
diff --git a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
index 5bf727a..8720bd8 100644
--- a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
+++ b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
@@ -18,7 +18,9 @@
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
import android.content.Context;
+import android.view.View;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.appprediction.AppsDividerView;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.model.BgDataModel;
@@ -39,10 +41,13 @@
@Override
void updateAppDivider() {
OnboardingPrefs<?> onboardingPrefs = mActivityContext.getOnboardingPrefs();
- mActivityContext.getAppsView().getFloatingHeaderView()
- .findFixedRowByType(AppsDividerView.class)
- .setShowAllAppsLabel(!onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
- onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
+ if (onboardingPrefs != null) {
+ mActivityContext.getAppsView().getFloatingHeaderView()
+ .findFixedRowByType(AppsDividerView.class)
+ .setShowAllAppsLabel(
+ !onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
+ onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
+ }
}
@Override
@@ -51,4 +56,12 @@
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(item.items);
}
+
+ @Override
+ public void setLongClickListener(ActivityAllAppsContainerView<?> appsView,
+ View.OnLongClickListener onIconLongClickListener) {
+ appsView.getFloatingHeaderView()
+ .findFixedRowByType(PredictionRowView.class)
+ .setOnIconLongClickListener(onIconLongClickListener);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 2a78bdf..867e168 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -112,7 +113,7 @@
return;
}
- STATE_DEPTH.set(this, toState.getDepth(mLauncher));
+ stateDepth.setValue(toState.getDepth(mLauncher));
if (toState == LauncherState.BACKGROUND_APP) {
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
}
@@ -127,7 +128,8 @@
}
float toDepth = toState.getDepth(mLauncher);
- animation.setFloat(this, STATE_DEPTH, toDepth, config.getInterpolator(ANIM_DEPTH, LINEAR));
+ animation.setFloat(stateDepth, MULTI_PROPERTY_VALUE, toDepth,
+ config.getInterpolator(ANIM_DEPTH, LINEAR));
}
@Override
@@ -140,7 +142,7 @@
public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
mIgnoreStateChangesDuringMultiWindowAnimation = true;
- ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(this, STATE_DEPTH,
+ ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(stateDepth, MULTI_PROPERTY_VALUE,
mLauncher.getStateManager().getState().getDepth(mLauncher, isInMultiWindowMode))
.setDuration(300);
mwAnimation.addListener(new AnimatorListenerAdapter() {
@@ -158,8 +160,8 @@
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
writer.println(prefix + "\tmSurface=" + mSurface);
- writer.println(prefix + "\tmStateDepth=" + STATE_DEPTH.get(this));
- writer.println(prefix + "\tmWidgetDepth=" + WIDGET_DEPTH.get(this));
+ writer.println(prefix + "\tmStateDepth=" + stateDepth.getValue());
+ writer.println(prefix + "\tmWidgetDepth=" + widgetDepth.getValue());
writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
writer.println(prefix + "\tmInEarlyWakeUp=" + mInEarlyWakeUp);
writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation="
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
new file mode 100644
index 0000000..0c8952d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -0,0 +1,98 @@
+/*
+ * 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.statehandlers;
+
+import android.os.SystemProperties;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+/**
+ * Controls the visibility of the workspace and the resumed / paused state when desktop mode
+ * is enabled.
+ */
+public class DesktopVisibilityController {
+
+ private final Launcher mLauncher;
+
+ private boolean mFreeformTasksVisible;
+ private boolean mInOverviewState;
+
+ public DesktopVisibilityController(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ /**
+ * Whether desktop mode is supported.
+ */
+ private boolean isDesktopModeSupported() {
+ return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false);
+ }
+
+ /**
+ * Whether freeform windows are visible in desktop mode.
+ */
+ public boolean areFreeformTasksVisible() {
+ return mFreeformTasksVisible;
+ }
+
+ /**
+ * Sets whether freeform windows are visible and updates launcher visibility based on that.
+ */
+ public void setFreeformTasksVisible(boolean freeformTasksVisible) {
+ if (freeformTasksVisible != mFreeformTasksVisible) {
+ mFreeformTasksVisible = freeformTasksVisible;
+ updateLauncherVisibility();
+ }
+ }
+
+ /**
+ * Sets whether the overview is visible and updates launcher visibility based on that.
+ */
+ public void setOverviewStateEnabled(boolean overviewStateEnabled) {
+ if (overviewStateEnabled != mInOverviewState) {
+ mInOverviewState = overviewStateEnabled;
+ updateLauncherVisibility();
+ }
+ }
+
+ /**
+ * Updates launcher visibility and state to look like it is paused or resumed depending on
+ * whether freeform windows are showing in desktop mode.
+ */
+ private void updateLauncherVisibility() {
+ StatefulActivity<LauncherState> activity =
+ QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
+ View workspaceView = mLauncher.getWorkspace();
+ if (activity == null || workspaceView == null || !isDesktopModeSupported()) return;
+
+ if (mFreeformTasksVisible) {
+ workspaceView.setVisibility(View.INVISIBLE);
+ if (!mInOverviewState) {
+ // When freeform is visible & we're not in overview, we want launcher to appear
+ // paused, this ensures that taskbar displays.
+ activity.setPaused();
+ }
+ } else {
+ workspaceView.setVisibility(View.VISIBLE);
+ // If freeform isn't visible ensure that launcher appears resumed to behave normally.
+ activity.setResumed();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
index 0ab3cfd5..48481d8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_NOTIFICATIONS;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_QUICK_SETTINGS;
+import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -31,6 +33,8 @@
private final TaskbarActivityContext mContext;
private final FrameLayout mNavButtonsView;
private final ViewGroup mNavButtonContainer;
+ private final ViewGroup mStartContextualContainer;
+ private final View mAllAppsButton;
private TaskbarControllers mControllers;
@@ -40,6 +44,12 @@
mContext = context;
mNavButtonsView = navButtonsView;
mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
+ mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
+ mAllAppsButton = LayoutInflater.from(context)
+ .inflate(R.layout.taskbar_all_apps_button, mStartContextualContainer, false);
+ mAllAppsButton.setOnClickListener((View v) -> {
+ mControllers.taskbarAllAppsController.show();
+ });
}
/**
@@ -57,6 +67,8 @@
addButton(R.drawable.ic_sysbar_notifications, BUTTON_NOTIFICATIONS,
mNavButtonContainer, mControllers.navButtonController,
R.id.notifications_button);
+ // All apps button
+ mStartContextualContainer.addView(mAllAppsButton);
}
/** Cleans up on destroy */
diff --git a/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java b/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java
deleted file mode 100644
index 5f4d239..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java
+++ /dev/null
@@ -1,97 +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 android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.R;
-
-// TODO: This would be replaced by the thing that has the role and provides the intent.
-/**
- * Helper to determine what intent should be used to display in a floating window, if one
- * exists.
- */
-public class FloatingTaskIntentResolver {
- private static final String TAG = FloatingTaskIntentResolver.class.getSimpleName();
-
- @Nullable
- /** Gets an intent for a floating task, if one exists. */
- public static Intent getIntent(Context context) {
- PackageManager pm = context.getPackageManager();
- String pkg = context.getString(R.string.floating_task_package);
- String action = context.getString(R.string.floating_task_action);
- if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(action)) {
- Log.d(TAG, "intent could not be found, pkg= " + pkg + " action= " + action);
- return null;
- }
- Intent intent = createIntent(pm, null, pkg, action);
- if (intent != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
- Log.d(TAG, "No valid intent found!");
- return null;
- }
-
- @Nullable
- private static Intent createIntent(PackageManager pm, @Nullable String activityName,
- String packageName, String action) {
- if (TextUtils.isEmpty(activityName)) {
- activityName = queryActivityForAction(pm, packageName, action);
- }
- if (TextUtils.isEmpty(activityName)) {
- Log.d(TAG, "Activity name is empty even after action search: " + action);
- return null;
- }
- ComponentName component = new ComponentName(packageName, activityName);
- Intent intent = new Intent(action).setComponent(component).setPackage(packageName);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- Log.d(TAG, "createIntent returning: " + intent);
- return intent;
- }
-
- @Nullable
- private static String queryActivityForAction(PackageManager pm, String packageName,
- String action) {
- Intent intent = new Intent(action).setPackage(packageName);
- ResolveInfo resolveInfo = pm.resolveActivity(intent, MATCH_DEFAULT_ONLY);
- if (resolveInfo == null || resolveInfo.activityInfo == null) {
- Log.d(TAG, "queryActivityForAction: + " + resolveInfo);
- return null;
- }
- ActivityInfo info = resolveInfo.activityInfo;
- if (!info.exported) {
- Log.d(TAG, "queryActivityForAction: + " + info + " not exported");
- return null;
- }
- if (!info.enabled) {
- Log.d(TAG, "queryActivityForAction: + " + info + " not enabled");
- return null;
- }
- return resolveInfo.activityInfo.name;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java b/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java
deleted file mode 100644
index b15669b..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java
+++ /dev/null
@@ -1,51 +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 android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.FastBitmapDrawable;
-
-/**
- * Button in Taskbar that opens something in a floating task.
- */
-public class LaunchFloatingTaskButton extends BubbleTextView {
-
- public LaunchFloatingTaskButton(Context context) {
- this(context, null);
- }
-
- public LaunchFloatingTaskButton(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public LaunchFloatingTaskButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
- Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
- .createScaledBitmapWithShadow(
- theme.getDrawable(R.drawable.ic_floating_task_button));
- setIcon(new FastBitmapDrawable(bitmap));
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 555cd65..c9e42b7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -44,6 +44,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
@@ -227,7 +228,9 @@
} else {
// Adjust task transition spec to account for taskbar being visible
@ColorInt int taskAnimationBackgroundColor =
- mLauncher.getColor(R.color.taskbar_background);
+ DisplayController.isTransientTaskbar(mLauncher)
+ ? mLauncher.getColor(R.color.transient_taskbar_background)
+ : mLauncher.getColor(R.color.taskbar_background);
TaskTransitionSpec customTaskAnimationSpec = new TaskTransitionSpec(
taskAnimationBackgroundColor,
@@ -286,6 +289,10 @@
@Override
public void setSystemGestureInProgress(boolean inProgress) {
super.setSystemGestureInProgress(inProgress);
+ if (DisplayController.isTransientTaskbar(mLauncher)) {
+ forceHideBackground(false);
+ return;
+ }
if (!FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
// Launcher's ScrimView will draw the background throughout the gesture. But once the
// gesture ends, start drawing taskbar's background again since launcher might stop
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index af422cb..875327d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -31,6 +31,7 @@
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_SMALL_SCREEN;
import static com.android.launcher3.taskbar.Utilities.appendFlag;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
@@ -84,6 +85,7 @@
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory;
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter;
import com.android.launcher3.util.DimensionUtils;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -227,13 +229,13 @@
mPropertyHolders.add(new StatePropertyHolder(
mControllers.taskbarViewController.getTaskbarIconAlpha()
- .getProperty(ALPHA_INDEX_KEYGUARD),
+ .get(ALPHA_INDEX_KEYGUARD),
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
&& (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0));
mPropertyHolders.add(new StatePropertyHolder(
mControllers.taskbarViewController.getTaskbarIconAlpha()
- .getProperty(ALPHA_INDEX_SMALL_SCREEN),
+ .get(ALPHA_INDEX_SMALL_SCREEN),
flags -> (flags & FLAG_SMALL_SCREEN) == 0));
mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
@@ -340,7 +342,7 @@
mBackButtonAlpha = new MultiValueAlpha(mBackButton, NUM_ALPHA_CHANNELS);
mBackButtonAlpha.setUpdateVisibility(true);
mPropertyHolders.add(new StatePropertyHolder(
- mBackButtonAlpha.getProperty(ALPHA_INDEX_KEYGUARD_OR_DISABLE),
+ mBackButtonAlpha.get(ALPHA_INDEX_KEYGUARD_OR_DISABLE),
flags -> {
// Show only if not disabled, and if not on the keyguard or otherwise only when
// the bouncer or a lockscreen app is showing above the keyguard
@@ -368,7 +370,7 @@
mHomeButtonAlpha = new MultiValueAlpha(mHomeButton, NUM_ALPHA_CHANNELS);
mHomeButtonAlpha.setUpdateVisibility(true);
mPropertyHolders.add(
- new StatePropertyHolder(mHomeButtonAlpha.getProperty(
+ new StatePropertyHolder(mHomeButtonAlpha.get(
ALPHA_INDEX_KEYGUARD_OR_DISABLE),
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
(flags & FLAG_DISABLE_HOME) == 0));
@@ -694,14 +696,10 @@
}
/**
- * Adds the correct spacing to 3 button nav container. No-op if using gesture nav, setup
- * is incomplete, or in kids mode.
+ * Adds the correct spacing to 3 button nav container depending on if device is in kids mode,
+ * setup wizard, or normal 3 button nav.
*/
private void updateButtonLayoutSpacing() {
- if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()
- || !mContext.isUserSetupComplete()) {
- return;
- }
DeviceProfile dp = mContext.getDeviceProfile();
Resources res = mContext.getResources();
boolean isInSetup = !mContext.isUserSetupComplete();
@@ -719,47 +717,12 @@
return;
}
- // Add spacing after the end of the last nav button
- FrameLayout.LayoutParams navButtonParams =
- (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
- navButtonParams.gravity = Gravity.END;
- navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT;
- navButtonParams.height = MATCH_PARENT;
-
- int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
- int contextualWidth = mEndContextualContainer.getWidth();
- // If contextual buttons are showing, we check if the end margin is enough for the
- // contextual button to be showing - if not, move the nav buttons over a smidge
- if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
- // Additional spacing, eat up half of space between last icon and nav button
- navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
- }
- navButtonParams.setMarginEnd(navMarginEnd);
- mNavButtonContainer.setLayoutParams(navButtonParams);
-
- // Add the spaces in between the nav buttons
- int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween);
- for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
- View navButton = mNavButtonContainer.getChildAt(i);
- LinearLayout.LayoutParams buttonLayoutParams =
- (LinearLayout.LayoutParams) navButton.getLayoutParams();
- buttonLayoutParams.weight = 0;
- if (i == 0) {
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
- } else if (i == mNavButtonContainer.getChildCount() - 1) {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- } else {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
- }
- }
-
if (isInSetup) {
handleSetupUi();
// Hide back button in SUW if keyboard is showing (IME draws its own back).
mPropertyHolders.add(new StatePropertyHolder(
- mBackButtonAlpha.getProperty(ALPHA_INDEX_SUW),
+ mBackButtonAlpha.get(ALPHA_INDEX_SUW),
flags -> (flags & FLAG_IME_VISIBLE) == 0));
// TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
@@ -829,6 +792,42 @@
mNavButtonContainer.requestLayout();
mHomeButton.setOnLongClickListener(null);
+ } else if (mContext.isThreeButtonNav()) {
+ // Setup normal 3 button
+ // Add spacing after the end of the last nav button
+ FrameLayout.LayoutParams navButtonParams =
+ (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
+ navButtonParams.gravity = Gravity.END;
+ navButtonParams.width = FrameLayout.LayoutParams.WRAP_CONTENT;
+ navButtonParams.height = MATCH_PARENT;
+
+ int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
+ int contextualWidth = mEndContextualContainer.getWidth();
+ // If contextual buttons are showing, we check if the end margin is enough for the
+ // contextual button to be showing - if not, move the nav buttons over a smidge
+ if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
+ // Additional spacing, eat up half of space between last icon and nav button
+ navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
+ }
+ navButtonParams.setMarginEnd(navMarginEnd);
+ mNavButtonContainer.setLayoutParams(navButtonParams);
+
+ // Add the spaces in between the nav buttons
+ int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween);
+ for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
+ View navButton = mNavButtonContainer.getChildAt(i);
+ LinearLayout.LayoutParams buttonLayoutParams =
+ (LinearLayout.LayoutParams) navButton.getLayoutParams();
+ buttonLayoutParams.weight = 0;
+ if (i == 0) {
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ } else if (i == mNavButtonContainer.getChildCount() - 1) {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ } else {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ }
+ }
}
}
@@ -1049,9 +1048,9 @@
mAnimator.addListener(new AlphaUpdateListener(view));
}
- StatePropertyHolder(MultiValueAlpha.AlphaProperty alphaProperty,
+ StatePropertyHolder(MultiProperty alphaProperty,
IntPredicate enableCondition) {
- this(alphaProperty, enableCondition, MultiValueAlpha.VALUE, 1, 0);
+ this(alphaProperty, enableCondition, MULTI_PROPERTY_VALUE, 1, 0);
}
StatePropertyHolder(AnimatedFloat animatedFloat, IntPredicate enableCondition) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index e23e27e..12dbcb3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -31,6 +31,7 @@
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
@@ -105,7 +106,7 @@
.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
}
- mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue(
+ mTaskbarStashedHandleAlpha.get(ALPHA_INDEX_STASHED).setValue(
isPhoneGestureNavMode(deviceProfile) ? 1 : 0);
mTaskbarStashedHandleHintScale.updateValue(1f);
@@ -166,7 +167,7 @@
return TaskbarManager.isPhoneMode(deviceProfile) && !mActivity.isThreeButtonNav();
}
- public MultiValueAlpha getStashedHandleAlpha() {
+ public MultiPropertyFactory<View> getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
@@ -222,7 +223,7 @@
* Should be called when the home button is disabled, so we can hide this handle as well.
*/
public void setIsHomeButtonDisabled(boolean homeDisabled) {
- mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_HOME_DISABLED).setValue(
+ mTaskbarStashedHandleAlpha.get(ALPHA_INDEX_HOME_DISABLED).setValue(
homeDisabled ? 0 : 1);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 4c5e0be..fad9ff4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -24,6 +24,8 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
+import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
+import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW;
import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
@@ -75,6 +77,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.launcher3.testing.TestLogging;
@@ -135,14 +138,16 @@
private boolean mBindingItems = false;
private boolean mAddedWindow = false;
+ // The bounds of the taskbar items relative to TaskbarDragLayer
+ private final Rect mTransientTaskbarBounds = new Rect();
private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
- public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
+ public TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp,
TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
unfoldTransitionProgressProvider) {
super(windowContext);
- mDeviceProfile = dp.copy(this);
+ mDeviceProfile = launcherDp.copy(this);
final Resources resources = getResources();
@@ -172,8 +177,10 @@
mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
// Inflate views.
- mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
- R.layout.taskbar, null, false);
+ int taskbarLayout = DisplayController.isTransientTaskbar(this)
+ ? R.layout.transient_taskbar
+ : R.layout.taskbar;
+ mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
@@ -212,7 +219,7 @@
new TaskbarAutohideSuspendController(this),
new TaskbarPopupController(this),
new TaskbarForceVisibleImmersiveController(this),
- new TaskbarOverlayController(this, dp),
+ new TaskbarOverlayController(this, launcherDp),
new TaskbarAllAppsController(),
new TaskbarInsetsController(this),
new VoiceInteractionWindowController(this),
@@ -243,10 +250,10 @@
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
- public void updateDeviceProfile(DeviceProfile dp, NavigationMode navMode) {
+ public void updateDeviceProfile(DeviceProfile launcherDp, NavigationMode navMode) {
mNavMode = navMode;
- mControllers.taskbarOverlayController.updateDeviceProfile(dp);
- mDeviceProfile = dp.copy(this);
+ mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
+ mDeviceProfile = launcherDp.copy(this);
updateIconSize(getResources());
AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
@@ -257,12 +264,21 @@
}
private void updateIconSize(Resources resources) {
- float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
+ float taskbarIconSize = DisplayController.isTransientTaskbar(this)
+ ? resources.getDimension(R.dimen.transient_taskbar_icon_size)
+ : resources.getDimension(R.dimen.taskbar_icon_size);
mDeviceProfile.updateIconSize(1, resources);
float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
mDeviceProfile.updateIconSize(iconScale, resources);
}
+ /**
+ * Returns the View bounds of transient taskbar.
+ */
+ public Rect getTransientTaskbarBounds() {
+ return mTransientTaskbarBounds;
+ }
+
@VisibleForTesting
@Override
public StatsLogManager getStatsLogManager() {
@@ -284,13 +300,19 @@
// Taskbar is on the logical bottom of the screen
boolean isVerticalBarLayout = TaskbarManager.isPhoneMode(deviceProfile) &&
deviceProfile.isLandscape;
+
+ int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_SLIPPERY
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+ if (DisplayController.isTransientTaskbar(this)) {
+ windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+ }
WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams(
isVerticalBarLayout ? mLastRequestedNonFullscreenHeight : MATCH_PARENT,
isVerticalBarLayout ? MATCH_PARENT : mLastRequestedNonFullscreenHeight,
type,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SLIPPERY
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ windowFlags,
PixelFormat.TRANSLUCENT);
windowLayoutParams.setTitle(WINDOW_TITLE);
windowLayoutParams.packageName = getPackageName();
@@ -454,7 +476,7 @@
@Override
public void onDragEnd() {
- maybeSetTaskbarWindowNotFullscreen();
+ onDragEndOrViewRemoved();
}
@Override
@@ -523,7 +545,7 @@
private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) {
float alpha = isExpanded ? 0 : 1;
AnimatorSet anim = new AnimatorSet();
- anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().getProperty(
+ anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().get(
TaskbarViewController.ALPHA_INDEX_NOTIFICATION_EXPANDED).animateToValue(alpha));
if (!isThreeButtonNav()) {
anim.play(mControllers.taskbarDragLayerController.getNotificationShadeBgTaskbar()
@@ -559,24 +581,33 @@
}
/**
+ * Called to update a {@link AutohideSuspendFlag} with a new value.
+ */
+ public void setAutohideSuspendFlag(@AutohideSuspendFlag int flag, boolean newValue) {
+ mControllers.taskbarAutohideSuspendController.updateFlag(flag, newValue);
+ }
+
+ /**
* Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
*/
public void setTaskbarWindowFullscreen(boolean fullscreen) {
- mControllers.taskbarAutohideSuspendController.updateFlag(
- TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen);
+ setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen);
mIsFullscreen = fullscreen;
setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
}
/**
- * Reverts Taskbar window to its original size, if all floating views are closed and there is
- * no system drag operation in progress.
+ * Called when drag ends or when a view is removed from the DragLayer.
*/
- void maybeSetTaskbarWindowNotFullscreen() {
- if (AbstractFloatingView.getAnyView(this, TYPE_ALL) == null
- && !mControllers.taskbarDragController.isSystemDragInProgress()) {
+ void onDragEndOrViewRemoved() {
+ boolean isDragInProgress = mControllers.taskbarDragController.isSystemDragInProgress();
+
+ if (!isDragInProgress && !AbstractFloatingView.hasOpenView(this, TYPE_ALL)) {
+ // Reverts Taskbar window to its original size
setTaskbarWindowFullscreen(false);
}
+
+ setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, isDragInProgress);
}
public boolean isTaskbarWindowFullscreen() {
@@ -623,16 +654,24 @@
* Returns the default height of the window, including the static corner radii above taskbar.
*/
public int getDefaultTaskbarWindowHeight() {
+ Resources resources = getResources();
+
if (FLAG_HIDE_NAVBAR_WINDOW && mDeviceProfile.isPhone) {
- Resources resources = getResources();
return isThreeButtonNav() ?
resources.getDimensionPixelSize(R.dimen.taskbar_size) :
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
if (!isUserSetupComplete()) {
- return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
+ return resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame);
}
+
+ if (DisplayController.isTransientTaskbar(this)) {
+ return resources.getDimensionPixelSize(R.dimen.transient_taskbar_size)
+ + (2 * resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin))
+ + resources.getDimensionPixelSize(R.dimen.transient_taskbar_shadow_blur);
+ }
+
return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
}
@@ -682,6 +721,7 @@
Task task = (Task) tag;
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
ActivityOptions.makeBasic());
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} else if (tag instanceof FolderInfo) {
FolderIcon folderIcon = (FolderIcon) view;
Folder folder = folderIcon.getFolder();
@@ -741,6 +781,7 @@
}
mControllers.uiController.onTaskbarIconLaunched(info);
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
.show();
@@ -750,6 +791,7 @@
} else if (tag instanceof AppInfo) {
startItemInfoActivity((AppInfo) tag);
mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} else {
Log.e(TAG, "Unknown type clicked: " + tag);
}
@@ -785,6 +827,20 @@
}
/**
+ * Called when we want to unstash taskbar when user performs swipes up gesture.
+ */
+ public void onSwipeToUnstashTaskbar() {
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
+ }
+
+ /**
+ * Called when a transient Autohide flag suspend status changes.
+ */
+ public void onTransientAutohideSuspendFlagChanged(boolean isSuspended) {
+ mControllers.taskbarStashController.updateTaskbarTimeout(isSuspended);
+ }
+
+ /**
* Called when we detect a motion down or up/cancel in the nav region while stashed.
* @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
*/
@@ -907,4 +963,9 @@
mControllers.dumpLogs(prefix + "\t", pw);
mDeviceProfile.dump(this, prefix, pw);
}
+
+ @VisibleForTesting
+ public int getTaskbarAllAppsTopPadding() {
+ return mControllers.taskbarAllAppsController.getTaskbarAllAppsTopPadding();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
index 3cf9c99..4350e9c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -33,21 +33,28 @@
public class TaskbarAutohideSuspendController implements
TaskbarControllers.LoggableTaskbarController {
+ // Taskbar window is fullscreen.
public static final int FLAG_AUTOHIDE_SUSPEND_FULLSCREEN = 1 << 0;
+ // User is dragging item.
public static final int FLAG_AUTOHIDE_SUSPEND_DRAGGING = 1 << 1;
+ // User has touched down but has not lifted finger.
+ public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2;
@IntDef(flag = true, value = {
FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
FLAG_AUTOHIDE_SUSPEND_DRAGGING,
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutohideSuspendFlag {}
+ private final TaskbarActivityContext mActivity;
private final SystemUiProxy mSystemUiProxy;
private @AutohideSuspendFlag int mAutohideSuspendFlags = 0;
public TaskbarAutohideSuspendController(TaskbarActivityContext activity) {
+ mActivity = activity;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
}
@@ -59,12 +66,27 @@
* Adds or removes the given flag, then notifies system UI proxy whether to suspend auto-hide.
*/
public void updateFlag(@AutohideSuspendFlag int flag, boolean enabled) {
+ int flagsBefore = mAutohideSuspendFlags;
if (enabled) {
mAutohideSuspendFlags |= flag;
} else {
mAutohideSuspendFlags &= ~flag;
}
- mSystemUiProxy.notifyTaskbarAutohideSuspend(mAutohideSuspendFlags != 0);
+ if (flagsBefore == mAutohideSuspendFlags) {
+ // Nothing has changed, no need to notify.
+ return;
+ }
+
+ boolean isSuspended = isSuspended();
+ mSystemUiProxy.notifyTaskbarAutohideSuspend(isSuspended);
+ mActivity.onTransientAutohideSuspendFlagChanged(isSuspended);
+ }
+
+ /**
+ * Returns true iff taskbar autohide is currently suspended.
+ */
+ public boolean isSuspended() {
+ return mAutohideSuspendFlags != 0;
}
@Override
@@ -79,6 +101,7 @@
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
"FLAG_AUTOHIDE_SUSPEND_FULLSCREEN");
appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_DRAGGING, "FLAG_AUTOHIDE_SUSPEND_DRAGGING");
+ appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TOUCHING, "FLAG_AUTOHIDE_SUSPEND_TOUCHING");
return str.toString();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 1177bdb..abd467d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -16,10 +16,16 @@
package com.android.launcher3.taskbar
+import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
+import com.android.launcher3.Utilities.mapToRange
+
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import com.android.launcher3.R
+import com.android.launcher3.anim.Interpolators
+import com.android.launcher3.util.DisplayController
/**
* Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners.
@@ -29,6 +35,15 @@
val paint: Paint = Paint()
var backgroundHeight = context.deviceProfile.taskbarSize.toFloat()
+ private var maxBackgroundHeight = context.deviceProfile.taskbarSize.toFloat()
+ private val transientBackgroundBounds = context.transientTaskbarBounds
+
+ private val isTransientTaskbar = DisplayController.isTransientTaskbar(context);
+
+ private var shadowBlur = 0f
+ private var keyShadowDistance = 0f
+ private var bottomMargin = 0
+
private val leftCornerRadius = context.leftCornerRadius.toFloat()
private val rightCornerRadius = context.rightCornerRadius.toFloat()
private val invertedLeftCornerPath: Path = Path()
@@ -39,6 +54,15 @@
paint.flags = Paint.ANTI_ALIAS_FLAG
paint.style = Paint.Style.FILL
+ if (isTransientTaskbar) {
+ paint.color = context.getColor(R.color.transient_taskbar_background)
+
+ val res = context.resources
+ bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_margin)
+ shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
+ keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
+ }
+
// 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.
val square = Path()
@@ -58,17 +82,42 @@
*/
fun draw(canvas: Canvas) {
canvas.save()
- canvas.translate(0f, canvas.height - backgroundHeight)
+ canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
+ if (!isTransientTaskbar || transientBackgroundBounds.isEmpty) {
+ // Draw the background behind taskbar content.
+ canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
- // Draw the background behind taskbar content.
- canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
+ // Draw the inverted rounded corners above the taskbar.
+ canvas.translate(0f, -leftCornerRadius)
+ canvas.drawPath(invertedLeftCornerPath, paint)
+ canvas.translate(0f, leftCornerRadius)
+ canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
+ canvas.drawPath(invertedRightCornerPath, paint)
+ } else {
+ val scaleFactor = backgroundHeight / maxBackgroundHeight
+ val width = transientBackgroundBounds.width()
+ val widthScale = mapToRange(scaleFactor, 0f, 1f, 0.4f, 1f, Interpolators.LINEAR)
+ val newWidth = widthScale * width
+ val delta = width - newWidth
- // Draw the inverted rounded corners above the taskbar.
- canvas.translate(0f, -leftCornerRadius)
- canvas.drawPath(invertedLeftCornerPath, paint)
- canvas.translate(0f, leftCornerRadius)
- canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
- canvas.drawPath(invertedRightCornerPath, paint)
+ // Draw shadow.
+ val shadowAlpha = mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f,
+ Interpolators.LINEAR)
+ paint.setShadowLayer(shadowBlur, 0f, keyShadowDistance,
+ setColorAlphaBound(Color.BLACK, Math.round(shadowAlpha))
+ )
+
+ // Draw background.
+ val radius = backgroundHeight / 2f;
+
+ canvas.drawRoundRect(
+ transientBackgroundBounds.left + (delta / 2f),
+ 0f,
+ transientBackgroundBounds.right - (delta / 2f),
+ backgroundHeight,
+ radius, radius, paint
+ )
+ }
canvas.restore()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 9a1e064..d7bb16e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -18,10 +18,12 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
@@ -49,7 +51,6 @@
import com.android.launcher3.DropTarget;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
@@ -70,6 +71,7 @@
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.quickstep.util.LogUtils;
+import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.systemui.shared.recents.model.Task;
import java.io.PrintWriter;
@@ -83,7 +85,8 @@
public class TaskbarDragController extends DragController<BaseTaskbarContext> implements
TaskbarControllers.LoggableTaskbarController {
- private static boolean DEBUG_DRAG_SHADOW_SURFACE = false;
+ private static final boolean DEBUG_DRAG_SHADOW_SURFACE = false;
+ private static final int ANIM_DURATION_RETURN_ICON_TO_TASKBAR = 300;
private final int mDragIconSize;
private final int[] mTempXY = new int[2];
@@ -99,6 +102,8 @@
// Animation for the drag shadow back into position after an unsuccessful drag
private ValueAnimator mReturnAnimator;
+ private boolean mDisallowGlobalDrag;
+ private boolean mDisallowLongClick;
public TaskbarDragController(BaseTaskbarContext activity) {
super(activity);
@@ -110,6 +115,14 @@
mControllers = controllers;
}
+ public void setDisallowGlobalDrag(boolean disallowGlobalDrag) {
+ mDisallowGlobalDrag = disallowGlobalDrag;
+ }
+
+ public void setDisallowLongClick(boolean disallowLongClick) {
+ mDisallowLongClick = disallowLongClick;
+ }
+
/**
* Attempts to start a system drag and drop operation for the given View, using its tag to
* generate the ClipDescription and Intent.
@@ -131,7 +144,7 @@
View view,
@Nullable DragPreviewProvider dragPreviewProvider,
@Nullable Point iconShift) {
- if (!(view instanceof BubbleTextView)) {
+ if (!(view instanceof BubbleTextView) || mDisallowLongClick) {
return false;
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
@@ -293,6 +306,7 @@
}
private void startSystemDrag(BubbleTextView btv) {
+ if (mDisallowGlobalDrag) return;
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) {
@Override
@@ -422,6 +436,45 @@
}
@Override
+ protected void endDrag() {
+ if (mDisallowGlobalDrag) {
+ // We need to explicitly set deferDragViewCleanupPostAnimation to true here so the
+ // super call doesn't remove it from the drag layer before the animation completes.
+ // This variable gets set in to false in super.dispatchDropComplete() because it
+ // (rightfully so, perhaps) thinks this drag operation has failed, and does its own
+ // internal cleanup.
+ // Another way to approach this would be to make all of overview a drop target and
+ // accept the drop as successful and then run the setupReturnDragAnimator to simulate
+ // drop failure to the user
+ mDragObject.deferDragViewCleanupPostAnimation = true;
+
+ float fromX = mDragObject.x - mDragObject.xOffset;
+ float fromY = mDragObject.y - mDragObject.yOffset;
+ DragView dragView = mDragObject.dragView;
+ setupReturnDragAnimator(fromX, fromY, (View) mDragObject.originalView,
+ (x, y, scale, alpha) -> {
+ dragView.setTranslationX(x);
+ dragView.setTranslationY(y);
+ dragView.setScaleX(scale);
+ dragView.setScaleY(scale);
+ dragView.setAlpha(alpha);
+ });
+ mReturnAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ callOnDragEnd();
+ dragView.remove();
+ dragView.clearAnimation();
+ mReturnAnimator = null;
+
+ }
+ });
+ mReturnAnimator.start();
+ }
+ super.endDrag();
+ }
+
+ @Override
protected void callOnDragEnd() {
super.callOnDragEnd();
maybeOnDragEnd();
@@ -432,56 +485,20 @@
SurfaceControl dragSurface = dragEvent.getDragSurface();
// For top level icons, the target is the icon itself
- View target = btv;
- Object tag = btv.getTag();
- if (tag instanceof ItemInfo) {
- ItemInfo item = (ItemInfo) tag;
- TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
- if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
- // Since all apps closes when the drag starts, target the all apps button instead.
- target = taskbarViewController.getAllAppsButtonView();
- } else if (item.container >= 0) {
- // Since folders close when the drag starts, target the folder icon instead.
- Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
- ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
- target = taskbarViewController.getFirstIconMatch(matcher);
- } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
- // Find first icon with same package/user as the deep shortcut.
- Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
- Collections.singleton(item.getTargetPackage()), item.user);
- target = taskbarViewController.getFirstIconMatch(packageUserMatcher);
- }
- }
-
- // Finish any pending return animation before starting a new drag
- if (mReturnAnimator != null) {
- mReturnAnimator.end();
- }
+ View target = findTaskbarTargetForIconView(btv);
float fromX = dragEvent.getX() - dragEvent.getOffsetX();
float fromY = dragEvent.getY() - dragEvent.getOffsetY();
- int[] toPosition = target.getLocationOnScreen();
- float toScale = (float) target.getWidth() / mDragIconSize;
- float toAlpha = (target == btv) ? 1f : 0f;
final ViewRootImpl viewRoot = target.getViewRootImpl();
SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
- mReturnAnimator.setDuration(300);
- mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mReturnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = animation.getAnimatedFraction();
- float accelT = Interpolators.ACCEL_2.getInterpolation(t);
- float scale = 1f - t * (1f - toScale);
- float alpha = 1f - accelT * (1f - toAlpha);
- tx.setPosition(dragSurface, Utilities.mapRange(t, fromX, toPosition[0]),
- Utilities.mapRange(t, fromY, toPosition[1]));
- tx.setScale(dragSurface, scale, scale);
- tx.setAlpha(dragSurface, alpha);
- tx.apply();
- }
- });
+ setupReturnDragAnimator(fromX, fromY, btv,
+ (x, y, scale, alpha) -> {
+ tx.setPosition(dragSurface, x, y);
+ tx.setScale(dragSurface, scale, scale);
+ tx.setAlpha(dragSurface, alpha);
+ tx.apply();
+ });
+
mReturnAnimator.addListener(new AnimatorListenerAdapter() {
private boolean mCanceled = false;
@@ -517,6 +534,63 @@
mReturnAnimator.start();
}
+ private View findTaskbarTargetForIconView(@NonNull View iconView) {
+ Object tag = iconView.getTag();
+ if (tag instanceof ItemInfo) {
+ ItemInfo item = (ItemInfo) tag;
+ TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
+ if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
+ // Since all apps closes when the drag starts, target the all apps button instead.
+ return taskbarViewController.getAllAppsButtonView();
+ } else if (item.container >= 0) {
+ // Since folders close when the drag starts, target the folder icon instead.
+ Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
+ ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
+ return taskbarViewController.getFirstIconMatch(matcher);
+ } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+ // Find first icon with same package/user as the deep shortcut.
+ Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
+ Collections.singleton(item.getTargetPackage()), item.user);
+ return taskbarViewController.getFirstIconMatch(packageUserMatcher);
+ }
+ }
+ return iconView;
+ }
+
+ private void setupReturnDragAnimator(float fromX, float fromY, View originalView,
+ TaskbarReturnPropertiesListener animListener) {
+ // Finish any pending return animation before starting a new return
+ if (mReturnAnimator != null) {
+ mReturnAnimator.end();
+ }
+
+ // For top level icons, the target is the icon itself
+ View target = findTaskbarTargetForIconView(originalView);
+
+ int[] toPosition = target.getLocationOnScreen();
+ float toScale = (float) target.getWidth() / mDragIconSize;
+ float toAlpha = (target == originalView) ? 1f : 0f;
+ MultiValueUpdateListener listener = new MultiValueUpdateListener() {
+ final FloatProp mDx = new FloatProp(fromX, toPosition[0], 0,
+ ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.FAST_OUT_SLOW_IN);
+ final FloatProp mDy = new FloatProp(fromY, toPosition[1], 0,
+ ANIM_DURATION_RETURN_ICON_TO_TASKBAR,
+ FAST_OUT_SLOW_IN);
+ final FloatProp mScale = new FloatProp(1f, toScale, 0,
+ ANIM_DURATION_RETURN_ICON_TO_TASKBAR, FAST_OUT_SLOW_IN);
+ final FloatProp mAlpha = new FloatProp(1f, toAlpha, 0,
+ ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.ACCEL_2);
+ @Override
+ public void onUpdate(float percent, boolean initOnly) {
+ animListener.updateDragShadow(mDx.value, mDy.value, mScale.value, mAlpha.value);
+ }
+ };
+ mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mReturnAnimator.setDuration(ANIM_DURATION_RETURN_ICON_TO_TASKBAR);
+ mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mReturnAnimator.addUpdateListener(listener);
+ }
+
@Override
protected float getX(MotionEvent ev) {
// We will resize to fill the screen while dragging, so use screen coordinates. This ensures
@@ -540,7 +614,7 @@
@Override
protected void exitDrag() {
- if (mDragObject != null) {
+ if (mDragObject != null && !mDisallowGlobalDrag) {
mActivity.getDragLayer().removeView(mDragObject.dragView);
}
}
@@ -556,6 +630,10 @@
return null;
}
+ interface TaskbarReturnPropertiesListener {
+ void updateDragShadow(float x, float y, float scale, float alpha);
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarDragController:");
@@ -566,5 +644,7 @@
pw.println(prefix + "\tmRegistrationY=" + mRegistrationY);
pw.println(prefix + "\tmIsSystemDragInProgress=" + mIsSystemDragInProgress);
pw.println(prefix + "\tisInternalDragInProgess=" + super.isDragging());
+ pw.println(prefix + "\tmDisallowGlobalDrag=" + mDisallowGlobalDrag);
+ pw.println(prefix + "\tmDisallowLongClick=" + mDisallowLongClick);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 7e75779..7c9a13c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -116,6 +116,14 @@
}
@Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mControllerCallbacks != null && ev.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mControllerCallbacks.onActionOutsideEvent();
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
if (mControllerCallbacks != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 353f1e0..13ecf81 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -23,6 +23,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.util.DimensionUtils;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
@@ -166,10 +167,24 @@
}
/**
+ * Called whenever TaskbarDragLayer receives an ACTION_OUTSIDE event.
+ */
+ public void onActionOutsideEvent() {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+ if (mControllers.taskbarStashController.isStashed()) {
+ return;
+ }
+
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+ }
+
+ /**
* Called when a child is removed from TaskbarDragLayer.
*/
public void onDragLayerViewRemoved() {
- mActivity.maybeSetTaskbarWindowNotFullscreen();
+ mActivity.onDragEndOrViewRemoved();
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index 16cc0ac..365ec75 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -219,7 +219,7 @@
}
int getIconLayoutBoundsWidth() {
- return mControllers.taskbarViewController.getIconLayoutBounds().width();
+ return mControllers.taskbarViewController.getIconLayoutWidth();
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index 6c793a6..f7aafe0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -31,13 +31,10 @@
import android.view.View;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
-import java.util.Optional;
-import java.util.function.Consumer;
-
/**
* Controller for taskbar when force visible in immersive mode is set.
*/
@@ -54,8 +51,6 @@
private final Runnable mUndimmingRunnable = this::undimIcons;
private final AnimatedFloat mIconAlphaForDimming = new AnimatedFloat(
this::updateIconDimmingAlpha);
- private final Consumer<MultiValueAlpha> mImmersiveModeAlphaUpdater = alpha -> alpha.getProperty(
- ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
private final View.AccessibilityDelegate mKidsModeAccessibilityDelegate =
new View.AccessibilityDelegate() {
@Override
@@ -145,22 +140,20 @@
}
private void updateIconDimmingAlpha() {
- getBackButtonAlphaOptional().ifPresent(mImmersiveModeAlphaUpdater);
- getHomeButtonAlphaOptional().ifPresent(mImmersiveModeAlphaUpdater);
- }
-
- private Optional<MultiValueAlpha> getBackButtonAlphaOptional() {
if (mControllers == null || mControllers.navbarButtonsViewController == null) {
- return Optional.empty();
+ return;
}
- return Optional.ofNullable(mControllers.navbarButtonsViewController.getBackButtonAlpha());
- }
- private Optional<MultiValueAlpha> getHomeButtonAlphaOptional() {
- if (mControllers == null || mControllers.navbarButtonsViewController == null) {
- return Optional.empty();
+ MultiPropertyFactory<View> ba =
+ mControllers.navbarButtonsViewController.getBackButtonAlpha();
+ if (ba != null) {
+ ba.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
}
- return Optional.ofNullable(mControllers.navbarButtonsViewController.getHomeButtonAlpha());
+ MultiPropertyFactory<View> ha =
+ mControllers.navbarButtonsViewController.getHomeButtonAlpha();
+ if (ba != null) {
+ ha.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
+ }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index de37b70..bc5bcf5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -38,6 +38,8 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
@@ -49,7 +51,6 @@
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.StringJoiner;
-import java.util.function.Consumer;
/**
* Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
@@ -73,7 +74,7 @@
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
- private MultiValueAlpha.AlphaProperty mIconAlphaForHome;
+ private MultiProperty mIconAlphaForHome;
private QuickstepLauncher mLauncher;
private Integer mPrevState;
@@ -89,18 +90,8 @@
// We skip any view synchronizations during init/destroy.
private boolean mCanSyncViews;
- private final Consumer<Float> mIconAlphaForHomeConsumer = alpha -> {
- /*
- * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
- * should not be visible at the same time.
- */
- mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
- mLauncher.getHotseat().setQsbAlpha(
- mLauncher.getDeviceProfile().isQsbInline && alpha > 0 ? 0 : 1);
- };
-
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
- dp -> mIconAlphaForHomeConsumer.accept(mIconAlphaForHome.getValue());
+ dp -> updateIconAlphaForHome(mIconAlphaForHome.getValue());
private final StateManager.StateListener<LauncherState> mStateListener =
new StateManager.StateListener<LauncherState>() {
@@ -128,6 +119,10 @@
mLauncherState = finalState;
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
applyState();
+ mControllers.taskbarDragController.setDisallowGlobalDrag(
+ (finalState instanceof OverviewState));
+ mControllers.taskbarDragController.setDisallowLongClick(
+ finalState == LauncherState.OVERVIEW_SPLIT_SELECT);
}
};
@@ -139,9 +134,8 @@
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
- MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
- mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
- mIconAlphaForHome.setConsumer(mIconAlphaForHomeConsumer);
+ mIconAlphaForHome = mControllers.taskbarViewController
+ .getTaskbarIconAlpha().get(ALPHA_INDEX_HOME);
mIconAlignment.finishAnimation();
onIconAlignmentRatioChanged();
@@ -162,7 +156,6 @@
mIconAlignment.finishAnimation();
- mIconAlphaForHome.setConsumer(null);
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.getStateManager().removeStateListener(mStateListener);
@@ -383,7 +376,7 @@
@Override
public void onAnimationStart(Animator animation) {
if (mLauncher.getHotseat().getIconsAlpha() > 0) {
- mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
+ updateIconAlphaForHome(mLauncher.getHotseat().getIconsAlpha());
}
}
});
@@ -405,7 +398,7 @@
mIconAlignment.value, mIconAlignment.getEndValue(), mLauncher.getDeviceProfile());
mControllers.navbarButtonsViewController.updateTaskbarAlignment(mIconAlignment.value);
// Switch taskbar and hotseat in last frame
- mIconAlphaForHome.setValue(taskbarWillBeVisible ? 1 : 0);
+ updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0);
// Sync the first frame where we swap taskbar and hotseat.
if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -415,6 +408,18 @@
}
}
+ private void updateIconAlphaForHome(float alpha) {
+ mIconAlphaForHome.setValue(alpha);
+
+ /*
+ * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
+ * should not be visible at the same time.
+ */
+ mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
+ mLauncher.getHotseat().setQsbAlpha(
+ mLauncher.getDeviceProfile().isQsbInline && alpha > 0 ? 0 : 1);
+ }
+
private final class TaskBarRecentsAnimationListener implements
RecentsAnimationCallbacks.RecentsAnimationListener {
private final RecentsAnimationCallbacks mCallbacks;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c5e1b8f..afd659f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -39,12 +39,14 @@
import androidx.annotation.NonNull;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.launcher3.Alarm;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
@@ -70,6 +72,7 @@
public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 7; // All apps is visible.
public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard
public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 9; // phone screen gesture nav, stashed
+ public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 10; // Autohide (transient taskbar).
// If any of these flags are enabled, isInApp should return true.
private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
@@ -78,7 +81,7 @@
private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
| FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP
| FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS
- | FLAG_STASHED_SMALL_SCREEN;
+ | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
private static final int FLAGS_STASHED_IN_APP_IGNORING_IME =
FLAGS_STASHED_IN_APP & ~FLAG_STASHED_IN_APP_IME;
@@ -132,6 +135,9 @@
*/
private static final boolean DEFAULT_STASHED_PREF = false;
+ // Auto stashes when user has not interacted with the Taskbar after X ms.
+ private static final long NO_TOUCH_TIMEOUT_TO_STASH_MS = 5000;
+
private final TaskbarActivityContext mActivity;
private final SharedPreferences mPrefs;
private final int mStashedHeight;
@@ -144,11 +150,11 @@
private AnimatedFloat mTaskbarBackgroundOffset;
private AnimatedFloat mTaskbarImeBgAlpha;
// TaskbarView icon properties.
- private AlphaProperty mIconAlphaForStash;
+ private MultiProperty mIconAlphaForStash;
private AnimatedFloat mIconScaleForStash;
private AnimatedFloat mIconTranslationYForStash;
// Stashed handle properties.
- private AlphaProperty mTaskbarStashedHandleAlpha;
+ private MultiProperty mTaskbarStashedHandleAlpha;
private AnimatedFloat mTaskbarStashedHandleHintScale;
/** Whether we are currently visually stashed (might change based on launcher state). */
@@ -162,6 +168,8 @@
private boolean mEnableManualStashingDuringTests = false;
+ private final Alarm mTimeoutAlarm = new Alarm();
+
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
flags -> {
@@ -199,24 +207,27 @@
mTaskbarImeBgAlpha = dragLayerController.getImeBgTaskbar();
TaskbarViewController taskbarViewController = controllers.taskbarViewController;
- mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().getProperty(
+ mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().get(
TaskbarViewController.ALPHA_INDEX_STASH);
mIconScaleForStash = taskbarViewController.getTaskbarIconScaleForStash();
mIconTranslationYForStash = taskbarViewController.getTaskbarIconTranslationYForStash();
StashedHandleViewController stashedHandleController =
controllers.stashedHandleViewController;
- mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().getProperty(
+ mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().get(
StashedHandleViewController.ALPHA_INDEX_STASHED);
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
// We use supportsVisualStashing() here instead of supportsManualStashing() because we want
// it to work properly for tests that recreate taskbar. This check is here just to ensure
// that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false).
boolean isManuallyStashedInApp = supportsVisualStashing()
- && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
+ && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF)
+ && !isTransientTaskbar;
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isTransientTaskbar);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
updateStateForFlag(FLAG_IN_SETUP, isInSetup);
updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
@@ -243,7 +254,8 @@
protected boolean supportsManualStashing() {
return supportsVisualStashing()
&& isInApp()
- && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests);
+ && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests)
+ && !DisplayController.isTransientTaskbar(mActivity);
}
/**
@@ -377,6 +389,20 @@
}
/**
+ * Stash or unstashes the transient taskbar.
+ */
+ public void updateAndAnimateTransientTaskbar(boolean stash) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) {
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash);
+ applyState();
+ }
+ }
+
+ /**
* Should be called when long pressing the nav region when taskbar is present.
* @return Whether taskbar was stashed and now is unstashed.
*/
@@ -549,11 +575,17 @@
public void onAnimationStart(Animator animation) {
mIsStashed = isStashed;
onIsStashedChanged(mIsStashed);
+
+ cancelTimeoutIfExists();
}
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
+
+ if (!mIsStashed) {
+ tryStartTaskbarTimeout();
+ }
}
});
}
@@ -779,6 +811,54 @@
mControllers.rotationButtonController.onTaskbarStateChange(visible, stashed);
}
+ /**
+ * Cancels a timeout if any exists.
+ */
+ public void cancelTimeoutIfExists() {
+ if (mTimeoutAlarm.alarmPending()) {
+ mTimeoutAlarm.cancelAlarm();
+ }
+ }
+
+ /**
+ * Updates the status of the taskbar timeout.
+ * @param isAutohideSuspended If true, cancels any existing timeout
+ * If false, attempts to re/start the timeout
+ */
+ public void updateTaskbarTimeout(boolean isAutohideSuspended) {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+ if (isAutohideSuspended) {
+ cancelTimeoutIfExists();
+ } else {
+ tryStartTaskbarTimeout();
+ }
+ }
+
+ /**
+ * Attempts to start timer to auto hide the taskbar based on time.
+ */
+ public void tryStartTaskbarTimeout() {
+ if (!DisplayController.isTransientTaskbar(mActivity)) {
+ return;
+ }
+ if (mIsStashed) {
+ return;
+ }
+ cancelTimeoutIfExists();
+
+ mTimeoutAlarm.setOnAlarmListener(this::onTaskbarTimeout);
+ mTimeoutAlarm.setAlarm(NO_TOUCH_TIMEOUT_TO_STASH_MS);
+ }
+
+ private void onTaskbarTimeout(Alarm alarm) {
+ if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
+ return;
+ }
+ updateAndAnimateTransientTaskbar(true);
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarStashController:");
@@ -794,17 +874,18 @@
}
private static String getStateString(int flags) {
- StringJoiner str = new StringJoiner("|");
- appendFlag(str, flags, FLAGS_IN_APP, "FLAG_IN_APP");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_PINNED, "FLAG_STASHED_IN_APP_PINNED");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
- appendFlag(str, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
- appendFlag(str, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE");
- appendFlag(str, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_APP_ALL_APPS");
- appendFlag(str, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP");
- return str.toString();
+ StringJoiner sj = new StringJoiner("|");
+ appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_PINNED, "FLAG_STASHED_IN_APP_PINNED");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
+ appendFlag(sj, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE");
+ appendFlag(sj, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_TASKBAR_ALL_APPS");
+ appendFlag(sj, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP");
+ appendFlag(sj, flags, FLAG_STASHED_IN_APP_AUTO, "FLAG_STASHED_IN_APP_AUTO");
+ return sj.toString();
}
private class StatePropertyHolder {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 49dba95..2294306 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar;
+import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.CallSuper;
@@ -97,6 +98,22 @@
}
}
+ /**
+ * Returns true iff taskbar is stashed.
+ */
+ public boolean isTaskbarStashed() {
+ return mControllers.taskbarStashController.isStashed();
+ }
+
+ /*
+ * @param ev MotionEvent in screen coordinates.
+ * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
+ */
+ public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
+ return mControllers.taskbarViewController.isEventOverAnyItem(ev)
+ || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
+ }
+
@CallSuper
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(String.format(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index bb82d19..fe38bb1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,17 +16,14 @@
package com.android.launcher3.taskbar;
import android.content.Context;
-import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.os.SystemProperties;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.LayoutRes;
@@ -45,9 +42,9 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.AllAppsButton;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
import java.util.function.Predicate;
@@ -62,7 +59,7 @@
public int mThemeIconsBackground;
private final int[] mTempOutLocation = new int[2];
- private final Rect mIconLayoutBounds = new Rect();
+ private final Rect mIconLayoutBounds;
private final int mIconTouchSize;
private final int mItemMarginLeftRight;
private final int mItemPadding;
@@ -81,16 +78,10 @@
private @Nullable FolderIcon mLeaveBehindFolderIcon;
// Only non-null when device supports having an All Apps button.
- private @Nullable AllAppsButton mAllAppsButton;
+ private @Nullable View mAllAppsButton;
private View mQsb;
- // Only non-null when device supports having a floating task.
- private @Nullable BubbleTextView mFloatingTaskButton;
- private @Nullable Intent mFloatingTaskIntent;
- private static final boolean FLOATING_TASKS_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
-
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -108,11 +99,14 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mActivityContext = ActivityContext.lookupContext(context);
+ mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds();
Resources resources = getResources();
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
- int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+ int actualMargin = DisplayController.isTransientTaskbar(mActivityContext)
+ ? resources.getDimensionPixelSize(R.dimen.transient_taskbar_icon_spacing)
+ : resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx;
// We layout the icons to be of mIconTouchSize in width and height
@@ -125,27 +119,17 @@
mThemeIconsBackground = calculateThemeIconsBackground();
if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
- mAllAppsButton = new AllAppsButton(context);
- mAllAppsButton.setLayoutParams(
- new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
+ mAllAppsButton = LayoutInflater.from(context)
+ .inflate(R.layout.taskbar_all_apps_button, this, false);
mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ if (mActivityContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
+ mAllAppsButton.setVisibility(GONE);
+ }
}
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
- if (FLOATING_TASKS_ENABLED) {
- mFloatingTaskIntent = FloatingTaskIntentResolver.getIntent(context);
- if (mFloatingTaskIntent != null) {
- mFloatingTaskButton = new LaunchFloatingTaskButton(context);
- mFloatingTaskButton.setLayoutParams(
- new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
- mFloatingTaskButton.setPadding(mItemPadding, mItemPadding, mItemPadding,
- mItemPadding);
- } else {
- Log.d(TAG, "Floating tasks is enabled but no intent was found!");
- }
- }
}
private int getColorWithGivenLuminance(int color, float luminance) {
@@ -173,10 +157,6 @@
if (mAllAppsButton != null) {
mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
}
- if (mFloatingTaskButton != null) {
- mFloatingTaskButton.setOnClickListener(
- mControllerCallbacks.getFloatingTaskButtonListener(mFloatingTaskIntent));
- }
}
private void removeAndRecycle(View view) {
@@ -201,9 +181,6 @@
}
removeView(mQsb);
- if (mFloatingTaskButton != null) {
- removeView(mFloatingTaskButton);
- }
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
@@ -286,11 +263,6 @@
mQsb.setVisibility(View.INVISIBLE);
}
- if (mFloatingTaskButton != null) {
- int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
- addView(mFloatingTaskButton, index);
- }
-
mThemeIconsBackground = calculateThemeIconsBackground();
setThemedIconsBackgroundColor(mThemeIconsBackground);
}
@@ -321,12 +293,8 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
- int countExcludingQsb = count;
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
- if (deviceProfile.isQsbInline) {
- countExcludingQsb--;
- }
- int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ int spaceNeeded = getIconLayoutWidth();
int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
boolean layoutRtl = isLayoutRtl();
int iconEnd = right - (right - left - spaceNeeded) / 2;
@@ -377,12 +345,22 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ mControllerCallbacks.onInterceptTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
if (!mTouchEnabled) {
return true;
}
- if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) {
- // Don't allow long pressing between icons, or above/below them.
+ if (mIconLayoutBounds.left <= event.getX()
+ && event.getX() <= mIconLayoutBounds.right
+ && !DisplayController.isTransientTaskbar(mActivityContext)) {
+ // Don't allow long pressing between icons, or above/below them
+ // unless its transient taskbar.
+ mControllerCallbacks.clearTouchInProgress();
return true;
}
if (mControllerCallbacks.onTouchEvent(event)) {
@@ -399,6 +377,7 @@
public void setTouchesEnabled(boolean touchEnabled) {
this.mTouchEnabled = touchEnabled;
+ mControllerCallbacks.clearTouchInProgress();
}
/**
@@ -417,6 +396,18 @@
}
/**
+ * Returns the space used by the icons
+ */
+ public int getIconLayoutWidth() {
+ int countExcludingQsb = getChildCount();
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ if (deviceProfile.isQsbInline) {
+ countExcludingQsb--;
+ }
+ return countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ }
+
+ /**
* Returns the app icons currently shown in the taskbar.
*/
public View[] getIconViews() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 16dd90d..ee87185 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,20 +17,23 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.annotation.NonNull;
-import android.content.Intent;
import android.graphics.Rect;
import android.util.FloatProperty;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.OneShotPreDrawListener;
@@ -47,12 +50,14 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -87,11 +92,16 @@
private AnimatedFloat mTaskbarNavButtonTranslationY;
private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
+ private final int mTaskbarBottomMargin;
+
private final AnimatedFloat mThemeIconsBackground = new AnimatedFloat(
this::updateIconsBackground);
private final TaskbarModelCallbacks mModelCallbacks;
+ // Captures swipe down action to close transient taskbar.
+ protected @Nullable SingleAxisSwipeDetector mSwipeDownDetector;
+
// Initialized in init.
private TaskbarControllers mControllers;
@@ -111,6 +121,34 @@
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
mTaskbarIconAlpha.setUpdateVisibility(true);
mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView);
+ mTaskbarBottomMargin = DisplayController.isTransientTaskbar(activity)
+ ? activity.getResources().getDimensionPixelSize(R.dimen.transient_taskbar_margin)
+ : 0;
+
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ mSwipeDownDetector = new SingleAxisSwipeDetector(activity,
+ new SingleAxisSwipeDetector.Listener() {
+ private float mLastDisplacement;
+
+ @Override
+ public boolean onDrag(float displacement) {
+ mLastDisplacement = displacement;
+ return false;
+ }
+
+ @Override
+ public void onDragEnd(float velocity) {
+ if (mLastDisplacement > 0) {
+ mControllers.taskbarStashController
+ .updateAndAnimateTransientTaskbar(true);
+ }
+ }
+
+ @Override
+ public void onDragStart(boolean start, float startDisplacement) {}
+ }, VERTICAL);
+ mSwipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
+ }
}
public void init(TaskbarControllers controllers) {
@@ -146,7 +184,7 @@
return mTaskbarView.areIconsVisible();
}
- public MultiValueAlpha getTaskbarIconAlpha() {
+ public MultiPropertyFactory<View> getTaskbarIconAlpha() {
return mTaskbarIconAlpha;
}
@@ -161,7 +199,7 @@
* Should be called when the IME switcher visibility changes.
*/
public void setIsImeSwitcherVisible(boolean isImeSwitcherVisible) {
- mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
+ mTaskbarIconAlpha.get(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
isImeSwitcherVisible ? 0 : 1);
}
@@ -170,7 +208,7 @@
*/
public void setRecentsButtonDisabled(boolean isDisabled) {
// TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha.
- mTaskbarIconAlpha.getProperty(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
+ mTaskbarIconAlpha.get(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
}
/**
@@ -193,6 +231,10 @@
return mTaskbarView.getIconLayoutBounds();
}
+ public int getIconLayoutWidth() {
+ return mTaskbarView.getIconLayoutWidth();
+ }
+
public View[] getIconViews() {
return mTaskbarView.getIconViews();
}
@@ -316,6 +358,8 @@
float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+
setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
isToHome
? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
@@ -341,6 +385,8 @@
float childCenter = (child.getLeft() + child.getRight()) / 2f;
setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+ setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+
setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
}
@@ -424,6 +470,8 @@
private float mDownX, mDownY;
private boolean mCanceledStashHint;
+ private boolean mTouchInProgress;
+
public View.OnClickListener getIconOnClickListener() {
return mActivity.getItemOnClickListener();
}
@@ -435,13 +483,6 @@
};
}
- public View.OnClickListener getFloatingTaskButtonListener(@NonNull Intent intent) {
- return v -> {
- SystemUiProxy proxy = SystemUiProxy.INSTANCE.get(v.getContext());
- proxy.showFloatingTask(intent);
- };
- }
-
public View.OnLongClickListener getIconOnLongClickListener() {
return mControllers.taskbarDragController::startDragOnLongClick;
}
@@ -452,37 +493,75 @@
}
/**
+ * Simply listens to all intercept touch events passed to TaskbarView.
+ */
+ public void onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchInProgress = true;
+ }
+
+ if (mTouchInProgress && mSwipeDownDetector != null) {
+ mSwipeDownDetector.onTouchEvent(ev);
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_UP
+ || ev.getAction() == MotionEvent.ACTION_CANCEL) {
+ clearTouchInProgress();
+ }
+ }
+
+ /**
* Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to
* consume the touch so TaskbarView treats it as an ACTION_CANCEL.
*/
public boolean onTouchEvent(MotionEvent motionEvent) {
+ boolean shouldConsumeTouch = false;
+ boolean clearTouchInProgress = false;
+
final float x = motionEvent.getRawX();
final float y = motionEvent.getRawY();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
+ mTouchInProgress = true;
mDownX = x;
mDownY = y;
mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
mCanceledStashHint = false;
break;
case MotionEvent.ACTION_MOVE:
- if (!mCanceledStashHint
+ if (mTouchInProgress
+ && !mCanceledStashHint
&& squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
mCanceledStashHint = true;
- return true;
+ shouldConsumeTouch = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (!mCanceledStashHint) {
+ if (mTouchInProgress && !mCanceledStashHint) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
}
+ clearTouchInProgress = true;
break;
}
- return false;
+
+ if (mTouchInProgress && mSwipeDownDetector != null) {
+ mSwipeDownDetector.onTouchEvent(motionEvent);
+ }
+ if (clearTouchInProgress) {
+ clearTouchInProgress();
+ }
+ return shouldConsumeTouch;
+ }
+
+ /**
+ * Ensures that we do not pass any more touch events to the SwipeDetector.
+ */
+ public void clearTouchInProgress() {
+ mTouchInProgress = false;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index 076900c..837af58 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -63,11 +63,11 @@
// Fade out taskbar icons and stashed handle.
val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f
val fadeTaskbarIcons = controllers.taskbarViewController.taskbarIconAlpha
- .getProperty(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(TASKBAR_ICONS_FADE_DURATION)
val fadeStashedHandle = controllers.stashedHandleViewController.stashedHandleAlpha
- .getProperty(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(STASHED_HANDLE_FADE_DURATION)
fadeTaskbarIcons.start()
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index ce51f7d..e41c75f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -20,10 +20,6 @@
import android.view.WindowInsets;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
-import com.android.launcher3.allapps.BaseAdapterProvider;
-import com.android.launcher3.allapps.BaseAllAppsAdapter;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/** All apps container accessible from taskbar. */
@@ -43,12 +39,4 @@
setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
return super.onApplyWindowInsets(insets);
}
-
- @Override
- protected BaseAllAppsAdapter<TaskbarOverlayContext> createAdapter(
- AlphabeticalAppsList<TaskbarOverlayContext> appsList,
- BaseAdapterProvider[] adapterProviders) {
- return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
- adapterProviders);
- }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index ea37944..85c6318 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar.allapps;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
import com.android.launcher3.appprediction.PredictionRowView;
@@ -123,4 +124,11 @@
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
}
+
+
+ @VisibleForTesting
+ public int getTaskbarAllAppsTopPadding() {
+ // Allow null-pointer since this should only be null if the apps view is not showing.
+ return mAppsView.getActiveRecyclerView().getClipBounds().top;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 5701de0..7e3163d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -84,7 +84,7 @@
@Override
public DeviceProfile getDeviceProfile() {
- return mOverlayController.getDeviceProfile();
+ return mOverlayController.getLauncherDeviceProfile();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 0574058..6c7bdbf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -63,17 +63,17 @@
}
};
- private DeviceProfile mDeviceProfile;
+ private DeviceProfile mLauncherDeviceProfile;
private @Nullable TaskbarOverlayContext mOverlayContext;
private TaskbarControllers mControllers; // Initialized in init.
public TaskbarOverlayController(
- TaskbarActivityContext taskbarContext, DeviceProfile deviceProfile) {
+ TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile) {
mTaskbarContext = taskbarContext;
mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
mProxyView = new TaskbarOverlayProxyView();
mLayoutParams = createLayoutParams();
- mDeviceProfile = deviceProfile;
+ mLauncherDeviceProfile = launcherDeviceProfile;
}
/** Initialize the controller. */
@@ -132,13 +132,13 @@
}
/** The current device profile for the overlay window. */
- public DeviceProfile getDeviceProfile() {
- return mDeviceProfile;
+ public DeviceProfile getLauncherDeviceProfile() {
+ return mLauncherDeviceProfile;
}
/** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
- public void updateDeviceProfile(DeviceProfile dp) {
- mDeviceProfile = dp;
+ public void updateLauncherDeviceProfile(DeviceProfile dp) {
+ mLauncherDeviceProfile = dp;
Optional.ofNullable(mOverlayContext).ifPresent(c -> {
AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
c.dispatchDeviceProfileChanged();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index f450496..729eea9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,18 @@
package com.android.launcher3.uioverrides;
import android.app.Person;
+import android.appwidget.AppWidgetHost;
import android.content.pm.ShortcutInfo;
-import com.android.launcher3.Utilities;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+
+/**
+ * A wrapper for the hidden API calls
+ */
public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
@@ -29,4 +37,14 @@
Person[] persons = si.getPersons();
return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
}
+
+ /**
+ * Set the interaction handler for the host
+ * @param host AppWidgetHost that needs the interaction handler
+ * @param handler InteractionHandler for the views in the host
+ */
+ public static void setHostInteractionHandler(@NonNull AppWidgetHost host,
+ @Nullable LauncherAppWidgetHost.LauncherWidgetInteractionHandler handler) {
+ host.setInteractionHandler(handler::onInteraction);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 08d147f..2dde6b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -33,10 +33,12 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
/** Provides a Quickstep specific animation when launching an activity from an app widget. */
-class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
+class QuickstepInteractionHandler implements
+ LauncherAppWidgetHost.LauncherWidgetInteractionHandler {
private static final String TAG = "QuickstepInteractionHandler";
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3203f44..b228fdb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -45,7 +45,6 @@
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.util.BaseDepthController.WIDGET_DEPTH;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import android.animation.AnimatorSet;
@@ -59,22 +58,27 @@
import android.content.res.Configuration;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
+import android.media.permission.SafeCloseable;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.SystemProperties;
import android.view.Display;
import android.view.HapticFeedbackConstants;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.WindowManagerGlobal;
import android.window.SplashScreen;
+import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherWidgetHolder;
import com.android.launcher3.QuickstepAccessibilityDelegate;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
@@ -96,6 +100,7 @@
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.proxy.StartActivityParams;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
@@ -118,10 +123,8 @@
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.TouchController;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
@@ -133,13 +136,12 @@
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SplitWithKeyboardShortcutController;
import com.android.quickstep.util.TISBindHelper;
-import com.android.quickstep.util.ViewCapture;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.unfold.UnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -167,6 +169,7 @@
private FixedContainerItems mAllAppsPredictions;
private HotseatPredictionController mHotseatPredictionController;
private DepthController mDepthController;
+ private DesktopVisibilityController mDesktopVisibilityController;
private QuickstepTransitionManager mAppTransitionManager;
private OverviewActionsView mActionsView;
private TISBindHelper mTISBindHelper;
@@ -178,6 +181,8 @@
private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
private @Nullable RotationChangeProvider mRotationChangeProvider;
private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
+
+ private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
/**
* If Launcher restarted while in the middle of an Overview split select, it needs this data to
* recover. In all other cases this will remain null.
@@ -193,11 +198,13 @@
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
- RecentsView overviewPanel = (RecentsView) getOverviewPanel();
+ RecentsView overviewPanel = getOverviewPanel();
SplitSelectStateController controller =
new SplitSelectStateController(this, mHandler, getStateManager(),
getDepthController(), getStatsLogManager());
overviewPanel.init(mActionsView, controller);
+ mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
+ controller);
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
@@ -207,6 +214,7 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
+ mDesktopVisibilityController = new DesktopVisibilityController(this);
mHotseatPredictionController = new HotseatPredictionController(this);
mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
@@ -319,6 +327,17 @@
super.showAllAppsFromIntent(alreadyOnHome);
}
+ protected void onItemClicked(View view) {
+ if (!mSplitWithKeyboardShortcutController.handleSecondAppSelectionForSplit(view)) {
+ QuickstepLauncher.super.getItemOnClickListener().onClick(view);
+ }
+ }
+
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return this::onItemClicked;
+ }
+
@Override
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
Stream<SystemShortcut.Factory> base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
@@ -400,7 +419,8 @@
super.onDestroy();
mHotseatPredictionController.destroy();
- mViewCapture.close();
+ mSplitWithKeyboardShortcutController.onDestroy();
+ if (mViewCapture != null) mViewCapture.close();
}
@Override
@@ -485,10 +505,11 @@
return new QuickstepAtomicAnimationFactory(this);
}
- protected LauncherAppWidgetHost createAppWidgetHost() {
- LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost();
- appWidgetHost.setInteractionHandler(new QuickstepInteractionHandler(this));
- return appWidgetHost;
+ @Override
+ protected LauncherWidgetHolder createAppWidgetHolder() {
+ LauncherWidgetHolder appWidgetHolder = super.createAppWidgetHolder();
+ appWidgetHolder.setInteractionHandler(new QuickstepInteractionHandler(this));
+ return appWidgetHolder;
}
@Override
@@ -500,7 +521,9 @@
}
addMultiWindowModeChangedListener(mDepthController);
initUnfoldTransitionProgressProvider();
- mViewCapture = ViewCapture.INSTANCE.get(this).startCapture(getWindow());
+ if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+ mViewCapture = ViewCapture.getInstance().startCapture(getWindow());
+ }
}
@Override
@@ -593,9 +616,8 @@
super.onWidgetsTransition(progress);
onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
if (mEnableWidgetDepth) {
- WIDGET_DEPTH.set(getDepthController(),
- Utilities.mapToRange(progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth,
- EMPHASIZED));
+ getDepthController().widgetDepth.setValue(Utilities.mapToRange(
+ progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth, EMPHASIZED));
}
}
@@ -732,6 +754,10 @@
return mDepthController;
}
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ return mDesktopVisibilityController;
+ }
+
@Nullable
public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
return mUnfoldTransitionProgressProvider;
@@ -761,8 +787,8 @@
QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
@Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ public AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
// On the first call clear the reference.
signal.cancel();
@@ -824,6 +850,12 @@
return activityOptions;
}
+ @Override
+ @BinderThread
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+ mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
+ }
+
/**
* Adds a new launch cookie for the activity launch if supported.
*
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 0e1120b..e8e8328 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -43,7 +44,6 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.ClearAllButton;
@@ -164,7 +164,7 @@
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
- MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
+ MULTI_PROPERTY_VALUE, overviewButtonAlpha, config.getInterpolator(
ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 07ddcc8..5cc5f10 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -52,7 +53,6 @@
import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -64,6 +64,7 @@
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -73,6 +74,7 @@
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewGroup;
@@ -100,6 +102,7 @@
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface.AnimationFactory;
@@ -116,6 +119,7 @@
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TaskViewSimulator;
@@ -127,9 +131,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.common.TransactionPool;
@@ -312,6 +313,10 @@
// Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
private final float mQuickSwitchScaleScrollThreshold;
+ private final int mTaskbarAppWindowThreshold;
+ private final int mTaskbarCatchUpThreshold;
+ private boolean mTaskbarAlreadyOpen;
+
public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
@@ -332,11 +337,17 @@
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
- mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
- R.dimen.quick_switch_scaling_scroll_threshold);
- mSplashMainWindowShiftLength = -context.getResources().getDimensionPixelSize(
- R.dimen.starting_surface_exit_animation_window_shift_length);
+ Resources res = context.getResources();
+ mTaskbarAppWindowThreshold = res
+ .getDimensionPixelSize(R.dimen.taskbar_app_window_threshold);
+ mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold);
+
+ mQuickSwitchScaleScrollThreshold = res
+ .getDimension(R.dimen.quick_switch_scaling_scroll_threshold);
+
+ mSplashMainWindowShiftLength = -res
+ .getDimensionPixelSize(R.dimen.starting_surface_exit_animation_window_shift_length);
initAfterSubclassConstructor();
initStateCallbacks();
@@ -684,7 +695,7 @@
if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
- RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
: null;
final boolean recentsAttachedToAppWindow;
@@ -825,7 +836,7 @@
return;
}
mLauncherTransitionController.setProgress(
- Math.max(mCurrentShift.value, getScaleProgressDueToScroll()), mDragLengthFactor);
+ Math.max(getTaskbarProgress(), getScaleProgressDueToScroll()), mDragLengthFactor);
}
/**
@@ -868,7 +879,7 @@
// Only initialize the device profile, if it has not been initialized before, as in some
// configurations targets.homeContentInsets may not be correct.
if (mActivity == null) {
- RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[0];
+ RemoteAnimationTarget primaryTaskTarget = targets.apps[0];
// orientation state is independent of which remote target handle we use since both
// should be pointing to the same one. Just choose index 0 for now since that works for
// both split and non-split
@@ -952,7 +963,6 @@
new ActiveGestureLog.CompoundString("on gesture started (animate=false)"));
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
- SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
}
/**
@@ -1077,7 +1087,7 @@
}
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
@@ -1171,7 +1181,7 @@
}
private boolean hasReachedOverviewThreshold() {
- return mCurrentShift.value > MIN_PROGRESS_FOR_OVERVIEW;
+ return getTaskbarProgress() > MIN_PROGRESS_FOR_OVERVIEW;
}
@UiThread
@@ -1316,7 +1326,7 @@
protected abstract HomeAnimationFactory createHomeAnimationFactory(
ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent,
- boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget);
+ boolean appCanEnterPip, RemoteAnimationTarget runningTaskTarget);
private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
@Override
@@ -1363,7 +1373,7 @@
if (mGestureState.getEndTarget() == HOME) {
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
- final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ final RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
: null;
final ArrayList<IBinder> cookies = runningTaskTarget != null
@@ -1473,7 +1483,7 @@
}
}
- private int calculateWindowRotation(RemoteAnimationTargetCompat runningTaskTarget,
+ private int calculateWindowRotation(RemoteAnimationTarget runningTaskTarget,
RecentsOrientedState orientationState) {
if (runningTaskTarget.rotationChange != 0
&& TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -1486,7 +1496,7 @@
@Nullable
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
- RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
+ RemoteAnimationTarget runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = runningTaskTarget.taskInfo;
final RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator()
@@ -1835,6 +1845,17 @@
if (mRecentsAnimationController == null) return;
final ThumbnailData taskSnapshot =
mRecentsAnimationController.screenshotTask(runningTaskId);
+ // If split case, we should update all split tasks snapshot
+ if (mIsSwipeForSplit) {
+ int[] splitTaskIds = TopTaskTracker.INSTANCE.get(
+ mContext).getRunningSplitTaskIds();
+ for (int i = 0; i < splitTaskIds.length; i++) {
+ // Skip running one because done above.
+ if (splitTaskIds[i] == runningTaskId) continue;
+
+ mRecentsAnimationController.screenshotTask(splitTaskIds[i]);
+ }
+ }
MAIN_EXECUTOR.execute(() -> {
mTaskSnapshot = taskSnapshot;
if (!updateThumbnail(runningTaskId, false /* refreshView */)) {
@@ -1882,10 +1903,18 @@
}
private void finishCurrentTransitionToRecents() {
- // TODO(b/245569277#comment2): enable once isFreeformActive is implemented
- mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- if (mRecentsAnimationController != null) {
- mRecentsAnimationController.detachNavigationBarFromApp(true);
+ if (mRecentsView != null
+ && mActivityInterface.getDesktopVisibilityController() != null
+ && mActivityInterface.getDesktopVisibilityController().areFreeformTasksVisible()) {
+ mRecentsView.switchToScreenshot(() -> {
+ mRecentsView.finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ });
+ } else {
+ mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.detachNavigationBarFromApp(true);
+ }
}
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "finishRecentsAnimation",
@@ -1960,9 +1989,9 @@
reset();
}
- private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
+ private static boolean isNotInRecents(RemoteAnimationTarget app) {
return app.isNotInRecents
- || app.activityType == ACTIVITY_TYPE_HOME;
+ || app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
}
/**
@@ -2085,10 +2114,10 @@
}
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
if (mRecentsAnimationController != null) {
if (handleTaskAppeared(appearedTaskTargets)) {
- Optional<RemoteAnimationTargetCompat> taskTargetOptional =
+ Optional<RemoteAnimationTarget> taskTargetOptional =
Arrays.stream(appearedTaskTargets)
.filter(targetCompat ->
targetCompat.taskId == mGestureState.getLastStartedTaskId())
@@ -2097,7 +2126,7 @@
finishRecentsAnimationOnTasksAppeared();
return;
}
- RemoteAnimationTargetCompat taskTarget = taskTargetOptional.get();
+ RemoteAnimationTarget taskTarget = taskTargetOptional.get();
TaskView taskView = mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
finishRecentsAnimationOnTasksAppeared();
@@ -2108,16 +2137,13 @@
// When revealing the app with launcher splash screen, make the app visible
// and behind the splash view before the splash is animated away.
- SyncRtSurfaceTransactionApplierCompat surfaceApplier =
- new SyncRtSurfaceTransactionApplierCompat(splashView);
- ArrayList<SurfaceParams> params = new ArrayList<>();
- for (RemoteAnimationTargetCompat target : appearedTaskTargets) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
- builder.withAlpha(1);
- builder.withLayer(-1);
- params.add(builder.build());
+ SurfaceTransactionApplier surfaceApplier =
+ new SurfaceTransactionApplier(splashView);
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ for (RemoteAnimationTarget target : appearedTaskTargets) {
+ transaction.forSurface(target.leash).setAlpha(1).setLayer(-1);
}
- surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[0]));
+ surfaceApplier.scheduleApply(transaction);
SplashScreenExitAnimationUtils.startAnimations(splashView, taskTarget.leash,
mSplashMainWindowShiftLength, new TransactionPool(), new Rect(),
@@ -2183,7 +2209,7 @@
AnimatorControllerWithResistance playbackController =
remoteHandle.getPlaybackController();
if (playbackController != null) {
- playbackController.setProgress(Math.max(mCurrentShift.value,
+ playbackController.setProgress(Math.max(getTaskbarProgress(),
getScaleProgressDueToScroll()), mDragLengthFactor);
}
@@ -2227,6 +2253,41 @@
return scaleProgress;
}
+ /**
+ * Updates the current status of taskbar during this swipe.
+ */
+ public void setTaskbarAlreadyOpen(boolean taskbarAlreadyOpen) {
+ mTaskbarAlreadyOpen = taskbarAlreadyOpen;
+ }
+
+ /**
+ * Overrides the current shift progress to keep the app window at the bottom of the screen
+ * while the transient taskbar is being swiped in.
+ *
+ * There is also a catch up period so that the window can start moving 1:1 with the swipe.
+ */
+ private float getTaskbarProgress() {
+ if (!DisplayController.isTransientTaskbar(mContext)) {
+ return mCurrentShift.value;
+ }
+
+ if (mTaskbarAlreadyOpen) {
+ return mCurrentShift.value;
+ }
+
+ if (mCurrentDisplacement < mTaskbarAppWindowThreshold) {
+ return 0;
+ }
+
+ // "Catch up" with `mCurrentShift.value`.
+ if (mCurrentDisplacement < mTaskbarCatchUpThreshold) {
+ return Utilities.mapToRange(mCurrentDisplacement, mTaskbarAppWindowThreshold,
+ mTaskbarCatchUpThreshold, 0, mCurrentShift.value, ACCEL_DEACCEL);
+ }
+
+ return mCurrentShift.value;
+ }
+
private void setDividerShown(boolean shown, boolean immediate) {
if (mDividerAnimator != null) {
mDividerAnimator.cancel();
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index d432004..de150e1 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -40,6 +40,7 @@
import android.os.Build;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import androidx.annotation.Nullable;
@@ -50,6 +51,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarUIController;
@@ -62,7 +64,6 @@
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.HashMap;
import java.util.Optional;
@@ -142,6 +143,11 @@
}
@Nullable
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ return null;
+ }
+
+ @Nullable
public abstract TaskbarUIController getTaskbarController();
public final boolean isResumed() {
@@ -162,7 +168,7 @@
public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
public abstract Rect getOverviewWindowBounds(
- Rect homeBounds, RemoteAnimationTargetCompat target);
+ Rect homeBounds, RemoteAnimationTarget target);
public abstract boolean allowMinimizeSplitScreen();
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 6e963f3..c62220f 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -38,7 +39,6 @@
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -132,7 +132,7 @@
}
@Override
- public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+ public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
// TODO: Remove this once b/77875376 is fixed
return target.screenSpaceBounds;
}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 99f7bdd..374b839 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Intent.EXTRA_COMPONENT_NAME;
import static android.content.Intent.EXTRA_USER;
@@ -26,7 +27,6 @@
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -65,12 +66,11 @@
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -125,24 +125,24 @@
}
}
- private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
+ private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
}
- private void setHomeScaleAndAlpha(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, float verticalShift, float alpha) {
+ private void setHomeScaleAndAlpha(SurfaceProperties builder,
+ RemoteAnimationTarget app, float verticalShift, float alpha) {
float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
mTmpMatrix.setScale(scale, scale,
app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
- builder.withMatrix(mTmpMatrix).withAlpha(alpha);
+ builder.setMatrix(mTmpMatrix).setAlpha(alpha);
}
@Override
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
mAppCanEnterPip = appCanEnterPip;
if (appCanEnterPip) {
return new FallbackPipToHomeAnimationFactory();
@@ -154,7 +154,7 @@
private void startHomeIntent(
@Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory,
- @Nullable RemoteAnimationTargetCompat runningTaskTarget) {
+ @Nullable RemoteAnimationTarget runningTaskTarget) {
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
Intent intent = new Intent(mGestureState.getHomeIntent());
if (gestureContractAnimationFactory != null && runningTaskTarget != null) {
@@ -164,7 +164,7 @@
}
@Override
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) {
if (mActiveAnimationFactory != null
&& mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
mActiveAnimationFactory = null;
@@ -279,13 +279,13 @@
return mTargetRect;
}
- private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withAlpha(mRecentsAlpha.value);
+ private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
+ builder.setAlpha(mRecentsAlpha.value);
}
- private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params) {
+ private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
}
@@ -304,12 +304,12 @@
}
}
- public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
- RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
- if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
+ public boolean handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
+ if (appearedTaskTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
RemoteAnimationTargets targets = new RemoteAnimationTargets(
- new RemoteAnimationTargetCompat[] {appearedTaskTarget},
- new RemoteAnimationTargetCompat[0], new RemoteAnimationTargetCompat[0],
+ new RemoteAnimationTarget[] {appearedTaskTarget},
+ new RemoteAnimationTarget[0], new RemoteAnimationTarget[0],
appearedTaskTarget.mode);
mHomeAlphaParams.setTargetSet(targets);
updateHomeAlpha();
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index bcd9687..3c4ee75 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -28,6 +28,7 @@
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
+import android.view.RemoteAnimationTarget;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -37,7 +38,6 @@
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -143,7 +143,7 @@
private CachedTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
- private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private RemoteAnimationTarget mLastAppearedTaskTarget;
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
private RecentsAnimationController mRecentsAnimationController;
@@ -272,7 +272,7 @@
/**
* Updates the last task that appeared during this gesture.
*/
- public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
+ public void updateLastAppearedTaskTarget(RemoteAnimationTarget lastAppearedTaskTarget) {
mLastAppearedTaskTarget = lastAppearedTaskTarget;
if (lastAppearedTaskTarget != null) {
mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 8522a87..ecb3747 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -22,12 +22,14 @@
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -39,6 +41,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -51,7 +54,6 @@
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -128,9 +130,9 @@
// Animate the blur and wallpaper zoom
float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
float toDepthRatio = OVERVIEW.getDepth(activity);
- pa.addFloat(getDepthController(),
+ pa.addFloat(getDepthController().stateDepth,
new LauncherAnimUtils.ClampedProperty<>(
- DepthController.STATE_DEPTH, fromDepthRatio, toDepthRatio),
+ MULTI_PROPERTY_VALUE, fromDepthRatio, toDepthRatio),
fromDepthRatio, toDepthRatio, LINEAR);
}
};
@@ -175,6 +177,16 @@
@Nullable
@Override
+ public DesktopVisibilityController getDesktopVisibilityController() {
+ QuickstepLauncher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return null;
+ }
+ return launcher.getDesktopVisibilityController();
+ }
+
+ @Nullable
+ @Override
public LauncherTaskbarUIController getTaskbarController() {
QuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
@@ -252,7 +264,7 @@
}
@Override
- public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+ public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTarget target) {
return homeBounds;
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 7a281dd..2741751 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -36,6 +36,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.window.BackEvent;
+import android.window.BackProgressAnimator;
import android.window.IOnBackInvokedCallback;
import com.android.launcher3.AbstractFloatingView;
@@ -45,8 +46,6 @@
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
/**
* Controls the animation of swiping back and returning to launcher.
@@ -84,13 +83,14 @@
private final Interpolator mCancelInterpolator;
private final PointF mInitialTouchPos = new PointF();
- private RemoteAnimationTargetCompat mBackTarget;
+ private RemoteAnimationTarget mBackTarget;
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mSpringAnimationInProgress = false;
private boolean mAnimatorSetInProgress = false;
private float mBackProgress = 0;
private boolean mBackInProgress = false;
private IOnBackInvokedCallback mBackCallback;
+ private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
public LauncherBackAnimationController(
QuickstepLauncher launcher,
@@ -119,30 +119,41 @@
mBackCallback = new IOnBackInvokedCallback.Stub() {
@Override
public void onBackCancelled() {
- handler.post(() -> resetPositionAnimated());
+ handler.post(() -> {
+ resetPositionAnimated();
+ mProgressAnimator.reset();
+ });
}
@Override
public void onBackInvoked() {
- handler.post(() -> startTransition());
+ handler.post(() -> {
+ startTransition();
+ mProgressAnimator.reset();
+ });
}
@Override
public void onBackProgressed(BackEvent backEvent) {
- mBackProgress = backEvent.getProgress();
- // TODO: Update once the interpolation curve spec is finalized.
- mBackProgress =
- 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
- - mBackProgress);
- if (!mBackInProgress) {
- startBack(backEvent);
- } else {
- updateBackProgress(mBackProgress, backEvent);
- }
+ handler.post(() -> {
+ mProgressAnimator.onBackProgressed(backEvent);
+ });
}
@Override
- public void onBackStarted() { }
+ public void onBackStarted(BackEvent backEvent) {
+ handler.post(() -> {
+ startBack(backEvent);
+ mProgressAnimator.onBackStarted(backEvent, event -> {
+ mBackProgress = event.getProgress();
+ // TODO: Update once the interpolation curve spec is finalized.
+ mBackProgress =
+ 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
+ - mBackProgress);
+ updateBackProgress(mBackProgress, event);
+ });
+ });
+ }
};
SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback);
}
@@ -170,6 +181,7 @@
if (mBackCallback != null) {
SystemUiProxy.INSTANCE.get(mLauncher).clearBackToLauncherCallback(mBackCallback);
}
+ mProgressAnimator.reset();
mBackCallback = null;
}
@@ -183,33 +195,25 @@
mTransaction.show(appTarget.leash).apply();
mTransaction.setAnimationTransaction();
- mBackTarget = new RemoteAnimationTargetCompat(appTarget);
+ mBackTarget = appTarget;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
// TODO(b/218916755): Offset start rectangle in multiwindow mode.
mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
+ mCurrentRect.set(mStartRect);
}
private void updateBackProgress(float progress, BackEvent event) {
- if (mBackTarget == null) {
+ if (!mBackInProgress || mBackTarget == null) {
return;
}
float screenWidth = mStartRect.width();
float screenHeight = mStartRect.height();
- float dX = Math.abs(event.getTouchX() - mInitialTouchPos.x);
- // The 'follow width' is the width of the window if it completely matches
- // the gesture displacement.
- float followWidth = screenWidth - dX;
- // The 'progress width' is the width of the window if it strictly linearly interpolates
- // to minimum scale base on progress.
- float progressWidth = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
- // The final width is derived from interpolating between the follow with and progress width
- // using gesture progress.
- float width = Utilities.mapRange(progress, followWidth, progressWidth);
+ float width = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
float height = screenHeight / screenWidth * width;
float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight;
// Base the window movement in the Y axis on the touch movement in the Y axis.
- float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY;
+ float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY * progress;
// Move the window along the Y axis.
float top = (screenHeight - height) * 0.5f + deltaY;
// Move the window along the X axis.
@@ -242,20 +246,17 @@
/** Transform the target window to match the target rect. */
private void applyTransform(RectF targetRect, float cornerRadius) {
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
- new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
final float scale = targetRect.width() / mStartRect.width();
mTransformMatrix.reset();
mTransformMatrix.setScale(scale, scale);
mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
- builder.withMatrix(mTransformMatrix)
- .withWindowCrop(mStartRect)
- .withCornerRadius(cornerRadius);
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(mTransaction);
+ if (mBackTarget.leash.isValid()) {
+ mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]);
+ mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
+ mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
}
+
mTransaction.apply();
}
@@ -284,8 +285,8 @@
mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
Pair<RectFSpringAnim, AnimatorSet> pair =
mQuickstepTransitionManager.createWallpaperOpenAnimations(
- new RemoteAnimationTargetCompat[]{mBackTarget},
- new RemoteAnimationTargetCompat[]{},
+ new RemoteAnimationTarget[]{mBackTarget},
+ new RemoteAnimationTarget[0],
false /* fromUnlock */,
mCurrentRect,
cornerRadius);
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index d1533f0..bb781c8 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -30,6 +30,7 @@
import android.os.IBinder;
import android.os.UserHandle;
import android.util.Size;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import androidx.annotation.NonNull;
@@ -49,7 +50,6 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -70,7 +70,7 @@
@Override
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
if (mActivity == null) {
mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
isPresent -> mRecentsView.startHome());
@@ -144,7 +144,7 @@
private HomeAnimationFactory createWidgetHomeAnimationFactory(
LauncherAppWidgetHostView hostView, boolean isTargetTranslucent,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTarget runningTaskTarget) {
final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
RectF backgroundLocation = new RectF();
Rect crop = new Rect();
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index b7cdecd..9621ce6 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -20,6 +20,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import java.util.function.Function;
public class QuickstepTestInformationHandler extends TestInformationHandler {
@@ -112,6 +113,13 @@
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
return response;
}
+
+ case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: {
+ return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
+ tisBinder.getTaskbarManager()
+ .getCurrentActivityContext()
+ .getTaskbarAllAppsTopPadding());
+ }
}
return super.call(method, arg, extras);
@@ -159,4 +167,16 @@
throw new RuntimeException(e);
}
}
+
+ private <T> Bundle getTISBinderUIProperty(
+ BundleSetter<T> bundleSetter, Function<TouchInteractionService.TISBinder, T> provider) {
+ Bundle response = new Bundle();
+
+ runOnTISBinder(tisBinder -> bundleSetter.set(
+ response,
+ TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ provider.apply(tisBinder)));
+
+ return response;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 4f5e216..dc405ff 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,6 +15,9 @@
*/
package com.android.quickstep;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
@@ -23,8 +26,6 @@
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -36,8 +37,11 @@
import android.os.Handler;
import android.os.Looper;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl.Transaction;
import android.view.View;
+import android.window.RemoteTransition;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -77,8 +81,6 @@
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -238,9 +240,9 @@
mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
@Override
- public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
wallpaperTargets, nonAppTargets);
@@ -258,13 +260,11 @@
final LauncherAnimationRunner wrapper = new LauncherAnimationRunner(
mUiHandler, mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */);
- RemoteAnimationAdapterCompat adapterCompat = new RemoteAnimationAdapterCompat(
- wrapper, RECENTS_LAUNCH_DURATION,
- RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
- - STATUS_BAR_TRANSITION_PRE_DELAY, getIApplicationThread());
final ActivityOptions options = ActivityOptions.makeRemoteAnimation(
- adapterCompat.getWrapped(),
- adapterCompat.getRemoteTransition().getTransition());
+ new RemoteAnimationAdapter(wrapper, RECENTS_LAUNCH_DURATION,
+ RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
+ - STATUS_BAR_TRANSITION_PRE_DELAY),
+ new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread()));
final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(options,
onEndCallback);
activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
@@ -279,9 +279,9 @@
* Composes the animations for a launch from the recents list if possible.
*/
private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets) {
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets) {
AnimatorSet target = new AnimatorSet();
boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
@@ -400,28 +400,25 @@
private void startHomeInternal() {
LauncherAnimationRunner runner = new LauncherAnimationRunner(
getMainThreadHandler(), mAnimationToHomeFactory, true);
- RemoteAnimationAdapterCompat adapterCompat =
- new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
- getIApplicationThread());
ActivityOptions options = ActivityOptions.makeRemoteAnimation(
- adapterCompat.getWrapped(),
- adapterCompat.getRemoteTransition().getTransition());
+ new RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0),
+ new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread()));
startHomeIntentSafely(this, options.toBundle());
}
private final RemoteAnimationFactory mAnimationToHomeFactory =
new RemoteAnimationFactory() {
@Override
- public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
+ RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
AnimatorPlaybackController controller = getStateManager()
.createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
controller.dispatchOnStart();
RemoteAnimationTargets targets = new RemoteAnimationTargets(
appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
- for (RemoteAnimationTargetCompat app : targets.apps) {
+ for (RemoteAnimationTarget app : targets.apps) {
new Transaction().setAlpha(app.leash, 1).apply();
}
AnimatorSet anim = new AnimatorSet();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index b233521..2451ad8 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -33,9 +33,7 @@
import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
@@ -88,17 +86,17 @@
@BinderThread
@Deprecated
public final void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] appTargets, Rect homeContentInsets,
+ RemoteAnimationTarget[] appTargets, Rect homeContentInsets,
Rect minimizedHomeBounds) {
- onAnimationStart(controller, appTargets, new RemoteAnimationTargetCompat[0],
+ onAnimationStart(controller, appTargets, new RemoteAnimationTarget[0],
homeContentInsets, minimizedHomeBounds);
}
// Called only in R+ platform
@BinderThread
public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
mController = new RecentsAnimationController(animationController,
mAllowMinimizeSplitScreen, this::onAnimationFinished);
@@ -107,12 +105,13 @@
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
mController::finishAnimationToApp);
} else {
- final RemoteAnimationTarget[] nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(
- Arrays.stream(appTargets).map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new));
+ RemoteAnimationTarget[] nonAppTargets =
+ mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
+ }
final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
- wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
- homeContentInsets, minimizedHomeBounds);
+ wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureLog.INSTANCE.addLog(
@@ -141,7 +140,7 @@
@BinderThread
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
+ public void onTasksAppeared(RemoteAnimationTarget[] apps) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureLog.INSTANCE.addLog("onTasksAppeared",
ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
@@ -197,7 +196,7 @@
/**
* Callback made when a task started from the recents is ready for an app transition.
*/
- default void onTasksAppeared(@NonNull RemoteAnimationTargetCompat[] appearedTaskTarget) {}
+ default void onTasksAppeared(@NonNull RemoteAnimationTarget[] appearedTaskTarget) {}
/**
* @return whether this will call onFinished or not (onFinished should only be called once).
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 542c0d4..81e3782 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.window.PictureInPictureSurfaceTransaction;
@@ -37,7 +38,6 @@
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
@@ -114,7 +114,7 @@
* {@link RecentsAnimationCallbacks#onTasksAppeared}}.
*/
@UiThread
- public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
+ public void removeTaskTarget(@NonNull RemoteAnimationTarget target) {
UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index b6d9016..388e125 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -15,11 +15,10 @@
*/
package com.android.quickstep;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import android.graphics.Rect;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
/**
* Extension of {@link RemoteAnimationTargets} with additional information about swipe
@@ -30,8 +29,8 @@
public final Rect homeContentInsets;
public final Rect minimizedHomeBounds;
- public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public RecentsAnimationTargets(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
Rect homeContentInsets, Rect minimizedHomeBounds) {
super(apps, wallpapers, nonApps, MODE_CLOSING);
this.homeContentInsets = homeContentInsets;
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 1bd808d..80aaad0 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -15,9 +15,11 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -29,41 +31,40 @@
private final CopyOnWriteArrayList<ReleaseCheck> mReleaseChecks = new CopyOnWriteArrayList<>();
- public final RemoteAnimationTargetCompat[] unfilteredApps;
- public final RemoteAnimationTargetCompat[] apps;
- public final RemoteAnimationTargetCompat[] wallpapers;
- public final RemoteAnimationTargetCompat[] nonApps;
+ public final RemoteAnimationTarget[] unfilteredApps;
+ public final RemoteAnimationTarget[] apps;
+ public final RemoteAnimationTarget[] wallpapers;
+ public final RemoteAnimationTarget[] nonApps;
public final int targetMode;
public final boolean hasRecents;
private boolean mReleased = false;
- public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public RemoteAnimationTargets(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
int targetMode) {
- ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
+ ArrayList<RemoteAnimationTarget> filteredApps = new ArrayList<>();
boolean hasRecents = false;
if (apps != null) {
- for (RemoteAnimationTargetCompat target : apps) {
+ for (RemoteAnimationTarget target : apps) {
if (target.mode == targetMode) {
filteredApps.add(target);
}
- hasRecents |= target.activityType ==
- RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
+ hasRecents |= target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS;
}
}
this.unfilteredApps = apps;
- this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
+ this.apps = filteredApps.toArray(new RemoteAnimationTarget[filteredApps.size()]);
this.wallpapers = wallpapers;
this.targetMode = targetMode;
this.hasRecents = hasRecents;
this.nonApps = nonApps;
}
- public RemoteAnimationTargetCompat findTask(int taskId) {
- for (RemoteAnimationTargetCompat target : apps) {
+ public RemoteAnimationTarget findTask(int taskId) {
+ for (RemoteAnimationTarget target : apps) {
if (target.taskId == taskId) {
return target;
}
@@ -74,12 +75,12 @@
/**
* Gets the navigation bar remote animation target if exists.
*/
- public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+ public RemoteAnimationTarget getNavBarRemoteAnimationTarget() {
return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
}
- public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
- for (RemoteAnimationTargetCompat target : nonApps) {
+ public RemoteAnimationTarget getNonAppTargetOfType(int type) {
+ for (RemoteAnimationTarget target : nonApps) {
if (target.windowType == type) {
return target;
}
@@ -88,19 +89,19 @@
}
/** Returns the first opening app target. */
- public RemoteAnimationTargetCompat getFirstAppTarget() {
+ public RemoteAnimationTarget getFirstAppTarget() {
return apps.length > 0 ? apps[0] : null;
}
/** Returns the task id of the first opening app target, or -1 if none is found. */
public int getFirstAppTargetTaskId() {
- RemoteAnimationTargetCompat target = getFirstAppTarget();
+ RemoteAnimationTarget target = getFirstAppTarget();
return target == null ? -1 : target.taskId;
}
public boolean isAnimatingHome() {
- for (RemoteAnimationTargetCompat target : unfilteredApps) {
- if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ for (RemoteAnimationTarget target : unfilteredApps) {
+ if (target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) {
return true;
}
}
@@ -123,15 +124,19 @@
}
mReleaseChecks.clear();
mReleased = true;
+ release(unfilteredApps);
+ release(wallpapers);
+ release(nonApps);
+ }
- for (RemoteAnimationTargetCompat target : unfilteredApps) {
- target.release();
- }
- for (RemoteAnimationTargetCompat target : wallpapers) {
- target.release();
- }
- for (RemoteAnimationTargetCompat target : nonApps) {
- target.release();
+ private static void release(RemoteAnimationTarget[] targets) {
+ for (RemoteAnimationTarget target : targets) {
+ if (target.leash != null) {
+ target.leash.release();
+ }
+ if (target.startLeash != null) {
+ target.startLeash.release();
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 7183c49..4c41bef 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,6 +17,8 @@
package com.android.quickstep;
import android.content.Context;
+import android.graphics.Rect;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -24,7 +26,6 @@
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -75,7 +76,7 @@
*/
public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
- RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i];
+ RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
mRemoteTargetHandles[i].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, null));
mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
@@ -100,8 +101,8 @@
*/
public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
int[] splitIds) {
- RemoteAnimationTargetCompat topLeftTarget; // only one set if single/fullscreen task
- RemoteAnimationTargetCompat bottomRightTarget;
+ RemoteAnimationTarget topLeftTarget; // only one set if single/fullscreen task
+ RemoteAnimationTarget bottomRightTarget;
if (mRemoteTargetHandles.length == 1) {
// If we're not in split screen, the splitIds count doesn't really matter since we
// should always hit this case.
@@ -119,8 +120,8 @@
// remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
// vice versa
mSplitBounds = new SplitBounds(
- topLeftTarget.startScreenSpaceBounds,
- bottomRightTarget.startScreenSpaceBounds, splitIds[0], splitIds[1]);
+ getStartBounds(topLeftTarget),
+ getStartBounds(bottomRightTarget), splitIds[0], splitIds[1]);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
@@ -134,6 +135,10 @@
return mRemoteTargetHandles;
}
+ private Rect getStartBounds(RemoteAnimationTarget target) {
+ return target.startBounds == null ? target.screenSpaceBounds : target.startBounds;
+ }
+
/**
* Ensures that we aren't excluding ancillary targets such as home/recents
*
@@ -144,11 +149,10 @@
*/
private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
RemoteAnimationTargets targets,
- RemoteAnimationTargetCompat targetToExclude) {
- ArrayList<RemoteAnimationTargetCompat> targetsWithoutExcluded =
- new ArrayList<RemoteAnimationTargetCompat>();
+ RemoteAnimationTarget targetToExclude) {
+ ArrayList<RemoteAnimationTarget> targetsWithoutExcluded = new ArrayList<>();
- for (RemoteAnimationTargetCompat targetCompat : targets.unfilteredApps) {
+ for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
if (targetCompat == targetToExclude) {
continue;
}
@@ -162,9 +166,8 @@
targetsWithoutExcluded.add(targetCompat);
}
- final RemoteAnimationTargetCompat[] filteredApps =
- targetsWithoutExcluded.toArray(
- new RemoteAnimationTargetCompat[targetsWithoutExcluded.size()]);
+ final RemoteAnimationTarget[] filteredApps = targetsWithoutExcluded.toArray(
+ new RemoteAnimationTarget[targetsWithoutExcluded.size()]);
return new RemoteAnimationTargets(
filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode);
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index baeb514..ddb06ce 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -24,6 +24,7 @@
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
@@ -37,11 +38,10 @@
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -65,6 +65,7 @@
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
// visible.
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+ protected float mCurrentDisplacement;
// The distance needed to drag to reach the task size in recents.
protected int mTransitionDragLength;
@@ -116,6 +117,8 @@
public void updateDisplacement(float displacement) {
// We are moving in the negative x/y direction
displacement = -displacement;
+ mCurrentDisplacement = displacement;
+
float shift;
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
shift = mDragLengthFactor;
@@ -335,11 +338,11 @@
}
@Override
- public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mCropRect)
- .withCornerRadius(params.getCornerRadius());
+ public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app,
+ TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mCropRect)
+ .setCornerRadius(params.getCornerRadius());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 129b88e..acf597b 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -44,6 +44,8 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.IOnBackInvokedCallback;
+import android.window.RemoteTransition;
+import android.window.TransitionFilter;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -53,13 +55,11 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -74,6 +74,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashMap;
/**
* Holds the reference to SystemUI.
@@ -90,7 +91,6 @@
private IPip mPip;
private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
private ISplitScreen mSplitScreen;
- private IFloatingTasks mFloatingTasks;
private IOneHanded mOneHanded;
private IShellTransitions mShellTransitions;
private IStartingWindow mStartingWindow;
@@ -110,7 +110,8 @@
private IStartingWindowListener mStartingWindowListener;
private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
- private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
+ private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
+ new LinkedHashMap<>();
private IOnBackInvokedCallback mBackToLauncherCallback;
// Used to dedupe calls to SystemUI
@@ -168,7 +169,7 @@
}
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
- IFloatingTasks floatingTasks, IOneHanded oneHanded, IShellTransitions shellTransitions,
+ IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
IBackAnimation backAnimation, IDesktopMode desktopMode) {
@@ -176,7 +177,6 @@
mSystemUiProxy = proxy;
mPip = pip;
mSplitScreen = splitScreen;
- mFloatingTasks = floatingTasks;
mOneHanded = oneHanded;
mShellTransitions = shellTransitions;
mStartingWindow = startingWindow;
@@ -198,9 +198,7 @@
if (mSysuiUnlockAnimationController != null && mLauncherUnlockAnimationController != null) {
setLauncherUnlockAnimationController(mLauncherUnlockAnimationController);
}
- for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
- registerRemoteTransition(mRemoteTransitions.get(i));
- }
+ new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
if (mRecentTasksListener != null && mRecentTasks != null) {
registerRecentTasksListener(mRecentTasksListener);
}
@@ -210,7 +208,7 @@
}
public void clearProxy() {
- setProxy(null, null, null, null, null, null, null, null, null, null, null);
+ setProxy(null, null, null, null, null, null, null, null, null, null);
}
// TODO(141886704): Find a way to remove this
@@ -347,17 +345,6 @@
}
@Override
- public void notifySwipeUpGestureStarted() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.notifySwipeUpGestureStarted();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call notifySwipeUpGestureStarted", e);
- }
- }
- }
-
- @Override
public void notifyPrioritizedRotation(int rotation) {
if (mSystemUiProxy != null) {
try {
@@ -550,11 +537,11 @@
/** Start multiple tasks in split-screen simultaneously. */
public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
@SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
- RemoteTransitionCompat remoteTransition, InstanceId instanceId) {
+ RemoteTransition remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
- splitRatio, remoteTransition.getTransition(), instanceId);
+ splitRatio, remoteTransition, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTasks");
}
@@ -564,12 +551,12 @@
public void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent,
Bundle options1, int taskId, Bundle options2,
@SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
- RemoteTransitionCompat remoteTransition, InstanceId instanceId) {
+ RemoteTransition remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startIntentAndTask(pendingIntent, fillInIntent, options1,
taskId, options2, splitPosition, splitRatio,
- remoteTransition.getTransition(), instanceId);
+ remoteTransition, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntentAndTask");
}
@@ -578,11 +565,11 @@
public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
- float splitRatio, RemoteTransitionCompat remoteTransition, InstanceId instanceId) {
+ float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
- splitPosition, splitRatio, remoteTransition.getTransition(), instanceId);
+ splitPosition, splitRatio, remoteTransition, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startShortcutAndTask");
}
@@ -671,6 +658,7 @@
*
* @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
*/
+ @Nullable
public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -682,6 +670,7 @@
return null;
}
+ @Nullable
public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
@@ -694,20 +683,6 @@
}
//
- // Floating tasks
- //
-
- public void showFloatingTask(Intent intent) {
- if (mFloatingTasks != null) {
- try {
- mFloatingTasks.showTask(intent);
- } catch (RemoteException e) {
- Log.w(TAG, "Launcher: Failed call showFloatingTask", e);
- }
- }
- }
-
- //
// One handed
//
@@ -735,24 +710,24 @@
// Remote transitions
//
- public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ public void registerRemoteTransition(
+ RemoteTransition remoteTransition, TransitionFilter filter) {
if (mShellTransitions != null) {
try {
- mShellTransitions.registerRemote(remoteTransition.getFilter(),
- remoteTransition.getTransition());
+ mShellTransitions.registerRemote(filter, remoteTransition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
}
- if (!mRemoteTransitions.contains(remoteTransition)) {
- mRemoteTransitions.add(remoteTransition);
+ if (!mRemoteTransitions.containsKey(remoteTransition)) {
+ mRemoteTransitions.put(remoteTransition, filter);
}
}
- public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
if (mShellTransitions != null) {
try {
- mShellTransitions.unregisterRemote(remoteTransition.getTransition());
+ mShellTransitions.unregisterRemote(remoteTransition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 7f16565..90e8091 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -15,11 +15,13 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.systemui.shared.system.RemoteTransitionCompat.newRemoteTransition;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -28,6 +30,7 @@
import android.os.SystemProperties;
import android.util.Log;
import android.view.RemoteAnimationTarget;
+import android.window.RemoteTransition;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -38,12 +41,9 @@
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import java.util.Arrays;
import java.util.HashMap;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
@@ -57,7 +57,7 @@
private RecentsAnimationTargets mTargets;
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
- private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private RemoteAnimationTarget mLastAppearedTaskTarget;
private Runnable mLiveTileCleanUpHandler;
private Context mCtx;
@@ -151,12 +151,12 @@
}
@Override
- public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
- RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
+ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
+ RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
- for (RemoteAnimationTargetCompat compat : appearedTaskTargets) {
- if (compat.activityType == ACTIVITY_TYPE_HOME
+ for (RemoteAnimationTarget compat : appearedTaskTargets) {
+ if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME
&& activityInterface.getCreatedActivity() instanceof RecentsActivity) {
// When receive opening home activity while recents is running, enter home
// and dismiss recents.
@@ -165,11 +165,11 @@
}
}
- RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.getNoCreate()
- .onStartingSplitLegacy(Arrays.stream(appearedTaskTargets)
- .map(RemoteAnimationTargetCompat::unwrap)
- .toArray(RemoteAnimationTarget[]::new));
-
+ RemoteAnimationTarget[] nonAppTargets = SystemUiProxy.INSTANCE.get(mCtx)
+ .onStartingSplitLegacy(appearedTaskTargets);
+ if (nonAppTargets == null) {
+ nonAppTargets = new RemoteAnimationTarget[0];
+ }
if (activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView =
@@ -177,13 +177,13 @@
if (recentsView != null) {
recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
appearedTaskTargets,
- new RemoteAnimationTargetCompat[0] /* wallpaper */,
- RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */);
+ new RemoteAnimationTarget[0] /* wallpaper */,
+ nonAppTargets /* nonApps */);
return;
}
- } else if (nonAppTargets != null && nonAppTargets.length > 0) {
+ } else if (nonAppTargets.length > 0) {
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
- RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */,
+ nonAppTargets /* nonApps */,
true /*shown*/, dividerAnimator -> {
dividerAnimator.start();
dividerAnimator.end();
@@ -224,11 +224,10 @@
mCallbacks.addListener(listener);
if (ENABLE_SHELL_TRANSITIONS) {
- RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
+ RemoteTransition transition = newRemoteTransition(mCallbacks,
mController != null ? mController.getController() : null,
mCtx.getIApplicationThread());
- final ActivityOptions options = ActivityOptions.makeRemoteTransition(
- transition.getTransition());
+ final ActivityOptions options = ActivityOptions.makeRemoteTransition(transition);
// Allowing to pause Home if Home is top activity and Recents is not Home. So when user
// start home when recents animation is playing, the home activity can be resumed again
// to let the transition controller collect Home activity.
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index d722778..67360c4 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.Nullable;
@@ -34,7 +35,6 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.List;
@@ -87,9 +87,9 @@
}
- public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
+ public static boolean taskIsATargetWithMode(RemoteAnimationTarget[] targets,
int taskId, int mode) {
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == mode && target.taskId == taskId) {
return true;
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index df80e2f..e8722e2 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -34,16 +36,13 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Matrix;
@@ -51,6 +50,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.View;
import android.window.TransitionInfo;
@@ -70,6 +70,8 @@
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -80,7 +82,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.ArrayList;
import java.util.List;
@@ -102,7 +103,7 @@
* opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
*/
public static TaskView findTaskViewToLaunch(
- RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
+ RecentsView recentsView, View v, RemoteAnimationTarget[] targets) {
if (v instanceof TaskView) {
TaskView taskView = (TaskView) v;
return recentsView.isTaskViewVisible(taskView) ? taskView : null;
@@ -132,7 +133,7 @@
}
// Resolve the opening task id
int openingTaskId = -1;
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTarget target : targets) {
if (target.mode == MODE_OPENING) {
openingTaskId = target.taskId;
break;
@@ -155,9 +156,9 @@
public static void createRecentsWindowAnimator(
@NonNull TaskView v, boolean skipViewChanges,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
@Nullable DepthController depthController,
PendingAnimation out) {
RecentsView recentsView = v.getRecentsView();
@@ -167,7 +168,7 @@
final RemoteAnimationTargets targets =
new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
MODE_OPENING);
- final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
+ final RemoteAnimationTarget navBarTarget = targets.getNavBarRemoteAnimationTarget();
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
targets.addReleaseCheck(applier);
@@ -250,21 +251,24 @@
@Override
public void onUpdate(float percent, boolean initOnly) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+
// TODO Do we need to operate over multiple TVSs for the navbar leash?
for (RemoteTargetHandle handle : remoteTargetHandles) {
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceProperties navBuilder =
+ transaction.forSurface(navBarTarget.leash);
+
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
taskViewSimulator.getCurrentCropRect().round(cropRect);
- navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
- .withWindowCrop(cropRect)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(taskViewSimulator.getCurrentMatrix())
+ .setWindowCrop(cropRect)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- handle.getTransformParams().applySurfaceParams(navBuilder.build());
+ handle.getTransformParams().applySurfaceParams(transaction);
}
}
});
@@ -371,8 +375,8 @@
});
if (depthController != null) {
- out.setFloat(depthController, STATE_DEPTH, BACKGROUND_APP.getDepth(baseActivity),
- TOUCH_RESPONSE_INTERPOLATOR);
+ out.setFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE,
+ BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE_INTERPOLATOR);
}
}
@@ -394,9 +398,8 @@
*/
public static void composeRecentsSplitLaunchAnimator(GroupedTaskView launchingTaskView,
@NonNull StateManager stateManager, @Nullable DepthController depthController,
- int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
- @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t,
- @NonNull Runnable finishCallback) {
+ int initialTaskId, int secondTaskId, @NonNull TransitionInfo transitionInfo,
+ SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
if (launchingTaskView != null) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
@@ -406,12 +409,12 @@
}
});
- final RemoteAnimationTargetCompat[] appTargets =
+ final RemoteAnimationTarget[] appTargets =
RemoteAnimationTargetCompat.wrapApps(transitionInfo, t, null /* leashMap */);
- final RemoteAnimationTargetCompat[] wallpaperTargets =
+ final RemoteAnimationTarget[] wallpaperTargets =
RemoteAnimationTargetCompat.wrapNonApps(
transitionInfo, true /* wallpapers */, t, null /* leashMap */);
- final RemoteAnimationTargetCompat[] nonAppTargets =
+ final RemoteAnimationTarget[] nonAppTargets =
RemoteAnimationTargetCompat.wrapNonApps(
transitionInfo, false /* wallpapers */, t, null /* leashMap */);
final RecentsView recentsView = launchingTaskView.getRecentsView();
@@ -480,17 +483,16 @@
* If {@param launchingTaskView} is not null, then this will play the tasks launch animation
* from the position of the GroupedTaskView (when user taps on the TaskView to start it).
* Technically this case should be taken care of by
- * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
+ * {@link #composeRecentsSplitLaunchAnimatorLegacy} below, but the way we launch tasks whether
* it's a single task or multiple tasks results in different entry-points.
*
* If it is null, then it will simply fade in the starting apps and fade out launcher (for the
* case where launcher handles animating starting split tasks from app icon) */
public static void composeRecentsSplitLaunchAnimatorLegacy(
- @Nullable GroupedTaskView launchingTaskView, int initialTaskId,
- @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+ @Nullable GroupedTaskView launchingTaskView, int initialTaskId, int secondTaskId,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets,
@NonNull StateManager stateManager,
@Nullable DepthController depthController,
@NonNull Runnable finishCallback) {
@@ -513,7 +515,7 @@
final ArrayList<SurfaceControl> openingTargets = new ArrayList<>();
final ArrayList<SurfaceControl> closingTargets = new ArrayList<>();
- for (RemoteAnimationTargetCompat appTarget : appTargets) {
+ for (RemoteAnimationTarget appTarget : appTargets) {
final int taskId = appTarget.taskInfo != null ? appTarget.taskInfo.taskId : -1;
final int mode = appTarget.mode;
final SurfaceControl leash = appTarget.leash;
@@ -568,9 +570,9 @@
}
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
- @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
+ @NonNull RemoteAnimationTarget[] appTargets,
+ @NonNull RemoteAnimationTarget[] wallpaperTargets,
+ @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing,
@NonNull StateManager stateManager, @NonNull RecentsView recentsView,
@Nullable DepthController depthController) {
boolean skipLauncherChanges = !launcherClosing;
@@ -662,7 +664,7 @@
* @return the animator animating the surfaces
*/
public static ValueAnimator createSplitAuxiliarySurfacesAnimator(
- RemoteAnimationTargetCompat[] nonApps, boolean shown,
+ RemoteAnimationTarget[] nonApps, boolean shown,
Consumer<ValueAnimator> animatorHandler) {
if (nonApps == null || nonApps.length == 0) {
return null;
@@ -672,7 +674,7 @@
List<SurfaceControl> auxiliarySurfaces = new ArrayList<>(nonApps.length);
boolean hasSurfaceToAnimate = false;
for (int i = 0; i < nonApps.length; ++i) {
- final RemoteAnimationTargetCompat targ = nonApps[i];
+ final RemoteAnimationTarget targ = nonApps[i];
final SurfaceControl leash = targ.leash;
if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null && leash.isValid()) {
auxiliarySurfaces.add(leash);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1452c8f..43aafbe 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -27,11 +27,11 @@
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
@@ -59,6 +59,7 @@
import android.view.Choreographer;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.BinderThread;
@@ -66,11 +67,13 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarManager;
@@ -101,7 +104,6 @@
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.SplitScreenBounds;
-import com.android.quickstep.util.ViewCapture;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -112,7 +114,6 @@
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
@@ -170,8 +171,6 @@
IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
KEY_EXTRA_SHELL_SPLIT_SCREEN));
- IFloatingTasks floatingTasks = IFloatingTasks.Stub.asInterface(bundle.getBinder(
- KEY_EXTRA_SHELL_FLOATING_TASKS));
IOneHanded onehanded = IOneHanded.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
@@ -189,7 +188,7 @@
bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
- splitscreen, floatingTasks, onehanded, shellTransitions, startingWindow,
+ splitscreen, onehanded, shellTransitions, startingWindow,
recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode);
TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()");
preloadOverview(true /* fromInit */);
@@ -230,12 +229,6 @@
@BinderThread
@Override
- public void onTip(int actionType, int viewType) {
- // Please delete this method from the interface
- }
-
- @BinderThread
- @Override
public void onAssistantAvailable(boolean available) {
MAIN_EXECUTOR.execute(() -> {
mDeviceState.setAssistantAvailable(available);
@@ -252,10 +245,9 @@
});
}
- @BinderThread
- public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
- boolean gestureSwipeLeft) {
- // Remove this method from the interface
+ @Override
+ public void onNavigationBarSurface(SurfaceControl surface) {
+ // TODO: implement
}
@BinderThread
@@ -296,6 +288,16 @@
MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff);
}
+ @BinderThread
+ @Override
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+ StatefulActivity activity =
+ mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
+ if (activity != null) {
+ activity.enterStageSplitFromRunningApp(leftOrTop);
+ }
+ }
+
/**
* Preloads the Overview activity.
*
@@ -543,6 +545,18 @@
mOverviewComponentObserver.onSystemUiStateChanged();
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
+ boolean wasFreeformActive =
+ (lastSysUIFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
+ boolean isFreeformActive =
+ (systemUiStateFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0;
+ if (wasFreeformActive != isFreeformActive) {
+ DesktopVisibilityController controller = mOverviewComponentObserver
+ .getActivityInterface().getDesktopVisibilityController();
+ if (controller != null) {
+ controller.setFreeformTasksVisible(isFreeformActive);
+ }
+ }
+
boolean wasExpanded = (lastSysUIFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
boolean isExpanded =
(systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
@@ -1224,7 +1238,9 @@
}
mTaskbarManager.dumpLogs("", pw);
- ViewCapture.INSTANCE.get(this).dump(pw, fd);
+ if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+ ViewCapture.getInstance().dump(pw, fd, this);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 19a6c38..062e50e 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -45,7 +45,7 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiPropertyFactory;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.ClearAllButton;
@@ -95,7 +95,7 @@
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
- MultiValueAlpha.VALUE, overviewButtonAlpha, LINEAR);
+ MultiPropertyFactory.MULTI_PROPERTY_VALUE, overviewButtonAlpha, LINEAR);
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 6bc24f2..8410149 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -37,6 +37,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.VelocityTracker;
import com.android.launcher3.R;
@@ -53,13 +54,12 @@
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.HashMap;
@@ -290,9 +290,9 @@
@Override
public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
- builder.withMatrix(mMatrix);
+ builder.setMatrix(mMatrix);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 7ccd8af..9d269fb 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -37,10 +37,9 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.PointF;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -50,9 +49,11 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.tracing.InputConsumerProto;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.AbsSwipeUpHandler;
@@ -70,7 +71,6 @@
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -136,11 +136,9 @@
// Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
private float mStartDisplacement;
- private Handler mMainThreadHandler;
- private Runnable mCancelRecentsAnimationRunnable = () -> {
- ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
- true /* restoreHomeStackPosition */);
- };
+ private final boolean mIsTransientTaskbar;
+ private final boolean mTaskbarAlreadyOpen;
+ private final int mTaskbarHomeOverviewThreshold;
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
@@ -152,10 +150,13 @@
mNavBarPosition = mDeviceState.getNavBarPosition();
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
- mMainThreadHandler = new Handler(Looper.getMainLooper());
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
+ Resources res = base.getResources();
+ mTaskbarHomeOverviewThreshold = res
+ .getDimensionPixelSize(R.dimen.taskbar_home_overview_threshold);
+
mMotionPauseDetector = new MotionPauseDetector(base, false,
mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
? MotionEvent.AXIS_X : MotionEvent.AXIS_Y);
@@ -166,6 +167,10 @@
mInputMonitorCompat = inputMonitorCompat;
mInputEventReceiver = inputEventReceiver;
+ TaskbarUIController controller = mActivityInterface.getTaskbarController();
+ mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed();
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(base);
+
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
@@ -292,6 +297,7 @@
float upDist = -displacement;
boolean passedSlop = squaredHypot(displacementX, displacementY)
>= mSquaredTouchSlop;
+
if (!mPassedSlopOnThisGesture && passedSlop) {
mPassedSlopOnThisGesture = true;
}
@@ -336,7 +342,13 @@
}
if (mDeviceState.isFullyGesturalNavMode()) {
- mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
+ float minDisplacement = mMotionPauseMinDisplacement;
+
+ if (mIsTransientTaskbar && !mTaskbarAlreadyOpen) {
+ minDisplacement += mTaskbarHomeOverviewThreshold;
+ }
+
+ mMotionPauseDetector.setDisallowPause(upDist < minDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(ev);
mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
@@ -370,6 +382,8 @@
// Notify the handler that the gesture has actually started
mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
+
+ mInteractionHandler.setTaskbarAlreadyOpen(mTaskbarAlreadyOpen);
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
@@ -437,13 +451,6 @@
}
onConsumerAboutToBeSwitched();
onInteractionGestureFinished();
-
- // Cancel the recents animation if SysUI happens to handle UP before we have a chance
- // to start the recents animation. In addition, workaround for b/126336729 by delaying
- // the cancel of the animation for a period, in case SysUI is slow to handle UP and we
- // handle DOWN & UP and move the home stack before SysUI can start the activity
- mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
- mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100);
}
cleanupAfterGesture();
TraceHelper.INSTANCE.endSection(traceToken);
@@ -465,7 +472,6 @@
@Override
public void onConsumerAboutToBeSwitched() {
Preconditions.assertUIThread();
- mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
if (mInteractionHandler != null) {
// The consumer is being switched while we are active. Set up the shared state to be
// used by the next animation
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index 3785de4..1430492 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -15,9 +15,14 @@
*/
package com.android.quickstep.inputconsumers;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
@@ -25,6 +30,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -37,20 +43,32 @@
private final GestureDetector mLongPressDetector;
private final float mSquaredTouchSlop;
-
- private float mDownX, mDownY;
+ private float mLongPressDownX, mLongPressDownY;
private boolean mCanceledUnstashHint;
private final float mUnstashArea;
private final float mScreenWidth;
+ private final int mTaskbarThreshold;
+ private boolean mHasPassedTaskbarThreshold;
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ private final boolean mIsTransientTaskbar;
+
public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
super(delegate, inputMonitor);
mTaskbarActivityContext = taskbarActivityContext;
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx;
- mUnstashArea = context.getResources()
- .getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
+
+ Resources res = context.getResources();
+ mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
+ mTaskbarThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
+
+ mIsTransientTaskbar = DisplayController.isTransientTaskbar(context);
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
@@ -76,28 +94,71 @@
final float y = ev.getRawY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+
+ mHasPassedTaskbarThreshold = false;
+ mTaskbarActivityContext.setAutohideSuspendFlag(
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING, true);
if (isInArea(x)) {
- mDownX = x;
- mDownY = y;
- mTaskbarActivityContext.startTaskbarUnstashHint(
- /* animateForward = */ true);
- mCanceledUnstashHint = false;
+ if (!mIsTransientTaskbar) {
+ mLongPressDownX = x;
+ mLongPressDownY = y;
+ mTaskbarActivityContext.startTaskbarUnstashHint(
+ /* animateForward = */ true);
+ mCanceledUnstashHint = false;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
}
break;
case MotionEvent.ACTION_MOVE:
- if (!mCanceledUnstashHint
- && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
+ if (!mIsTransientTaskbar
+ && !mCanceledUnstashHint
+ && squaredHypot(mLongPressDownX - x, mLongPressDownY - y)
+ > mSquaredTouchSlop) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
mCanceledUnstashHint = true;
}
+
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ float displacementY = mLastPos.y - mDownPos.y;
+ float verticalDist = Math.abs(displacementY);
+ boolean passedTaskbarThreshold = verticalDist >= mTaskbarThreshold;
+
+ if (!mHasPassedTaskbarThreshold
+ && passedTaskbarThreshold
+ && mIsTransientTaskbar) {
+ mHasPassedTaskbarThreshold = true;
+
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (!mCanceledUnstashHint) {
+ if (!mIsTransientTaskbar && !mCanceledUnstashHint) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
}
+ mTaskbarActivityContext.setAutohideSuspendFlag(
+ FLAG_AUTOHIDE_SUSPEND_TOUCHING, false);
+ mHasPassedTaskbarThreshold = false;
break;
}
}
@@ -111,7 +172,9 @@
}
private void onLongPressDetected(MotionEvent motionEvent) {
- if (mTaskbarActivityContext != null && isInArea(motionEvent.getRawX())) {
+ if (mTaskbarActivityContext != null
+ && isInArea(motionEvent.getRawX())
+ && !mIsTransientTaskbar) {
boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar();
if (taskBarPressed) {
setActive(motionEvent);
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 8ad17cb..7ff576e 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -143,6 +143,11 @@
mVibrator = getSystemService(Vibrator.class);
mAnimatedBackground = findViewById(R.id.animated_background);
+ // There's a bug in the currently used external Lottie library (v5.2.0), and it doesn't load
+ // the correct animation from the raw resources when configuration changes, so we need to
+ // manually load the resource and pass it to Lottie.
+ mAnimatedBackground.setAnimation(getResources().openRawResource(R.raw.all_set_page_bg),
+ null);
startBackgroundAnimation();
}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index fa7bc04..d7ff0be 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -33,7 +33,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -53,9 +52,11 @@
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
+import com.android.quickstep.util.RecordingSurfaceTransaction;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@TargetApi(Build.VERSION_CODES.R)
abstract class SwipeUpGestureTutorialController extends TutorialController {
@@ -415,21 +416,23 @@
private class FakeTransformParams extends TransformParams {
@Override
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, null, this);
- return new SurfaceParams[] {builder.build()};
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+ proxy.onBuildTargetParams(transaction.mockProperties, null, this);
+ return transaction;
}
@Override
- public void applySurfaceParams(SurfaceParams[] params) {
- SurfaceParams p = params[0];
- mFakeTaskView.setAnimationMatrix(p.matrix);
- mFakePreviousTaskView.setAnimationMatrix(p.matrix);
- mFakeTaskViewRect.set(p.windowCrop);
- mFakeTaskViewRadius = p.cornerRadius;
- mFakeTaskView.invalidateOutline();
- mFakePreviousTaskView.invalidateOutline();
+ public void applySurfaceParams(SurfaceTransaction params) {
+ if (params instanceof RecordingSurfaceTransaction) {
+ MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+ mFakeTaskView.setAnimationMatrix(p.matrix);
+ mFakePreviousTaskView.setAnimationMatrix(p.matrix);
+ mFakeTaskViewRect.set(p.windowCrop);
+ mFakeTaskViewRadius = p.cornerRadius;
+ mFakeTaskView.invalidateOutline();
+ mFakePreviousTaskView.invalidateOutline();
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 0c422a0..dac5a31 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -217,6 +217,7 @@
private Optional<String> mEditText = Optional.empty();
private SliceItem mSliceItem;
private LauncherAtom.Slice mSlice;
+ private Optional<Integer> mCardinality = Optional.empty();
StatsCompatLogger(Context context, ActivityContext activityContext) {
mContext = context;
@@ -304,6 +305,12 @@
}
@Override
+ public StatsLogger withCardinality(int cardinality) {
+ this.mCardinality = Optional.of(cardinality);
+ return this;
+ }
+
+ @Override
public void log(EventEnum event) {
if (!Utilities.ATLEAST_R) {
return;
@@ -421,6 +428,7 @@
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return;
}
+ int cardinality = mCardinality.orElseGet(() -> getCardinality(atomInfo));
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
@@ -446,7 +454,7 @@
atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
- getCardinality(atomInfo) /* cardinality */,
+ cardinality /* cardinality */,
getFeatures(atomInfo) /* features */,
getSearchAttributes(atomInfo) /* searchAttributes */,
getAttributes(atomInfo) /* attributes */
@@ -465,6 +473,7 @@
private int mPackageId = 0;
private long mLatencyInMillis;
private int mQueryLength = -1;
+ private int mSubEventType = 0;
StatsCompatLatencyLogger(Context context, ActivityContext activityContext) {
mContext = context;
@@ -502,6 +511,12 @@
}
@Override
+ public StatsLatencyLogger withSubEventType(int type) {
+ this.mSubEventType = type;
+ return this;
+ }
+
+ @Override
public void log(EventEnum event) {
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
@@ -518,7 +533,8 @@
mPackageId, // package_id
mLatencyInMillis, // latency_in_millis
mType.getId(), //type
- mQueryLength // query_length
+ mQueryLength, // query_length
+ mSubEventType // sub_event_type
);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 064e675..877e28a 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -25,6 +25,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.systemui.shared.system.BlurUtils;
/**
@@ -45,20 +46,15 @@
}
};
- private static final MultiPropertyFactory<BaseDepthController> DEPTH_PROPERTY_FACTORY =
- new MultiPropertyFactory<>("depthProperty", DEPTH, Float::max);
-
- private static final int DEPTH_INDEX_STATE_TRANSITION = 1;
- private static final int DEPTH_INDEX_WIDGET = 2;
-
- /** Property to set the depth for state transition. */
- public static final FloatProperty<BaseDepthController> STATE_DEPTH =
- DEPTH_PROPERTY_FACTORY.get(DEPTH_INDEX_STATE_TRANSITION);
- /** Property to set the depth for widget picker. */
- public static final FloatProperty<BaseDepthController> WIDGET_DEPTH =
- DEPTH_PROPERTY_FACTORY.get(DEPTH_INDEX_WIDGET);
+ private static final int DEPTH_INDEX_STATE_TRANSITION = 0;
+ private static final int DEPTH_INDEX_WIDGET = 1;
+ private static final int DEPTH_INDEX_COUNT = 2;
protected final Launcher mLauncher;
+ /** Property to set the depth for state transition. */
+ public final MultiProperty stateDepth;
+ /** Property to set the depth for widget picker. */
+ public final MultiProperty widgetDepth;
/**
* Blur radius when completely zoomed out, in pixels.
@@ -92,6 +88,11 @@
mLauncher = activity;
mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
mWallpaperManager = activity.getSystemService(WallpaperManager.class);
+
+ MultiPropertyFactory<BaseDepthController> depthProperty =
+ new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
+ stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
+ widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
}
protected void setCrossWindowBlursEnabled(boolean isEnabled) {
diff --git a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
new file mode 100644
index 0000000..a2f48dd
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
@@ -0,0 +1,28 @@
+/*
+ * 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.quickstep.util;
+
+/**
+ * Extension for {@link SurfaceTransaction} which records the commands for mocking
+ */
+public class RecordingSurfaceTransaction extends SurfaceTransaction {
+
+ /**
+ * A mock builder which can be used for recording values
+ */
+ public final MockProperties mockProperties = new MockProperties();
+
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index ee82ae6..10f2eaa 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -16,23 +16,22 @@
package com.android.quickstep.util;
import android.animation.AnimatorSet;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import android.view.RemoteAnimationTarget;
public abstract class RemoteAnimationProvider {
- public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets);
+ public abstract AnimatorSet createWindowAnimation(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets);
/**
* @return the target with the lowest opaque layer for a certain app animation, or null.
*/
- public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
- RemoteAnimationTargetCompat[] appTargets, int mode) {
+ public static RemoteAnimationTarget findLowestOpaqueLayerTarget(
+ RemoteAnimationTarget[] appTargets, int mode) {
int lowestLayer = Integer.MAX_VALUE;
int lowestLayerIndex = -1;
for (int i = appTargets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = appTargets[i];
+ RemoteAnimationTarget target = appTargets[i];
if (target.mode == mode && !target.isTranslucent) {
int layer = target.prefixOrderIndex;
if (layer < lowestLayer) {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 81c124f..382cf79 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -15,14 +15,14 @@
*/
package com.android.quickstep.util;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl.Transaction;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
/**
* Animation listener which fades out the closing targets
@@ -32,24 +32,24 @@
private final RemoteAnimationTargets mTarget;
private boolean mFirstFrame = true;
- public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ public RemoteFadeOutAnimationListener(RemoteAnimationTarget[] appTargets,
+ RemoteAnimationTarget[] wallpaperTargets) {
mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets,
- new RemoteAnimationTargetCompat[0], MODE_CLOSING);
+ new RemoteAnimationTarget[0], MODE_CLOSING);
}
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- TransactionCompat t = new TransactionCompat();
+ Transaction t = new Transaction();
if (mFirstFrame) {
- for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
+ for (RemoteAnimationTarget target : mTarget.unfilteredApps) {
t.show(target.leash);
}
mFirstFrame = false;
}
float alpha = 1 - valueAnimator.getAnimatedFraction();
- for (RemoteAnimationTargetCompat app : mTarget.apps) {
+ for (RemoteAnimationTarget app : mTarget.apps) {
t.setAlpha(app.leash, alpha);
}
t.apply();
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index f07f990..08f9fa6 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -22,8 +22,10 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
+import static com.android.launcher3.util.SplitConfigurationOptions.getOppositeStagePosition;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.PendingIntent;
@@ -33,12 +35,17 @@
import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.RemoteTransition;
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
@@ -56,14 +63,11 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.RemoteTransitionRunner;
import java.util.function.Consumer;
@@ -84,6 +88,8 @@
private ItemInfo mItemInfo;
private Intent mInitialTaskIntent;
private int mInitialTaskId = INVALID_TASK_ID;
+ private String mInitialTaskPackageName;
+ private Intent mSecondTaskIntent;
private int mSecondTaskId = INVALID_TASK_ID;
private String mSecondTaskPackageName;
private boolean mRecentsAnimationRunning;
@@ -95,6 +101,8 @@
/** Represents where split is intended to be invoked from. */
private StatsLogManager.EventEnum mSplitEvent;
+ private FloatingTaskView mFirstFloatingTaskView;
+
public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
DepthController depthController, StatsLogManager statsLogManager) {
mContext = context;
@@ -106,19 +114,36 @@
}
/**
- * To be called after first task selected
+ * To be called after first task selected in Overview.
*/
- public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition,
+ public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
- mInitialTaskId = taskId;
+ mInitialTaskId = task.key.id;
+ mInitialTaskPackageName = task.getTopComponent().getPackageName();
setInitialData(stagePosition, splitEvent, itemInfo);
}
+ /**
+ * To be called after first task selected from home or all apps.
+ */
public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
@NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
mInitialTaskIntent = intent;
mUser = itemInfo.user;
mItemInfo = itemInfo;
+ mInitialTaskPackageName = intent.getComponent().getPackageName();
+ setInitialData(stagePosition, splitEvent, itemInfo);
+ }
+
+ /**
+ * To be called after first task selected from using a split shortcut from the fullscreen
+ * running app.
+ */
+ public void setInitialTaskSelect(ActivityManager.RunningTaskInfo info,
+ @StagePosition int stagePosition, @NonNull ItemInfo itemInfo,
+ StatsLogManager.EventEnum splitEvent) {
+ mInitialTaskId = info.taskId;
+ mInitialTaskPackageName = info.topActivity.getPackageName();
setInitialData(stagePosition, splitEvent, itemInfo);
}
@@ -134,27 +159,11 @@
* to be launched. Call after launcher side animations are complete.
*/
public void launchSplitTasks(Consumer<Boolean> callback) {
- final Intent fillInIntent;
- if (mInitialTaskIntent != null) {
- fillInIntent = new Intent();
- if (TextUtils.equals(mInitialTaskIntent.getComponent().getPackageName(),
- mSecondTaskPackageName)) {
- fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- }
- } else {
- fillInIntent = null;
- }
-
- final PendingIntent pendingIntent = mInitialTaskIntent == null ? null : (mUser != null
- ? PendingIntent.getActivityAsUser(mContext, 0, mInitialTaskIntent,
- FLAG_MUTABLE, null /* options */, mUser)
- : PendingIntent.getActivity(mContext, 0, mInitialTaskIntent, FLAG_MUTABLE));
-
Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
LogUtils.getShellShareableInstanceId();
- launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition,
- callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
- instanceIds.first);
+ launchTasks(mInitialTaskId, mInitialTaskIntent, mInitialTaskPackageName, mSecondTaskId,
+ mSecondTaskIntent, mSecondTaskPackageName, mStagePosition, callback,
+ false /* freezeTaskList */, DEFAULT_SPLIT_RATIO, instanceIds.first);
mStatsLogManager.logger()
.withItemInfo(mItemInfo)
@@ -162,23 +171,25 @@
.log(mSplitEvent);
}
-
/**
* To be called as soon as user selects the second task (even if animations aren't complete)
* @param task The second task that will be launched.
*/
public void setSecondTask(Task task) {
mSecondTaskId = task.key.id;
- if (mInitialTaskIntent != null) {
- mSecondTaskPackageName = task.getTopComponent().getPackageName();
- }
+ mSecondTaskPackageName = task.getTopComponent().getPackageName();
+ }
+
+ public void setSecondTask(Intent intent) {
+ mSecondTaskIntent = intent;
+ mSecondTaskPackageName = intent.getComponent().getPackageName();
}
/**
* To be called when we want to launch split pairs from an existing GroupedTaskView.
*/
- public void launchTasks(GroupedTaskView groupedTaskView,
- Consumer<Boolean> callback, boolean freezeTaskList) {
+ public void launchTasks(GroupedTaskView groupedTaskView, Consumer<Boolean> callback,
+ boolean freezeTaskList) {
mLaunchingTaskView = groupedTaskView;
TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
groupedTaskView.getTaskIdAttributeContainers();
@@ -194,22 +205,23 @@
*/
public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
- launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2,
- stagePosition, callback, freezeTaskList, splitRatio, null);
+ launchTasks(taskId1, null /* intent1 */, null /* packageName1 */, taskId2,
+ null /* intent2 */, null /* packageName2 */, stagePosition, callback,
+ freezeTaskList, splitRatio, null);
}
/**
* To be called when we want to launch split pairs from Overview. Split can be initiated from
* either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
* fill in intent with a taskId2 are set.
- * @param taskPendingIntent is null when split is initiated from Overview
+ * @param intent1 is null when split is initiated from Overview
* @param stagePosition representing location of task1
* @param shellInstanceId loggingId to be used by shell, will be non-null for actions that create
* a split instance, null for cases that bring existing instaces to the
* foreground (quickswitch, launching previous pairs from overview)
*/
- public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
- @Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition,
+ public void launchTasks(int taskId1, @Nullable Intent intent1, String packageName1, int taskId2,
+ @Nullable Intent intent2, String packageName2, @StagePosition int stagePosition,
Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio,
@Nullable InstanceId shellInstanceId) {
TestLogging.recordEvent(
@@ -220,57 +232,105 @@
}
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
final RemoteSplitLaunchTransitionRunner animationRunner =
- new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2,
- callback);
- final RemoteTransitionCompat remoteTransition = new RemoteTransitionCompat(
- animationRunner, MAIN_EXECUTOR,
+ new RemoteSplitLaunchTransitionRunner(taskId1, taskId2, callback);
+ final RemoteTransition remoteTransition = new RemoteTransition(animationRunner,
ActivityThread.currentActivityThread().getApplicationThread());
- if (taskPendingIntent == null) {
+ if (intent1 == null && intent2 == null) {
mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2,
null /* options2 */, stagePosition, splitRatio, remoteTransition,
shellInstanceId);
+ } else if (intent2 == null) {
+ launchIntentOrShortcut(intent1, packageName2, options1, taskId2, stagePosition,
+ splitRatio, remoteTransition, shellInstanceId);
+ } else if (intent1 == null) {
+ launchIntentOrShortcut(intent2, packageName1, options1, taskId1,
+ getOppositeStagePosition(stagePosition), splitRatio, remoteTransition,
+ shellInstanceId);
} else {
- final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
- taskPendingIntent.getCreatorUserHandle());
- if (shortcutInfo != null) {
- mSystemUiProxy.startShortcutAndTask(shortcutInfo,
- options1.toBundle(), taskId2, null /* options2 */, stagePosition,
- splitRatio, remoteTransition, shellInstanceId);
- } else {
- mSystemUiProxy.startIntentAndTask(taskPendingIntent,
- fillInIntent, options1.toBundle(), taskId2, null /* options2 */,
- stagePosition, splitRatio, remoteTransition, shellInstanceId);
- }
+ // TODO: the case when both split apps are started from an intent.
}
} else {
final RemoteSplitLaunchAnimationRunner animationRunner =
- new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2,
- callback);
+ new RemoteSplitLaunchAnimationRunner(taskId1, taskId2, callback);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
- 300, 150,
+ animationRunner, 300, 150,
ActivityThread.currentActivityThread().getApplicationThread());
- if (taskPendingIntent == null) {
+ if (intent1 == null && intent2 == null) {
mSystemUiProxy.startTasksWithLegacyTransition(taskId1, options1.toBundle(),
taskId2, null /* options2 */, stagePosition, splitRatio, adapter,
shellInstanceId);
+ } else if (intent2 == null) {
+ launchIntentOrShortcutLegacy(intent1, packageName2, options1, taskId2,
+ stagePosition, splitRatio, adapter, shellInstanceId);
+ } else if (intent1 == null) {
+ launchIntentOrShortcutLegacy(intent2, packageName1, options1, taskId1,
+ getOppositeStagePosition(stagePosition), splitRatio, adapter,
+ shellInstanceId);
} else {
- final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
- taskPendingIntent.getCreatorUserHandle());
- if (shortcutInfo != null) {
- mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
- options1.toBundle(), taskId2, null /* options2 */, stagePosition,
- splitRatio, adapter, shellInstanceId);
- } else {
- mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
- fillInIntent, options1.toBundle(), taskId2, null /* options2 */,
- stagePosition, splitRatio, adapter, shellInstanceId);
- }
+ // TODO: the case when both split apps are started from an intent.
}
}
}
+ private void launchIntentOrShortcut(Intent intent, String otherTaskPackageName,
+ ActivityOptions options1, int taskId, @StagePosition int stagePosition,
+ float splitRatio, RemoteTransition remoteTransition,
+ @Nullable InstanceId shellInstanceId) {
+ PendingIntent pendingIntent = getPendingIntent(intent);
+ final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+ pendingIntent.getCreatorUserHandle());
+ if (shortcutInfo != null) {
+ mSystemUiProxy.startShortcutAndTask(shortcutInfo,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition,
+ splitRatio, remoteTransition, shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntentAndTask(pendingIntent,
+ getFillInIntent(intent, otherTaskPackageName), options1.toBundle(), taskId,
+ null /* options2 */, stagePosition, splitRatio, remoteTransition,
+ shellInstanceId);
+ }
+ }
+
+ private void launchIntentOrShortcutLegacy(Intent intent, String otherTaskPackageName,
+ ActivityOptions options1, int taskId, @StagePosition int stagePosition,
+ float splitRatio, RemoteAnimationAdapter adapter,
+ @Nullable InstanceId shellInstanceId) {
+ PendingIntent pendingIntent = getPendingIntent(intent);
+ final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+ pendingIntent.getCreatorUserHandle());
+ if (shortcutInfo != null) {
+ mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
+ options1.toBundle(), taskId, null /* options2 */, stagePosition,
+ splitRatio, adapter, shellInstanceId);
+ } else {
+ mSystemUiProxy.startIntentAndTaskWithLegacyTransition(pendingIntent,
+ getFillInIntent(intent, otherTaskPackageName), options1.toBundle(), taskId,
+ null /* options2 */, stagePosition, splitRatio, adapter,
+ shellInstanceId);
+ }
+ }
+
+ private PendingIntent getPendingIntent(Intent intent) {
+ return intent == null ? null : (mUser != null
+ ? PendingIntent.getActivityAsUser(mContext, 0, intent,
+ FLAG_MUTABLE, null /* options */, mUser)
+ : PendingIntent.getActivity(mContext, 0, intent, FLAG_MUTABLE));
+ }
+
+ private Intent getFillInIntent(Intent intent, String otherTaskPackageName) {
+ if (intent == null) {
+ return null;
+ }
+
+ Intent fillInIntent = new Intent();
+ if (TextUtils.equals(intent.getComponent().getPackageName(), otherTaskPackageName)) {
+ fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
+ return fillInIntent;
+ }
+
+
public @StagePosition int getActiveSplitStagePosition() {
return mStagePosition;
}
@@ -280,7 +340,7 @@
}
public void setRecentsAnimationRunning(boolean running) {
- this.mRecentsAnimationRunning = running;
+ mRecentsAnimationRunning = running;
}
@Nullable
@@ -308,65 +368,75 @@
/**
* Requires Shell Transitions
*/
- private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner {
+ private class RemoteSplitLaunchTransitionRunner extends IRemoteTransition.Stub {
private final int mInitialTaskId;
- private final PendingIntent mInitialTaskPendingIntent;
private final int mSecondTaskId;
private final Consumer<Boolean> mSuccessCallback;
- RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
- int secondTaskId, Consumer<Boolean> callback) {
+ RemoteSplitLaunchTransitionRunner(int initialTaskId, int secondTaskId,
+ Consumer<Boolean> callback) {
mInitialTaskId = initialTaskId;
- mInitialTaskPendingIntent = initialTaskPendingIntent;
mSecondTaskId = secondTaskId;
mSuccessCallback = callback;
}
@Override
- public void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
- TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
- mDepthController, mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId,
- info, t, () -> {
- finishCallback.run();
- if (mSuccessCallback != null) {
- mSuccessCallback.accept(true);
- }
- });
- // After successful launch, call resetState
- resetState();
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ final Runnable finishAdapter = () -> {
+ try {
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call transition finished callback", e);
+ }
+ };
+
+ MAIN_EXECUTOR.execute(() -> {
+ TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
+ mDepthController, mInitialTaskId, mSecondTaskId, info, t, () -> {
+ finishAdapter.run();
+ if (mSuccessCallback != null) {
+ mSuccessCallback.accept(true);
+ }
+ });
+ // After successful launch, call resetState
+ resetState();
+ });
}
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishedCallback) { }
}
/**
* LEGACY
* Remote animation runner for animation to launch an app.
*/
- private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat {
+ private class RemoteSplitLaunchAnimationRunner extends RemoteAnimationRunnerCompat {
private final int mInitialTaskId;
- private final PendingIntent mInitialTaskPendingIntent;
private final int mSecondTaskId;
private final Consumer<Boolean> mSuccessCallback;
- RemoteSplitLaunchAnimationRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
- int secondTaskId, Consumer<Boolean> successCallback) {
+ RemoteSplitLaunchAnimationRunner(int initialTaskId, int secondTaskId,
+ Consumer<Boolean> successCallback) {
mInitialTaskId = initialTaskId;
- mInitialTaskPendingIntent = initialTaskPendingIntent;
mSecondTaskId = secondTaskId;
mSuccessCallback = successCallback;
}
@Override
- public void onAnimationStart(int transit, RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
Runnable finishedCallback) {
postAsyncCallback(mHandler,
() -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
- mLaunchingTaskView, mInitialTaskId, mInitialTaskPendingIntent,
- mSecondTaskId, apps, wallpapers, nonApps, mStateManager,
- mDepthController, () -> {
+ mLaunchingTaskView, mInitialTaskId, mSecondTaskId, apps, wallpapers,
+ nonApps, mStateManager, mDepthController, () -> {
finishedCallback.run();
if (mSuccessCallback != null) {
mSuccessCallback.accept(true);
@@ -376,7 +446,7 @@
}
@Override
- public void onAnimationCancelled() {
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
postAsyncCallback(mHandler, () -> {
if (mSuccessCallback != null) {
// Launching legacy tasks while recents animation is running will always cause
@@ -394,7 +464,10 @@
public void resetState() {
mInitialTaskId = INVALID_TASK_ID;
mInitialTaskIntent = null;
+ mInitialTaskPackageName = null;
mSecondTaskId = INVALID_TASK_ID;
+ mSecondTaskIntent = null;
+ mSecondTaskPackageName = null;
mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
mRecentsAnimationRunning = false;
mLaunchingTaskView = null;
@@ -407,7 +480,7 @@
* chosen
*/
public boolean isSplitSelectActive() {
- return isInitialTaskIntentSet() && mSecondTaskId == INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && !isSecondTaskIntentSet();
}
/**
@@ -415,7 +488,7 @@
* be launched
*/
public boolean isBothSplitAppsConfirmed() {
- return isInitialTaskIntentSet() && mSecondTaskId != INVALID_TASK_ID;
+ return isInitialTaskIntentSet() && isSecondTaskIntentSet();
}
private boolean isInitialTaskIntentSet() {
@@ -425,4 +498,16 @@
public int getInitialTaskId() {
return mInitialTaskId;
}
+
+ private boolean isSecondTaskIntentSet() {
+ return (mSecondTaskId != INVALID_TASK_ID || mSecondTaskIntent != null);
+ }
+
+ public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) {
+ mFirstFloatingTaskView = floatingTaskView;
+ }
+
+ public FloatingTaskView getFirstFloatingTaskView() {
+ return mFirstFloatingTaskView;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
new file mode 100644
index 0000000..3587bd1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -0,0 +1,193 @@
+/*
+ * 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.quickstep.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.view.View;
+
+import androidx.annotation.BinderThread;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.FloatingTaskView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/** Transitions app from fullscreen to stage split when triggered from keyboard shortcuts. */
+public class SplitWithKeyboardShortcutController {
+
+ private final QuickstepLauncher mLauncher;
+ private final SplitSelectStateController mController;
+ private final OverviewComponentObserver mOverviewComponentObserver;
+
+ private final int mSplitPlaceholderSize;
+ private final int mSplitPlaceholderInset;
+
+ public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
+ SplitSelectStateController controller) {
+ mLauncher = launcher;
+ mController = controller;
+ RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
+ launcher.getApplicationContext());
+ mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
+ deviceState);
+
+ mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_size);
+ mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_inset);
+ }
+
+ @BinderThread
+ public void enterStageSplit(boolean leftOrTop) {
+ if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()) {
+ return;
+ }
+ RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
+ SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
+ false /* allowMinimizeSplitScreen */);
+ SplitWithKeyboardShortcutRecentsAnimationListener listener =
+ new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
+
+ MAIN_EXECUTOR.execute(() -> {
+ callbacks.addListener(listener);
+ UI_HELPER_EXECUTOR.execute(
+ // Transition from fullscreen app to enter stage split in launcher with
+ // recents animation.
+ () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+ mOverviewComponentObserver.getOverviewIntent(),
+ SystemClock.uptimeMillis(), callbacks, null, null));
+ });
+ }
+
+ /**
+ * Handles second app selection from stage split. If the item can't be opened in split or
+ * it's not in stage split state, we pass it onto Launcher's default item click handler.
+ */
+ public boolean handleSecondAppSelectionForSplit(View view) {
+ if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
+ || !mController.isSplitSelectActive()) {
+ return false;
+ }
+ Object tag = view.getTag();
+ Intent intent;
+ if (tag instanceof WorkspaceItemInfo) {
+ final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
+ intent = workspaceItemInfo.intent;
+ } else if (tag instanceof com.android.launcher3.model.data.AppInfo) {
+ final com.android.launcher3.model.data.AppInfo appInfo =
+ (com.android.launcher3.model.data.AppInfo) tag;
+ intent = appInfo.intent;
+ } else {
+ return false;
+ }
+ mController.setSecondTask(intent);
+ mController.launchSplitTasks(aBoolean -> mLauncher.getDragLayer().removeView(
+ mController.getFirstFloatingTaskView()));
+ return true;
+ }
+
+ public void onDestroy() {
+ mOverviewComponentObserver.onDestroy();
+ }
+
+ private class SplitWithKeyboardShortcutRecentsAnimationListener implements
+ RecentsAnimationCallbacks.RecentsAnimationListener {
+
+ private final boolean mLeftOrTop;
+ private final Rect mTempRect = new Rect();
+
+ private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
+ mLeftOrTop = leftOrTop;
+ }
+
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ ActivityManager.RunningTaskInfo runningTaskInfo =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ mController.setInitialTaskSelect(runningTaskInfo,
+ mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
+ null /* itemInfo */,
+ mLeftOrTop ? LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP
+ : LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM);
+
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
+ mSplitPlaceholderSize, mSplitPlaceholderInset, mLauncher.getDeviceProfile(),
+ mController.getActiveSplitStagePosition(), mTempRect);
+
+ PendingAnimation anim = new PendingAnimation(
+ SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
+ RectF startingTaskRect = new RectF();
+ final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
+ mLauncher, mLauncher.getDragLayer(),
+ controller.screenshotTask(runningTaskInfo.taskId).thumbnail,
+ null /* icon */, startingTaskRect);
+ RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
+ .getIconCache()
+ .updateIconInBackground(
+ Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo,
+ false /* isLocked */),
+ (task) -> {
+ if (task.thumbnail != null) {
+ floatingTaskView.setIcon(task.thumbnail.thumbnail);
+ }
+ });
+ floatingTaskView.setAlpha(1);
+ floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
+ false /* fadeWithThumbnail */, true /* isStagedTask */);
+ mController.setFirstFloatingTaskView(floatingTaskView);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ controller.finish(true /* toRecents */, null /* onFinishComplete */,
+ false /* sendUserLeaveHint */);
+ }
+ });
+ anim.buildAnim().start();
+ }
+ };
+}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
new file mode 100644
index 0000000..7ab285d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * 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.quickstep.util;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Helper class for building a {@link Transaction}.
+ */
+public class SurfaceTransaction {
+
+ private final Transaction mTransaction = new Transaction();
+ private final float[] mTmpValues = new float[9];
+
+ /**
+ * Creates a new builder for the provided surface
+ */
+ public SurfaceProperties forSurface(SurfaceControl surface) {
+ return surface.isValid() ? new SurfaceProperties(surface) : new MockProperties();
+ }
+
+ /**
+ * Returns the final transaction
+ */
+ public Transaction getTransaction() {
+ return mTransaction;
+ }
+
+ /**
+ * Utility class to update surface params in a transaction
+ */
+ public class SurfaceProperties {
+
+ private final SurfaceControl mSurface;
+
+ SurfaceProperties(SurfaceControl surface) {
+ mSurface = surface;
+ }
+
+ /**
+ * @param alpha The alpha value to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setAlpha(float alpha) {
+ mTransaction.setAlpha(mSurface, alpha);
+ return this;
+ }
+
+ /**
+ * @param matrix The matrix to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ mTransaction.setMatrix(mSurface, matrix, mTmpValues);
+ return this;
+ }
+
+ /**
+ * @param windowCrop The window crop to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ mTransaction.setWindowCrop(mSurface, windowCrop);
+ return this;
+ }
+
+ /**
+ * @param relativeLayer The relative layer.
+ * @return this Builder
+ */
+ public SurfaceProperties setLayer(int relativeLayer) {
+ mTransaction.setLayer(mSurface, relativeLayer);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for rounded corners to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setCornerRadius(float radius) {
+ mTransaction.setCornerRadius(mSurface, radius);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for the shadows to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setShadowRadius(float radius) {
+ mTransaction.setShadowRadius(mSurface, radius);
+ return this;
+ }
+ }
+
+ /**
+ * Extension of {@link SurfaceProperties} which just stores all the values locally
+ */
+ public class MockProperties extends SurfaceProperties {
+
+ public float alpha = -1;
+ public Matrix matrix = null;
+ public Rect windowCrop = null;
+ public float cornerRadius = 0;
+ public float shadowRadius = 0;
+
+ protected MockProperties() {
+ super(null);
+ }
+
+ @Override
+ public SurfaceProperties setAlpha(float alpha) {
+ this.alpha = alpha;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ this.matrix = matrix;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ this.windowCrop = windowCrop;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setLayer(int relativeLayer) {
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setCornerRadius(float radius) {
+ this.cornerRadius = radius;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setShadowRadius(float radius) {
+ this.shadowRadius = radius;
+ return this;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 1200208..95473dc 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -25,7 +25,6 @@
import android.view.ViewRootImpl;
import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.function.Consumer;
@@ -70,18 +69,12 @@
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- public void scheduleApply(final SurfaceParams... params) {
+ public void scheduleApply(SurfaceTransaction params) {
View view = mTargetViewRootImpl.getView();
if (view == null) {
return;
}
- Transaction t = new Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SurfaceParams surfaceParams = params[i];
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(t);
- }
- }
+ Transaction t = params.getTransaction();
mLastSequenceNumber++;
final int toApplySeqNo = mLastSequenceNumber;
@@ -102,7 +95,7 @@
}
/**
- * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+ * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
* attached if necessary.
*/
public static void create(
diff --git a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
index 580cc99..3756b4a 100644
--- a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
+++ b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
@@ -26,7 +26,7 @@
public int getPlaceholderIconFadeInStart() { return 167; }
public int getPlaceholderIconFadeInEnd() { return 250; }
public int getStagedRectSlideStart() { return 0; }
- public int getStagedRectSlideEnd() { return 383; }
+ public int getStagedRectSlideEnd() { return 500; }
public int getDuration() { return TABLET_CONFIRM_DURATION; }
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index c03aa3f..5c37da1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -35,6 +35,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
+import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
@@ -46,11 +47,10 @@
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
/**
* A utility class which emulates the layout behavior of TaskView and RecentsView
@@ -171,8 +171,11 @@
/**
* Sets the targets which the simulator will control
*/
- public void setPreview(RemoteAnimationTargetCompat runningTarget) {
- setPreviewBounds(runningTarget.startScreenSpaceBounds, runningTarget.contentInsets);
+ public void setPreview(RemoteAnimationTarget runningTarget) {
+ setPreviewBounds(
+ runningTarget.startBounds == null
+ ? runningTarget.screenSpaceBounds : runningTarget.startBounds,
+ runningTarget.contentInsets);
}
/**
@@ -181,7 +184,7 @@
*
* @param splitInfo set to {@code null} when not in staged split mode
*/
- public void setPreview(RemoteAnimationTargetCompat runningTarget, SplitBounds splitInfo) {
+ public void setPreview(RemoteAnimationTarget runningTarget, SplitBounds splitInfo) {
setPreview(runningTarget);
mSplitBounds = splitInfo;
if (mSplitBounds == null) {
@@ -191,6 +194,7 @@
mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
STAGE_POSITION_TOP_OR_LEFT :
STAGE_POSITION_BOTTOM_OR_RIGHT;
+ mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition);
}
/**
@@ -386,10 +390,10 @@
@Override
public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpCropRect)
- .withCornerRadius(getCurrentCornerRadius());
+ SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mTmpCropRect)
+ .setCornerRadius(getCurrentCornerRadius());
// If mDrawsBelowRecents is unset, no reordering will be enforced.
if (mDrawsBelowRecents != null) {
@@ -398,7 +402,7 @@
// conflict with layers that WM core positions (ie. the input consumers). For shell
// transitions, the animation leashes are reparented to an animation container so we
// can bump layers as needed.
- builder.withLayer(mDrawsBelowRecents
+ builder.setLayer(mDrawsBelowRecents
? Integer.MIN_VALUE + 1
: ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
}
@@ -418,4 +422,15 @@
return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
}
+ /**
+ * TODO(b/254378592): Remove this after consolidation of classes
+ */
+ public static com.android.wm.shell.util.SplitBounds convertSplitBounds(SplitBounds bounds) {
+ return new com.android.wm.shell.util.SplitBounds(
+ bounds.leftTopBounds,
+ bounds.rightBottomBounds,
+ bounds.leftTopTaskId,
+ bounds.rightBottomTaskId
+ );
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index a7f25d4..aa9a45b 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -15,16 +15,18 @@
*/
package com.android.quickstep.util;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
import android.util.FloatProperty;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.RemoteAnimationTargets;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.TransactionCompat;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
public class TransformParams {
@@ -113,8 +115,7 @@
* Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
* are computed based on these TransformParams.
*/
- public TransformParams setSyncTransactionApplier(
- SurfaceTransactionApplier applier) {
+ public TransformParams setSyncTransactionApplier(SurfaceTransactionApplier applier) {
mSyncTransactionApplier = applier;
return this;
}
@@ -137,28 +138,26 @@
return this;
}
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
- final int appLength = targets.unfilteredApps.length;
- final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
- SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
+ SurfaceTransaction transaction = new SurfaceTransaction();
mRecentsSurface = getRecentsSurface(targets);
- for (int i = 0; i < appLength; i++) {
- RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
+ for (int i = 0; i < targets.unfilteredApps.length; i++) {
+ RemoteAnimationTarget app = targets.unfilteredApps[i];
+ SurfaceProperties builder = transaction.forSurface(app.leash);
if (app.mode == targets.targetMode) {
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ int activityType = app.windowConfiguration.getActivityType();
+ if (activityType == ACTIVITY_TYPE_HOME) {
mHomeBuilderProxy.onBuildTargetParams(builder, app, this);
} else {
// Fade out Assistant overlay.
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
- && app.isNotInRecents) {
+ if (activityType == ACTIVITY_TYPE_ASSISTANT && app.isNotInRecents) {
float progress = Utilities.boundToRange(getProgress(), 0, 1);
- builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+ builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
} else {
- builder.withAlpha(getTargetAlpha());
+ builder.setAlpha(getTargetAlpha());
}
proxy.onBuildTargetParams(builder, app, this);
@@ -166,22 +165,22 @@
} else {
mBaseBuilderProxy.onBuildTargetParams(builder, app, this);
}
- surfaceParams[i] = builder.build();
}
+
// always put wallpaper layer to bottom.
+ final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
for (int i = 0; i < wallpaperLength; i++) {
- RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
- surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
- .withLayer(Integer.MIN_VALUE).build();
+ RemoteAnimationTarget wallpaper = targets.wallpapers[i];
+ transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
}
- return surfaceParams;
+ return transaction;
}
private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
for (int i = 0; i < targets.unfilteredApps.length; i++) {
- RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+ RemoteAnimationTarget app = targets.unfilteredApps[i];
if (app.mode == targets.targetMode) {
- if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
+ if (app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS) {
return app.leash;
}
} else {
@@ -213,15 +212,11 @@
return mTargetSet;
}
- public void applySurfaceParams(SurfaceParams... params) {
+ public void applySurfaceParams(SurfaceTransaction builder) {
if (mSyncTransactionApplier != null) {
- mSyncTransactionApplier.scheduleApply(params);
+ mSyncTransactionApplier.scheduleApply(builder);
} else {
- TransactionCompat t = new TransactionCompat();
- for (SurfaceParams param : params) {
- SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
- }
- t.apply();
+ builder.getTransaction().apply();
}
}
@@ -229,9 +224,9 @@
public interface BuilderProxy {
BuilderProxy NO_OP = (builder, app, params) -> { };
- BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
+ BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
- void onBuildTargetParams(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, TransformParams params);
+ void onBuildTargetParams(SurfaceProperties builder,
+ RemoteAnimationTarget app, TransformParams params);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java
deleted file mode 100644
index 6171c5d..0000000
--- a/quickstep/src/com/android/quickstep/util/ViewCapture.java
+++ /dev/null
@@ -1,526 +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.quickstep.util;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-
-import static java.util.stream.Collectors.toList;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.Trace;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Base64OutputStream;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnDrawListener;
-import android.view.Window;
-
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.view.ViewCaptureData.ExportedData;
-import com.android.launcher3.view.ViewCaptureData.FrameData;
-import com.android.launcher3.view.ViewCaptureData.ViewNode;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Future;
-import java.util.zip.GZIPOutputStream;
-
-/**
- * Utility class for capturing view data every frame
- */
-public class ViewCapture {
-
- private static final String TAG = "ViewCapture";
-
- // These flags are copies of two private flags in the View class.
- private static final int PFLAG_INVALIDATED = 0x80000000;
- private static final int PFLAG_DIRTY_MASK = 0x00200000;
-
- // Number of frames to keep in memory
- private static final int MEMORY_SIZE = 2000;
- // Initial size of the reference pool. This is at least be 5 * total number of views in
- // Launcher. This allows the first free frames avoid object allocation during view capture.
- private static final int INIT_POOL_SIZE = 300;
-
- public static final MainThreadInitializedObject<ViewCapture> INSTANCE =
- new MainThreadInitializedObject<>(ViewCapture::new);
-
- private final List<WindowListener> mListeners = new ArrayList<>();
-
- private final Context mContext;
- private final LooperExecutor mExecutor;
-
- // Pool used for capturing view tree on the UI thread.
- private ViewRef mPool = new ViewRef();
-
- private ViewCapture(Context context) {
- mContext = context;
- if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- Looper looper = createAndStartNewLooper("ViewCapture",
- Process.THREAD_PRIORITY_FOREGROUND);
- mExecutor = new LooperExecutor(looper);
- mExecutor.execute(this::initPool);
- } else {
- mExecutor = UI_HELPER_EXECUTOR;
- }
- }
-
- @UiThread
- private void addToPool(ViewRef start, ViewRef end) {
- end.next = mPool;
- mPool = start;
- }
-
- @WorkerThread
- private void initPool() {
- ViewRef start = new ViewRef();
- ViewRef current = start;
-
- for (int i = 0; i < INIT_POOL_SIZE; i++) {
- current.next = new ViewRef();
- current = current.next;
- }
-
- ViewRef finalCurrent = current;
- MAIN_EXECUTOR.execute(() -> addToPool(start, finalCurrent));
- }
-
- /**
- * Attaches the ViewCapture to the provided window and returns a handle to detach the listener
- */
- public SafeCloseable startCapture(Window window) {
- String title = window.getAttributes().getTitle().toString();
- String name = TextUtils.isEmpty(title) ? window.toString() : title;
- return startCapture(window.getDecorView(), name);
- }
-
- /**
- * Attaches the ViewCapture to the provided window and returns a handle to detach the listener
- */
- public SafeCloseable startCapture(View view, String name) {
- if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- return () -> { };
- }
-
- WindowListener listener = new WindowListener(view, name);
- mExecutor.execute(() -> MAIN_EXECUTOR.execute(listener::attachToRoot));
- mListeners.add(listener);
- return () -> {
- mListeners.remove(listener);
- listener.destroy();
- };
- }
-
- /**
- * Dumps all the active view captures
- */
- public void dump(PrintWriter writer, FileDescriptor out) {
- if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
- return;
- }
- ViewIdProvider idProvider = new ViewIdProvider(mContext.getResources());
-
- // Collect all the tasks first so that all the tasks are posted on the executor
- List<Pair<String, Future<ExportedData>>> tasks = mListeners.stream()
- .map(l -> Pair.create(l.name, mExecutor.submit(() -> l.dumpToProto(idProvider))))
- .collect(toList());
-
- tasks.forEach(pair -> {
- writer.println();
- writer.println(" ContinuousViewCapture:");
- writer.println(" window " + pair.first + ":");
- writer.println(" pkg:" + mContext.getPackageName());
- writer.print(" data:");
- writer.flush();
- try (OutputStream os = new FileOutputStream(out)) {
- ExportedData data = pair.second.get();
- OutputStream encodedOS = new GZIPOutputStream(new Base64OutputStream(os,
- Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP));
- data.writeTo(encodedOS);
- encodedOS.close();
- os.flush();
- } catch (Exception e) {
- Log.e(TAG, "Error capturing proto", e);
- }
- writer.println();
- writer.println("--end--");
- });
- }
-
- private class WindowListener implements OnDrawListener {
-
- private final View mRoot;
- public final String name;
-
- private final Handler mHandler;
- private final ViewRef mViewRef = new ViewRef();
-
- private int mFrameIndexBg = -1;
- private boolean mIsFirstFrame = true;
- private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
- private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
-
- private boolean mDestroyed = false;
-
- WindowListener(View view, String name) {
- mRoot = view;
- this.name = name;
- mHandler = new Handler(mExecutor.getLooper(), this::captureViewPropertiesBg);
- }
-
- @Override
- public void onDraw() {
- Trace.beginSection("view_capture");
- captureViewTree(mRoot, mViewRef);
- Message m = Message.obtain(mHandler);
- m.obj = mViewRef.next;
- mHandler.sendMessage(m);
- mIsFirstFrame = false;
- Trace.endSection();
- }
-
- /**
- * Captures the View property on the background thread, and transfer all the ViewRef objects
- * back to the pool
- */
- @WorkerThread
- private boolean captureViewPropertiesBg(Message msg) {
- ViewRef viewRefStart = (ViewRef) msg.obj;
- long time = msg.getWhen();
- if (viewRefStart == null) {
- return false;
- }
- mFrameIndexBg++;
- if (mFrameIndexBg >= MEMORY_SIZE) {
- mFrameIndexBg = 0;
- }
- mFrameTimesBg[mFrameIndexBg] = time;
-
- ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
-
- ViewPropertyRef resultStart = null;
- ViewPropertyRef resultEnd = null;
-
- ViewRef viewRefEnd = viewRefStart;
- while (viewRefEnd != null) {
- ViewPropertyRef propertyRef = recycle;
- if (propertyRef == null) {
- propertyRef = new ViewPropertyRef();
- } else {
- recycle = recycle.next;
- propertyRef.next = null;
- }
-
- ViewPropertyRef copy = null;
- if (viewRefEnd.childCount < 0) {
- copy = findInLastFrame(viewRefEnd.view.hashCode());
- viewRefEnd.childCount = (copy != null) ? copy.childCount : 0;
- }
- viewRefEnd.transferTo(propertyRef);
-
- if (resultStart == null) {
- resultStart = propertyRef;
- resultEnd = resultStart;
- } else {
- resultEnd.next = propertyRef;
- resultEnd = resultEnd.next;
- }
-
- if (copy != null) {
- int pending = copy.childCount;
- while (pending > 0) {
- copy = copy.next;
- pending = pending - 1 + copy.childCount;
-
- propertyRef = recycle;
- if (propertyRef == null) {
- propertyRef = new ViewPropertyRef();
- } else {
- recycle = recycle.next;
- propertyRef.next = null;
- }
-
- copy.transferTo(propertyRef);
-
- resultEnd.next = propertyRef;
- resultEnd = resultEnd.next;
- }
- }
-
- if (viewRefEnd.next == null) {
- // The compiler will complain about using a non-final variable from
- // an outer class in a lambda if we pass in viewRefEnd directly.
- final ViewRef finalViewRefEnd = viewRefEnd;
- MAIN_EXECUTOR.execute(() -> addToPool(viewRefStart, finalViewRefEnd));
- break;
- }
- viewRefEnd = viewRefEnd.next;
- }
- mNodesBg[mFrameIndexBg] = resultStart;
- return true;
- }
-
- private ViewPropertyRef findInLastFrame(int hashCode) {
- int lastFrameIndex = (mFrameIndexBg == 0) ? MEMORY_SIZE - 1 : mFrameIndexBg - 1;
- ViewPropertyRef viewPropertyRef = mNodesBg[lastFrameIndex];
- while (viewPropertyRef != null && viewPropertyRef.hashCode != hashCode) {
- viewPropertyRef = viewPropertyRef.next;
- }
- return viewPropertyRef;
- }
-
- void attachToRoot() {
- if (mRoot.isAttachedToWindow()) {
- mRoot.getViewTreeObserver().addOnDrawListener(this);
- } else {
- mRoot.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- if (!mDestroyed) {
- mRoot.getViewTreeObserver().addOnDrawListener(WindowListener.this);
- }
- mRoot.removeOnAttachStateChangeListener(this);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) { }
- });
- }
- }
-
- void destroy() {
- mRoot.getViewTreeObserver().removeOnDrawListener(this);
- mDestroyed = true;
- }
-
- @WorkerThread
- private ExportedData dumpToProto(ViewIdProvider idProvider) {
- ExportedData.Builder dataBuilder = ExportedData.newBuilder();
- ArrayList<Class> classList = new ArrayList<>();
-
- int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
- for (int i = size - 1; i >= 0; i--) {
- int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
- ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
- mNodesBg[index].toProto(idProvider, classList, nodeBuilder);
- dataBuilder.addFrameData(FrameData.newBuilder()
- .setNode(nodeBuilder)
- .setTimestamp(mFrameTimesBg[index]));
- }
- return dataBuilder
- .addAllClassname(classList.stream().map(Class::getName).collect(toList()))
- .build();
- }
-
- private ViewRef captureViewTree(View view, ViewRef start) {
- ViewRef ref;
- if (mPool != null) {
- ref = mPool;
- mPool = mPool.next;
- ref.next = null;
- } else {
- ref = new ViewRef();
- }
- ref.view = view;
- start.next = ref;
- if (view instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) view;
- // If a view has not changed since the last frame, we will copy
- // its children from the last processed frame's data.
- if ((view.mPrivateFlags & (PFLAG_INVALIDATED | PFLAG_DIRTY_MASK)) == 0
- && !mIsFirstFrame) {
- // A negative child count is the signal to copy this view from the last frame.
- ref.childCount = -parent.getChildCount();
- return ref;
- }
- ViewRef result = ref;
- int childCount = ref.childCount = parent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- result = captureViewTree(parent.getChildAt(i), result);
- }
- return result;
- } else {
- ref.childCount = 0;
- return ref;
- }
- }
- }
-
- private static class ViewPropertyRef {
- // We store reference in memory to avoid generating and storing too many strings
- public Class clazz;
- public int hashCode;
- public int childCount = 0;
-
- public int id;
- public int left, top, right, bottom;
- public int scrollX, scrollY;
-
- public float translateX, translateY;
- public float scaleX, scaleY;
- public float alpha;
- public float elevation;
-
- public int visibility;
- public boolean willNotDraw;
- public boolean clipChildren;
-
- public ViewPropertyRef next;
-
- public void transferTo(ViewPropertyRef out) {
- out.clazz = this.clazz;
- out.hashCode = this.hashCode;
- out.childCount = this.childCount;
- out.id = this.id;
- out.left = this.left;
- out.top = this.top;
- out.right = this.right;
- out.bottom = this.bottom;
- out.scrollX = this.scrollX;
- out.scrollY = this.scrollY;
- out.scaleX = this.scaleX;
- out.scaleY = this.scaleY;
- out.translateX = this.translateX;
- out.translateY = this.translateY;
- out.alpha = this.alpha;
- out.visibility = this.visibility;
- out.willNotDraw = this.willNotDraw;
- out.clipChildren = this.clipChildren;
- out.elevation = this.elevation;
- }
-
- /**
- * Converts the data to the proto representation and returns the next property ref
- * at the end of the iteration.
- * @return
- */
- public ViewPropertyRef toProto(ViewIdProvider idProvider, ArrayList<Class> classList,
- ViewNode.Builder outBuilder) {
- int classnameIndex = classList.indexOf(clazz);
- if (classnameIndex < 0) {
- classnameIndex = classList.size();
- classList.add(clazz);
- }
- outBuilder
- .setClassnameIndex(classnameIndex)
- .setHashcode(hashCode)
- .setId(idProvider.getName(id))
- .setLeft(left)
- .setTop(top)
- .setWidth(right - left)
- .setHeight(bottom - top)
- .setTranslationX(translateX)
- .setTranslationY(translateY)
- .setScaleX(scaleX)
- .setScaleY(scaleY)
- .setAlpha(alpha)
- .setVisibility(visibility)
- .setWillNotDraw(willNotDraw)
- .setElevation(elevation)
- .setClipChildren(clipChildren);
-
- ViewPropertyRef result = next;
- for (int i = 0; (i < childCount) && (result != null); i++) {
- ViewNode.Builder childBuilder = ViewNode.newBuilder();
- result = result.toProto(idProvider, classList, childBuilder);
- outBuilder.addChildren(childBuilder);
- }
- return result;
- }
- }
-
- private static class ViewRef {
- public View view;
- public int childCount = 0;
- public ViewRef next;
-
- public void transferTo(ViewPropertyRef out) {
- out.childCount = this.childCount;
-
- View view = this.view;
- this.view = null;
-
- out.clazz = view.getClass();
- out.hashCode = view.hashCode();
- out.id = view.getId();
- out.left = view.getLeft();
- out.top = view.getTop();
- out.right = view.getRight();
- out.bottom = view.getBottom();
- out.scrollX = view.getScrollX();
- out.scrollY = view.getScrollY();
-
- out.translateX = view.getTranslationX();
- out.translateY = view.getTranslationY();
- out.scaleX = view.getScaleX();
- out.scaleY = view.getScaleY();
- out.alpha = view.getAlpha();
- out.elevation = view.getElevation();
-
- out.visibility = view.getVisibility();
- out.willNotDraw = view.willNotDraw();
- }
- }
-
- private static final class ViewIdProvider {
-
- private final SparseArray<String> mNames = new SparseArray<>();
- private final Resources mRes;
-
- ViewIdProvider(Resources res) {
- mRes = res;
- }
-
- String getName(int id) {
- String name = mNames.get(id);
- if (name == null) {
- if (id >= 0) {
- try {
- name = mRes.getResourceTypeName(id) + '/' + mRes.getResourceEntryName(id);
- } catch (Resources.NotFoundException e) {
- name = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
- }
- } else {
- name = "NO_ID";
- }
- mNames.put(id, name);
- }
- return name;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 50be5ea..d098ffc 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -99,6 +99,10 @@
return false;
}
+ public float getScrollAlpha() {
+ return mScrollAlpha;
+ }
+
public void setContentAlpha(float alpha) {
if (mContentAlpha != alpha) {
mContentAlpha = alpha;
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 9874f96..2abd715 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -284,19 +284,22 @@
return false;
}
+ @Override
+ public RunnableList launchTasks() {
+ SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+ getRecentsView().startHome();
+ return new RunnableList();
+ }
+
@Nullable
@Override
public RunnableList launchTaskAnimated() {
- RunnableList endCallback = new RunnableList();
- SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
- RecentsView<?, ?> recentsView = getRecentsView();
- recentsView.addSideTaskLaunchCallback(endCallback);
- return endCallback;
+ return launchTasks();
}
@Override
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
- SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+ launchTasks();
callback.accept(true);
}
@@ -434,7 +437,7 @@
}
@Override
- protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
// no-op
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index dc1ae52..1d421b2 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -11,6 +11,7 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -74,6 +75,7 @@
}
};
+ private int mSplitHolderSize;
private FloatingTaskThumbnailView mThumbnailView;
private SplitPlaceholderView mSplitPlaceholderView;
private RectF mStartingPosition;
@@ -97,6 +99,9 @@
mActivity = BaseActivity.fromContext(context);
mIsRtl = Utilities.isRtl(getResources());
mFullscreenParams = new FullscreenDrawParams(context);
+
+ mSplitHolderSize = context.getResources().getDimensionPixelSize(
+ R.dimen.split_placeholder_icon_size);
}
@Override
@@ -126,8 +131,7 @@
RecentsView recentsView = launcher.getOverviewPanel();
mOrientationHandler = recentsView.getPagedOrientationHandler();
mStagePosition = recentsView.getSplitSelectController().getActiveSplitStagePosition();
- mSplitPlaceholderView.setIcon(icon,
- mContext.getResources().getDimensionPixelSize(R.dimen.split_placeholder_icon_size));
+ mSplitPlaceholderView.setIcon(icon, mSplitHolderSize);
mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
}
@@ -193,6 +197,10 @@
mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
}
+ public void setIcon(Bitmap icon) {
+ mSplitPlaceholderView.setIcon(new BitmapDrawable(icon), mSplitHolderSize);
+ }
+
protected void initPosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
mStartingPosition.set(pos);
lp.ignoreInsets = true;
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 8a5f42a..6431bdf 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -25,6 +25,7 @@
import android.util.AttributeSet;
import android.util.Size;
import android.view.GhostView;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -41,7 +42,6 @@
import com.android.launcher3.views.ListenerView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.RoundedCornerEnforcement;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/** A view that mimics an App Widget through a launch animation. */
@TargetApi(Build.VERSION_CODES.S)
@@ -304,7 +304,7 @@
* context's theme background color.
*/
public static int getDefaultBackgroundColor(
- Context context, RemoteAnimationTargetCompat target) {
+ Context context, RemoteAnimationTarget target) {
return (target != null && target.taskInfo.taskDescription != null)
? target.taskInfo.taskDescription.getBackgroundColor()
: Themes.getColorBackground(context);
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 71b0c60..2cada0a 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,6 +1,5 @@
package com.android.quickstep.views;
-import static com.android.launcher3.AbstractFloatingView.getAnyView;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -14,11 +13,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.quickstep.RecentsModel;
@@ -26,8 +25,10 @@
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.HashMap;
@@ -86,9 +87,19 @@
mTaskIdContainer[1] = secondary.key.id;
mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT);
- mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
+ mTaskIdAttributeContainer[0].setStagePosition(
+ SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT);
mSnapshotView2.bind(secondary);
mSplitBoundsConfig = splitBoundsConfig;
+ if (mSplitBoundsConfig == null) {
+ return;
+ }
+ mSnapshotView.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+ .convertSplitBounds(splitBoundsConfig),
+ PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT);
+ mSnapshotView2.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+ .convertSplitBounds(splitBoundsConfig),
+ PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT);
}
@Override
@@ -164,21 +175,6 @@
}
}
- @Override
- protected boolean showTaskMenuWithContainer(IconView iconView) {
- boolean showedTaskMenu = super.showTaskMenuWithContainer(iconView);
- if (iconView == mIconView2 && showedTaskMenu && !mActivity.getDeviceProfile().isTablet) {
- // Adjust the position of the secondary task's menu view (only on phones)
- TaskMenuView taskMenuView = getAnyView(mActivity, AbstractFloatingView.TYPE_TASK_MENU);
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- getRecentsView().getPagedOrientationHandler()
- .setSecondaryTaskMenuPosition(mSplitBoundsConfig, this,
- deviceProfile, mTaskIdAttributeContainer[0].getThumbnailView(),
- taskMenuView);
- }
- return showedTaskMenu;
- }
-
@Nullable
@Override
public RunnableList launchTaskAnimated() {
@@ -207,7 +203,8 @@
@Override
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
getRecentsView().getSplitSelectController().launchTasks(mTask.key.id, mSecondaryTask.key.id,
- STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, getSplitRatio());
+ SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+ getSplitRatio());
}
@Override
@@ -319,8 +316,8 @@
}
@Override
- protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
- super.setIconAndDimTransitionProgress(progress, invert);
+ protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
+ super.setIconsAndBannersTransitionProgress(progress, invert);
// Value set by super call
float scale = mIconView.getAlpha();
mIconView2.setAlpha(scale);
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 2ae7136..6c27587 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -147,6 +147,9 @@
& CLEAR_ALL_BUTTON) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
}
+ if (mActivity.getDesktopVisibilityController() != null) {
+ mActivity.getDesktopVisibilityController().setOverviewStateEnabled(enabled);
+ }
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 514d5b9..eeabdc8 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -33,8 +33,8 @@
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
import com.android.quickstep.util.LayoutUtils;
@@ -82,6 +82,8 @@
private static final int INDEX_FULLSCREEN_ALPHA = 2;
private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
private static final int INDEX_SHARE_TARGET_ALPHA = 4;
+ private static final int INDEX_SCROLL_ALPHA = 5;
+ private static final int NUM_ALPHAS = 6;
public @interface SplitButtonHiddenFlags { }
public static final int FLAG_IS_NOT_TABLET = 1 << 0;
@@ -126,11 +128,10 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), 5);
+ mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS);
mMultiValueAlpha.setUpdateVisibility(true);
findViewById(R.id.action_screenshot).setOnClickListener(this);
-
mSplitButton = findViewById(R.id.action_split);
mSplitButton.setOnClickListener(this);
}
@@ -177,7 +178,7 @@
mHiddenFlags &= ~visibilityFlags;
}
boolean isHidden = mHiddenFlags != 0;
- mMultiValueAlpha.getProperty(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
+ mMultiValueAlpha.get(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
}
/**
@@ -232,20 +233,24 @@
updateSplitButtonEnabledState();
}
- public AlphaProperty getContentAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_CONTENT_ALPHA);
+ public MultiProperty getContentAlpha() {
+ return mMultiValueAlpha.get(INDEX_CONTENT_ALPHA);
}
- public AlphaProperty getVisibilityAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_VISIBILITY_ALPHA);
+ public MultiProperty getVisibilityAlpha() {
+ return mMultiValueAlpha.get(INDEX_VISIBILITY_ALPHA);
}
- public AlphaProperty getFullscreenAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
+ public MultiProperty getFullscreenAlpha() {
+ return mMultiValueAlpha.get(INDEX_FULLSCREEN_ALPHA);
}
- public AlphaProperty getShareTargetAlpha() {
- return mMultiValueAlpha.getProperty(INDEX_SHARE_TARGET_ALPHA);
+ public MultiProperty getShareTargetAlpha() {
+ return mMultiValueAlpha.get(INDEX_SHARE_TARGET_ALPHA);
+ }
+
+ public MultiProperty getIndexScrollAlpha() {
+ return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA);
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 6d95e7d..8be544e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -45,10 +45,10 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
-import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
@@ -104,6 +104,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -146,7 +147,6 @@
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
@@ -180,6 +180,7 @@
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TaskVisualsChangeListener;
@@ -191,8 +192,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -1064,8 +1063,8 @@
}
}
- public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpaper, RemoteAnimationTargetCompat[] nonApps) {
+ public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpaper, RemoteAnimationTarget[] nonApps) {
AnimatorSet anim = new AnimatorSet();
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView == null || !isTaskViewVisible(taskView)) {
@@ -1077,14 +1076,15 @@
appAnimator.setInterpolator(ACCEL_DEACCEL);
appAnimator.addUpdateListener(valueAnimator -> {
float percent = valueAnimator.getAnimatedFraction();
- SurfaceParams.Builder builder = new SurfaceParams.Builder(
- apps[apps.length - 1].leash);
+ SurfaceTransaction transaction = new SurfaceTransaction();
Matrix matrix = new Matrix();
matrix.postScale(percent, percent);
matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
- builder.withAlpha(percent).withMatrix(matrix);
- surfaceApplier.scheduleApply(builder.build());
+ transaction.forSurface(apps[apps.length - 1].leash)
+ .setAlpha(percent)
+ .setMatrix(matrix);
+ surfaceApplier.scheduleApply(transaction);
});
anim.play(appAnimator);
anim.addListener(new AnimatorListenerAdapter() {
@@ -1245,6 +1245,8 @@
if (!mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
}
+ InteractionJankMonitorWrapper.begin(/* view= */ this,
+ InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
}
@Override
@@ -1258,6 +1260,7 @@
if (getNextPage() > 0) {
setSwipeDownShouldLaunchApp(true);
}
+ InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
}
@Override
@@ -1893,7 +1896,7 @@
private void animateActionsViewAlpha(float alphaValue, long duration) {
mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(
- mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, alphaValue);
+ mActionsView.getVisibilityAlpha(), MULTI_PROPERTY_VALUE, alphaValue);
mActionsViewAlphaAnimatorFinalValue = alphaValue;
mActionsViewAlphaAnimator.setDuration(duration);
// Set autocancel to prevent race-conditiony setting of alpha from other animations
@@ -1910,6 +1913,9 @@
}
int scroll = mOrientationHandler.getPrimaryScroll(this);
mClearAllButton.onRecentsViewScroll(scroll, mOverviewGridEnabled);
+
+ // Clear all button alpha was set by the previous line.
+ mActionsView.getIndexScrollAlpha().setValue(1 - mClearAllButton.getScrollAlpha());
}
@Override
@@ -4181,7 +4187,7 @@
public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent) {
mSplitHiddenTaskView = taskView;
- mSplitSelectStateController.setInitialTaskSelect(taskView.getTask().key.id,
+ mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
stagePosition, splitEvent, taskView.getItemInfo());
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
@@ -4550,8 +4556,8 @@
DepthController depthController = getDepthController();
if (depthController != null) {
- ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, STATE_DEPTH,
- BACKGROUND_APP.getDepth(mActivity));
+ ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController.stateDepth,
+ MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mActivity));
anim.play(depthAnimator);
}
anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0f, 1f));
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 6815745..2c9afb4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -23,7 +23,6 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Outline;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
@@ -32,7 +31,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -56,13 +54,12 @@
/**
* Contains options for a recent task when long-pressing its icon.
*/
-public class TaskMenuView extends AbstractFloatingView implements OnScrollChangedListener {
+public class TaskMenuView extends AbstractFloatingView {
private static final Rect sTempRect = new Rect();
private static final int REVEAL_OPEN_DURATION = 150;
private static final int REVEAL_CLOSE_DURATION = 100;
- private final float mTaskInsetMargin;
private BaseDraggingActivity mActivity;
private TextView mTaskName;
@@ -81,7 +78,6 @@
mActivity = BaseDraggingActivity.fromContext(context);
setClipToOutline(true);
- mTaskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
}
@Override
@@ -129,33 +125,6 @@
};
}
- private void setPosition(float x, float y, int overscrollShift) {
- PagedOrientationHandler pagedOrientationHandler = mTaskView.getPagedOrientationHandler();
- // Inset due to margin
- PointF additionalInset = pagedOrientationHandler
- .getAdditionalInsetForTaskMenu(mTaskInsetMargin);
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- int taskTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-
- float adjustedY = y + taskTopMargin - additionalInset.y;
- float adjustedX = x - additionalInset.x;
- // Changing pivot to make computations easier
- // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
- // which would render the X and Y position set here incorrect
- setPivotX(0);
- if (deviceProfile.isTablet) {
- // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
- setPivotY(-taskTopMargin);
- } else {
- setPivotY(0);
- }
- setRotation(pagedOrientationHandler.getDegreesRotated());
- setX(pagedOrientationHandler.getTaskMenuX(adjustedX,
- mTaskContainer.getThumbnailView(), overscrollShift, deviceProfile));
- setY(pagedOrientationHandler.getTaskMenuY(
- adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));
- }
-
public void onRotationChanged() {
if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
mOpenCloseAnimator.end();
@@ -187,17 +156,9 @@
return false;
}
post(this::animateOpen);
- ((RecentsView) mActivity.getOverviewPanel()).addOnScrollChangedListener(this);
return true;
}
- @Override
- public void onScrollChanged() {
- RecentsView rv = mActivity.getOverviewPanel();
- setPosition(mTaskView.getX() - rv.getScrollX(), mTaskView.getY() - rv.getScrollY(),
- rv.getOverScrollShift());
- }
-
/** @return true if successfully able to populate task view menu, false otherwise */
private boolean populateAndLayoutMenu() {
if (mTaskContainer.getTask().icon == null) {
@@ -234,18 +195,18 @@
RecentsView recentsView = mActivity.getOverviewPanel();
PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- orientationHandler.setTaskMenuAroundTaskView(this, mTaskInsetMargin);
// Get Position
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- mActivity.getDragLayer().getDescendantRectRelativeToSelf(mTaskView, sTempRect);
+ mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskContainer.getThumbnailView(),
+ sTempRect);
Rect insets = mActivity.getDragLayer().getInsets();
BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
int padding = getResources()
.getDimensionPixelSize(R.dimen.task_menu_vertical_padding);
params.width = orientationHandler
.getTaskMenuWidth(taskContainer.getThumbnailView(),
- deviceProfile) - (2 * padding);
+ deviceProfile, taskContainer.getStagePosition()) - (2 * padding);
// Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
params.gravity = Gravity.LEFT;
setLayoutParams(params);
@@ -260,7 +221,22 @@
orientationHandler.setTaskOptionsMenuLayoutOrientation(
deviceProfile, mOptionLayout, dividerSpacing, divider);
- setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, 0);
+ float thumbnailAlignedX = sTempRect.left - insets.left;
+ float thumbnailAlignedY = sTempRect.top - insets.top;
+ // Changing pivot to make computations easier
+ // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
+ // which would render the X and Y position set here incorrect
+ setPivotX(0);
+ setPivotY(0);
+ setRotation(orientationHandler.getDegreesRotated());
+
+ // Margin that insets the menuView inside the taskView
+ float taskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
+ setTranslationX(orientationHandler.getTaskMenuX(thumbnailAlignedX,
+ mTaskContainer.getThumbnailView(), deviceProfile, taskInsetMargin));
+ setTranslationY(orientationHandler.getTaskMenuY(
+ thumbnailAlignedY, mTaskContainer.getThumbnailView(),
+ mTaskContainer.getStagePosition(), this, taskInsetMargin));
}
private void animateOpen() {
@@ -306,7 +282,6 @@
private void closeComplete() {
mIsOpen = false;
mActivity.getDragLayer().removeView(this);
- ((RecentsView) mActivity.getOverviewPanel()).removeOnScrollChangedListener(this);
}
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 35f0f5d..527a0d1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -19,6 +19,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -53,6 +54,7 @@
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
@@ -78,6 +80,7 @@
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
@@ -101,7 +104,6 @@
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.annotation.Retention;
import java.util.Arrays;
@@ -171,7 +173,7 @@
new FloatProperty<TaskView>("focusTransition") {
@Override
public void setValue(TaskView taskView, float v) {
- taskView.setIconAndDimTransitionProgress(v, false /* invert */);
+ taskView.setIconsAndBannersTransitionProgress(v, false /* invert */);
}
@Override
@@ -739,14 +741,14 @@
} else {
TransformParams topLeftParams = remoteTargetHandles[0].getTransformParams();
TransformParams rightBottomParams = remoteTargetHandles[1].getTransformParams();
- RemoteAnimationTargetCompat[] apps = Stream.concat(
+ RemoteAnimationTarget[] apps = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().apps),
Arrays.stream(rightBottomParams.getTargetSet().apps))
- .toArray(RemoteAnimationTargetCompat[]::new);
- RemoteAnimationTargetCompat[] wallpapers = Stream.concat(
+ .toArray(RemoteAnimationTarget[]::new);
+ RemoteAnimationTarget[] wallpapers = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().wallpapers),
Arrays.stream(rightBottomParams.getTargetSet().wallpapers))
- .toArray(RemoteAnimationTargetCompat[]::new);
+ .toArray(RemoteAnimationTarget[]::new);
targets = new RemoteAnimationTargets(apps, wallpapers,
topLeftParams.getTargetSet().nonApps,
topLeftParams.getTargetSet().targetMode);
@@ -953,7 +955,11 @@
return deviceProfile.isTablet && !isFocusedTask();
}
- protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ /**
+ * Called to animate a smooth transition when going directly from an app into Overview (and
+ * vice versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
+ */
+ protected void setIconsAndBannersTransitionProgress(float progress, boolean invert) {
if (invert) {
progress = 1 - progress;
}
@@ -997,7 +1003,7 @@
if (mIconAndDimAnimator != null) {
mIconAndDimAnimator.cancel();
}
- setIconAndDimTransitionProgress(iconScale, invert);
+ setIconsAndBannersTransitionProgress(iconScale, invert);
}
protected void resetPersistentViewTransforms() {
@@ -1417,6 +1423,12 @@
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
mSnapshotView.getTaskOverlay().setFullscreenProgress(progress);
+ // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
+ // oversized and banner would look disproportionately large.
+ if (mActivity.getStateManager().getState() != BACKGROUND_APP) {
+ setIconsAndBannersTransitionProgress(progress, true);
+ }
+
updateSnapshotRadius();
}
@@ -1574,9 +1586,12 @@
/** The current scale we apply to the thumbnail to adjust for new left/right insets. */
public float mScale = 1;
+ private boolean mIsTaskbarTransient;
+
public FullscreenDrawParams(Context context) {
mCornerRadius = TaskCornerRadius.get(context);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
+ mIsTaskbarTransient = DisplayController.isTransientTaskbar(context);
mCurrentDrawnCornerRadius = mCornerRadius;
}
@@ -1586,7 +1601,7 @@
*/
public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale,
int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) {
- RectF insets = getInsetsToDrawInFullscreen(pph, dp);
+ RectF insets = getInsetsToDrawInFullscreen(pph, dp, mIsTaskbarTransient);
float currentInsetsLeft = insets.left * fullscreenProgress;
float currentInsetsTop = insets.top * fullscreenProgress;
@@ -1609,7 +1624,11 @@
/**
* Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
*/
- private static RectF getInsetsToDrawInFullscreen(PreviewPositionHelper pph, DeviceProfile dp) {
+ private static RectF getInsetsToDrawInFullscreen(PreviewPositionHelper pph,
+ DeviceProfile dp, boolean isTaskbarTransient) {
+ if (isTaskbarTransient) {
+ return pph.getClippedInsets();
+ }
return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps
? pph.getClippedInsets() : EMPTY_RECT_F;
}
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index c1b3beb..83341cb 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -36,13 +36,11 @@
import android.content.ComponentName;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -111,7 +109,6 @@
doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
doAnswer(i -> {
String pkg = i.getArgument(0);
- Log.e("Hello", "Getting v " + pkg);
return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream()
.filter(a -> pkg.equals(a.provider.getPackageName()))
.collect(Collectors.toList());
@@ -138,21 +135,21 @@
public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
throws Exception {
// WHEN newPredicationTask is executed with app predication of 5 apps.
- AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
+ AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
mUserHandle);
- AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
+ AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
mUserHandle);
AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
mUserHandle);
- AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "className",
+ AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
mUserHandle);
- AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
+ AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
mUserHandle);
mModelHelper.executeTaskForTest(
newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
.forEach(Runnable::run);
- // THEN only 3 widgets are returned because
+ // THEN only 2 widgets are returned because
// 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
// excluded from the result.
// 2. app3 doesn't have a widget.
@@ -161,45 +158,39 @@
.stream()
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(3);
+ assertThat(recommendedWidgets).hasSize(2);
assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
- assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
}
@Test
- public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder()
+ public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty()
throws Exception {
- if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
- return;
- }
- // WHEN newPredicationTask is executed with 5 predicated widgets.
- AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
- mUserHandle);
- AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2",
+ // Not installed widget
+ AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
mUserHandle);
// Not installed app
AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
mUserHandle);
- // Not installed widget
- AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3",
+ // Workspace added widgets
+ AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
mUserHandle);
AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
mUserHandle);
mModelHelper.executeTaskForTest(
- newWidgetsPredicationTask(List.of(widget5, widget3, widget2, widget4, widget1)))
+ newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)))
.forEach(Runnable::run);
- // THEN only 3 widgets are returned because the launcher only filters out non-exist widgets.
+ // THEN only 2 widgets are returned because the launcher only filters out non-exist widgets.
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
.stream()
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(3);
- assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2);
- assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+ assertThat(recommendedWidgets).hasSize(2);
+ // Another widget from the same package
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
}
private void assertWidgetInfo(
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 190b002..83602be 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -23,8 +23,8 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.ArrayMap;
+import android.view.RemoteAnimationTarget;
import android.view.Surface;
-import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -42,8 +42,7 @@
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
@@ -207,17 +206,21 @@
}
@Override
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
- return new SurfaceParams[] {builder.build()};
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+ proxy.onBuildTargetParams(
+ transaction.mockProperties, mock(RemoteAnimationTarget.class), this);
+ return transaction;
}
@Override
- public void applySurfaceParams(SurfaceParams[] params) {
+ public void applySurfaceParams(SurfaceTransaction params) {
+ Assert.assertTrue(params instanceof RecordingSurfaceTransaction);
+ MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+
// Verify that the task position remains the same
RectF newAppBounds = new RectF(mAppBounds);
- params[0].matrix.mapRect(newAppBounds);
+ p.matrix.mapRect(newAppBounds);
Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
diff --git a/res/color-v31/transient_taskbar_background.xml b/res/color-v31/transient_taskbar_background.xml
new file mode 100644
index 0000000..bce947d
--- /dev/null
+++ b/res/color-v31/transient_taskbar_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="95" />
+</selector>
+
diff --git a/res/drawable/ic_all_apps_button.xml b/res/drawable/ic_all_apps_button.xml
index 5770d3c..7de390a 100644
--- a/res/drawable/ic_all_apps_button.xml
+++ b/res/drawable/ic_all_apps_button.xml
@@ -18,27 +18,29 @@
android:width="80dp"
android:height="80dp"
android:viewportWidth="80"
- android:viewportHeight="80"
- android:theme="@style/AllAppsTheme">
- <path
- android:pathData="M40,0.5L40,0.5c21.8,0 39.5,17.7 39.5,39.5l0,0c0,21.8 -17.7,39.5 -39.5,39.5l0,0C18.2,79.5 0.5,61.8 0.5,40l0,0C0.5,18.2 18.2,0.5 40,0.5z"
- android:fillColor="?attr/allAppsButtonBgColor"/>
- <path
- android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor1"/>
- <path
- android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor2"/>
- <path
- android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor3"/>
- <path
- android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor2"/>
- <path
- android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor4"/>
- <path
- android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
- android:fillColor="?attr/allAppsButtonColor2"/>
+ android:viewportHeight="80">
+ <group
+ android:pivotY="40"
+ android:pivotX="40"
+ android:scaleX=".88"
+ android:scaleY=".88">
+ <path
+ android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_1"/>
+ <path
+ android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_2"/>
+ <path
+ android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_3"/>
+ <path
+ android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_2"/>
+ <path
+ android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_4"/>
+ <path
+ android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+ android:fillColor="@color/all_apps_button_color_2"/>
+ </group>
</vector>
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
new file mode 100644
index 0000000..d5fe51e
--- /dev/null
+++ b/res/layout/page_indicator_dots.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/page_indicator"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/workspace_page_indicator_height"
+ android:layout_gravity="bottom | center_horizontal"
+ android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 9da3e87..b2a3a0d 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -30,6 +30,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
android:paddingBottom="16dp"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:orientation="vertical">
<TextView
@@ -40,7 +41,6 @@
android:textSize="24sp"
android:layout_marginTop="24dp"
android:textColor="?android:attr/textColorSecondary"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:text="@string/widget_button_text"/>
<FrameLayout
@@ -49,7 +49,6 @@
android:layout_height="wrap_content"
android:elevation="0.1dp"
android:background="?android:attr/colorBackground"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:paddingBottom="8dp"
android:clipToPadding="false"
launcher:layout_sticky="true" >
@@ -63,7 +62,6 @@
android:layout_marginTop="8dp"
android:background="@drawable/widgets_recommendation_background"
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:visibility="gone" />
</com.android.launcher3.views.StickyHeaderLayout>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8623414..283c793 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,12 +56,6 @@
<attr name="preloadIconAccentColor" format="color" />
<attr name="preloadIconBackgroundColor" format="color" />
- <attr name="allAppsButtonBgColor" format="color" />
- <attr name="allAppsButtonColor1" format="color" />
- <attr name="allAppsButtonColor2" format="color" />
- <attr name="allAppsButtonColor3" format="color" />
- <attr name="allAppsButtonColor4" format="color" />
-
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
<attr name="layoutHorizontal" format="boolean" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 11b6e8c..d9b3da5 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -205,6 +205,4 @@
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">0</item>
- <string name="floating_task_package" translatable="false"></string>
- <string name="floating_task_action" translatable="false"></string>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a9ba07d..afdb071 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -361,6 +361,13 @@
<dimen name="min_hotseat_icon_space">18dp</dimen>
<dimen name="min_hotseat_qsb_width">0dp</dimen>
<dimen name="taskbar_icon_size">44dp</dimen>
+ <dimen name="transient_taskbar_icon_size">57dp</dimen>
+ <!-- Transient taskbar (placeholders to compile in Launcher3 without Quickstep) -->
+ <dimen name="transient_taskbar_size">0dp</dimen>
+ <dimen name="transient_taskbar_margin">0dp</dimen>
+ <dimen name="transient_taskbar_shadow_blur">0dp</dimen>
+ <dimen name="transient_taskbar_key_shadow_distance">0dp</dimen>
+ <dimen name="transient_taskbar_icon_spacing">10dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">8dp</dimen>
<dimen name="taskbar_nav_buttons_size">0dp</dimen>
@@ -373,6 +380,11 @@
<dimen name="taskbar_button_margin_6_5">0dp</dimen>
<dimen name="taskbar_button_margin_4_5">0dp</dimen>
<dimen name="taskbar_button_margin_4_4">0dp</dimen>
+ <!-- Taskbar swipe up thresholds threshold -->
+ <dimen name="taskbar_nav_threshold">0dp</dimen>
+ <dimen name="taskbar_app_window_threshold">0dp</dimen>
+ <dimen name="taskbar_home_overview_threshold">0dp</dimen>
+ <dimen name="taskbar_catch_up_threshold">0dp</dimen>
<!-- Size of the maximum radius for the enforced rounded rectangles. -->
<dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 90553a1..d0be420 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -207,14 +207,6 @@
<item name="android:importantForAccessibility">no</item>
</style>
- <style name="AllAppsButtonTheme">
- <item name="allAppsButtonBgColor">@color/all_apps_button_bg_color</item>
- <item name="allAppsButtonColor1">@color/all_apps_button_color_1</item>
- <item name="allAppsButtonColor2">@color/all_apps_button_color_2</item>
- <item name="allAppsButtonColor3">@color/all_apps_button_color_3</item>
- <item name="allAppsButtonColor4">@color/all_apps_button_color_4</item>
- </style>
-
<style name="AllAppsTheme">
<item name="disabledIconAlpha">.54</item>
</style>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 555fbb4..76a91c0 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -249,11 +249,11 @@
/* widgetHandler= */ null,
(ItemInfo) mWidgetView.getTag()));
mLauncher
- .getAppWidgetHost()
- .startConfigActivity(
- mLauncher,
- mWidgetView.getAppWidgetId(),
- Launcher.REQUEST_RECONFIGURE_APPWIDGET);
+ .getAppWidgetHolder()
+ .startConfigActivity(
+ mLauncher,
+ mWidgetView.getAppWidgetId(),
+ Launcher.REQUEST_RECONFIGURE_APPWIDGET);
});
if (!hasSeenReconfigurableWidgetEducationTip()) {
post(() -> {
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 83ff084..9bdc822 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -196,8 +196,7 @@
@Override
protected void onResume() {
- addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
- removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+ setResumed();
super.onResume();
}
@@ -228,7 +227,7 @@
@Override
protected void onPause() {
- removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
+ setPaused();
super.onPause();
// Reset the overridden sysui flags used for the task-swipe launch animation, we do this
@@ -260,6 +259,21 @@
return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
}
+ /**
+ * Sets the activity to appear as paused.
+ */
+ public void setPaused() {
+ removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
+ }
+
+ /**
+ * Sets the activity to appear as resumed.
+ */
+ public void setResumed() {
+ addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
+ removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+ }
+
public boolean isUserActive() {
return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index cdd8f5a..e66d441 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -55,7 +55,6 @@
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.ViewCompat;
@@ -193,7 +192,7 @@
private final Rect mOccupiedRect = new Rect();
private final int[] mDirectionVector = new int[2];
- final int[] mPreviousReorderDirection = new int[2];
+ ItemConfiguration mPreviousSolution = null;
private static final int INVALID_DIRECTION = -100;
private final Rect mTempRect = new Rect();
@@ -247,9 +246,6 @@
mOccupied = new GridOccupancy(mCountX, mCountY);
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
- mPreviousReorderDirection[0] = INVALID_DIRECTION;
- mPreviousReorderDirection[1] = INVALID_DIRECTION;
-
mFolderLeaveBehind.mDelegateCellX = -1;
mFolderLeaveBehind.mDelegateCellY = -1;
@@ -942,7 +938,7 @@
cellToRect(targetCell[0], targetCell[1], spanX, spanY, cellBoundsWithSpacing);
cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2);
- if (canCreateFolder(getChildAt(targetCell[0], targetCell[1]))) {
+ if (canCreateFolder(getChildAt(targetCell[0], targetCell[1])) && spanX == 1 && spanY == 1) {
// Take only the circle in the smaller dimension, to ensure we don't start reordering
// too soon before accepting a folder drop.
int minRadius = centerPoint[0] - cellBoundsWithSpacing.left;
@@ -952,8 +948,9 @@
return minRadius;
}
// Take up the entire cell, including space between this cell and the adjacent ones.
- return (float) Math.hypot(cellBoundsWithSpacing.width() / 2f,
- cellBoundsWithSpacing.height() / 2f);
+ // Multiply by span to scale radius
+ return (float) Math.hypot(spanX * cellBoundsWithSpacing.width() / 2f,
+ spanY * cellBoundsWithSpacing.height() / 2f);
}
public int getCellWidth() {
@@ -1400,649 +1397,6 @@
return bestXY;
}
- /**
- * Find a vacant area that will fit the given bounds nearest the requested
- * cell location, and will also weigh in a suggested direction vector of the
- * desired location. This method computers distance based on unit grid distances,
- * not pixel distances.
- *
- * @param cellX The X cell nearest to which you want to search for a vacant area.
- * @param cellY The Y cell nearest which you want to search for a vacant area.
- * @param spanX Horizontal span of the object.
- * @param spanY Vertical span of the object.
- * @param direction The favored direction in which the views should move from x, y
- * @param occupied The array which represents which cells in the CellLayout are occupied
- * @param blockOccupied The array which represents which cells in the specified block (cellX,
- * cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
- * @param result Array in which to place the result, or null (in which case a new array will
- * be allocated)
- * @return The X, Y cell of a vacant area that can contain this object,
- * nearest the requested location.
- */
- private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
- boolean[][] occupied, boolean blockOccupied[][], int[] result) {
- // Keep track of best-scoring drop area
- final int[] bestXY = result != null ? result : new int[2];
- float bestDistance = Float.MAX_VALUE;
- int bestDirectionScore = Integer.MIN_VALUE;
-
- final int countX = mCountX;
- final int countY = mCountY;
-
- for (int y = 0; y < countY - (spanY - 1); y++) {
- inner:
- for (int x = 0; x < countX - (spanX - 1); x++) {
- // First, let's see if this thing fits anywhere
- for (int i = 0; i < spanX; i++) {
- for (int j = 0; j < spanY; j++) {
- if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
- continue inner;
- }
- }
- }
-
- float distance = (float) Math.hypot(x - cellX, y - cellY);
- int[] curDirection = mTmpPoint;
- computeDirectionVector(x - cellX, y - cellY, curDirection);
- // The direction score is just the dot product of the two candidate direction
- // and that passed in.
- int curDirectionScore = direction[0] * curDirection[0] +
- direction[1] * curDirection[1];
- if (Float.compare(distance, bestDistance) < 0 ||
- (Float.compare(distance, bestDistance) == 0
- && curDirectionScore > bestDirectionScore)) {
- bestDistance = distance;
- bestDirectionScore = curDirectionScore;
- bestXY[0] = x;
- bestXY[1] = y;
- }
- }
- }
-
- // Return -1, -1 if no suitable location found
- if (bestDistance == Float.MAX_VALUE) {
- bestXY[0] = -1;
- bestXY[1] = -1;
- }
- return bestXY;
- }
-
- private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
- int[] direction, ItemConfiguration currentState) {
- CellAndSpan c = currentState.map.get(v);
- boolean success = false;
- mTmpOccupied.markCells(c, false);
- mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
-
- findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY, direction,
- mTmpOccupied.cells, null, mTempLocation);
-
- if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
- c.cellX = mTempLocation[0];
- c.cellY = mTempLocation[1];
- success = true;
- }
- mTmpOccupied.markCells(c, true);
- return success;
- }
-
- /**
- * This helper class defines a cluster of views. It helps with defining complex edges
- * of the cluster and determining how those edges interact with other views. The edges
- * essentially define a fine-grained boundary around the cluster of views -- like a more
- * precise version of a bounding box.
- */
- private class ViewCluster {
- final static int LEFT = 1 << 0;
- final static int TOP = 1 << 1;
- final static int RIGHT = 1 << 2;
- final static int BOTTOM = 1 << 3;
-
- final ArrayList<View> views;
- final ItemConfiguration config;
- final Rect boundingRect = new Rect();
-
- final int[] leftEdge = new int[mCountY];
- final int[] rightEdge = new int[mCountY];
- final int[] topEdge = new int[mCountX];
- final int[] bottomEdge = new int[mCountX];
- int dirtyEdges;
- boolean boundingRectDirty;
-
- @SuppressWarnings("unchecked")
- public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
- this.views = (ArrayList<View>) views.clone();
- this.config = config;
- resetEdges();
- }
-
- void resetEdges() {
- for (int i = 0; i < mCountX; i++) {
- topEdge[i] = -1;
- bottomEdge[i] = -1;
- }
- for (int i = 0; i < mCountY; i++) {
- leftEdge[i] = -1;
- rightEdge[i] = -1;
- }
- dirtyEdges = LEFT | TOP | RIGHT | BOTTOM;
- boundingRectDirty = true;
- }
-
- void computeEdge(int which) {
- int count = views.size();
- for (int i = 0; i < count; i++) {
- CellAndSpan cs = config.map.get(views.get(i));
- switch (which) {
- case LEFT:
- int left = cs.cellX;
- for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
- if (left < leftEdge[j] || leftEdge[j] < 0) {
- leftEdge[j] = left;
- }
- }
- break;
- case RIGHT:
- int right = cs.cellX + cs.spanX;
- for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
- if (right > rightEdge[j]) {
- rightEdge[j] = right;
- }
- }
- break;
- case TOP:
- int top = cs.cellY;
- for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
- if (top < topEdge[j] || topEdge[j] < 0) {
- topEdge[j] = top;
- }
- }
- break;
- case BOTTOM:
- int bottom = cs.cellY + cs.spanY;
- for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
- if (bottom > bottomEdge[j]) {
- bottomEdge[j] = bottom;
- }
- }
- break;
- }
- }
- }
-
- boolean isViewTouchingEdge(View v, int whichEdge) {
- CellAndSpan cs = config.map.get(v);
-
- if ((dirtyEdges & whichEdge) == whichEdge) {
- computeEdge(whichEdge);
- dirtyEdges &= ~whichEdge;
- }
-
- switch (whichEdge) {
- case LEFT:
- for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
- if (leftEdge[i] == cs.cellX + cs.spanX) {
- return true;
- }
- }
- break;
- case RIGHT:
- for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
- if (rightEdge[i] == cs.cellX) {
- return true;
- }
- }
- break;
- case TOP:
- for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
- if (topEdge[i] == cs.cellY + cs.spanY) {
- return true;
- }
- }
- break;
- case BOTTOM:
- for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
- if (bottomEdge[i] == cs.cellY) {
- return true;
- }
- }
- break;
- }
- return false;
- }
-
- void shift(int whichEdge, int delta) {
- for (View v: views) {
- CellAndSpan c = config.map.get(v);
- switch (whichEdge) {
- case LEFT:
- c.cellX -= delta;
- break;
- case RIGHT:
- c.cellX += delta;
- break;
- case TOP:
- c.cellY -= delta;
- break;
- case BOTTOM:
- default:
- c.cellY += delta;
- break;
- }
- }
- resetEdges();
- }
-
- public void addView(View v) {
- views.add(v);
- resetEdges();
- }
-
- public Rect getBoundingRect() {
- if (boundingRectDirty) {
- config.getBoundingRectForViews(views, boundingRect);
- }
- return boundingRect;
- }
-
- final PositionComparator comparator = new PositionComparator();
- class PositionComparator implements Comparator<View> {
- int whichEdge = 0;
- public int compare(View left, View right) {
- CellAndSpan l = config.map.get(left);
- CellAndSpan r = config.map.get(right);
- switch (whichEdge) {
- case LEFT:
- return (r.cellX + r.spanX) - (l.cellX + l.spanX);
- case RIGHT:
- return l.cellX - r.cellX;
- case TOP:
- return (r.cellY + r.spanY) - (l.cellY + l.spanY);
- case BOTTOM:
- default:
- return l.cellY - r.cellY;
- }
- }
- }
-
- public void sortConfigurationForEdgePush(int edge) {
- comparator.whichEdge = edge;
- Collections.sort(config.sortedViews, comparator);
- }
- }
-
- private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
- int[] direction, View dragView, ItemConfiguration currentState) {
-
- ViewCluster cluster = new ViewCluster(views, currentState);
- Rect clusterRect = cluster.getBoundingRect();
- int whichEdge;
- int pushDistance;
- boolean fail = false;
-
- // Determine the edge of the cluster that will be leading the push and how far
- // the cluster must be shifted.
- if (direction[0] < 0) {
- whichEdge = ViewCluster.LEFT;
- pushDistance = clusterRect.right - rectOccupiedByPotentialDrop.left;
- } else if (direction[0] > 0) {
- whichEdge = ViewCluster.RIGHT;
- pushDistance = rectOccupiedByPotentialDrop.right - clusterRect.left;
- } else if (direction[1] < 0) {
- whichEdge = ViewCluster.TOP;
- pushDistance = clusterRect.bottom - rectOccupiedByPotentialDrop.top;
- } else {
- whichEdge = ViewCluster.BOTTOM;
- pushDistance = rectOccupiedByPotentialDrop.bottom - clusterRect.top;
- }
-
- // Break early for invalid push distance.
- if (pushDistance <= 0) {
- return false;
- }
-
- // Mark the occupied state as false for the group of views we want to move.
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, false);
- }
-
- // We save the current configuration -- if we fail to find a solution we will revert
- // to the initial state. The process of finding a solution modifies the configuration
- // in place, hence the need for revert in the failure case.
- currentState.save();
-
- // The pushing algorithm is simplified by considering the views in the order in which
- // they would be pushed by the cluster. For example, if the cluster is leading with its
- // left edge, we consider sort the views by their right edge, from right to left.
- cluster.sortConfigurationForEdgePush(whichEdge);
-
- while (pushDistance > 0 && !fail) {
- for (View v: currentState.sortedViews) {
- // For each view that isn't in the cluster, we see if the leading edge of the
- // cluster is contacting the edge of that view. If so, we add that view to the
- // cluster.
- if (!cluster.views.contains(v) && v != dragView) {
- if (cluster.isViewTouchingEdge(v, whichEdge)) {
- CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
- if (!lp.canReorder) {
- // The push solution includes the all apps button, this is not viable.
- fail = true;
- break;
- }
- cluster.addView(v);
- CellAndSpan c = currentState.map.get(v);
-
- // Adding view to cluster, mark it as not occupied.
- mTmpOccupied.markCells(c, false);
- }
- }
- }
- pushDistance--;
-
- // The cluster has been completed, now we move the whole thing over in the appropriate
- // direction.
- cluster.shift(whichEdge, 1);
- }
-
- boolean foundSolution = false;
- clusterRect = cluster.getBoundingRect();
-
- // Due to the nature of the algorithm, the only check required to verify a valid solution
- // is to ensure that completed shifted cluster lies completely within the cell layout.
- if (!fail && clusterRect.left >= 0 && clusterRect.right <= mCountX && clusterRect.top >= 0 &&
- clusterRect.bottom <= mCountY) {
- foundSolution = true;
- } else {
- currentState.restore();
- }
-
- // In either case, we set the occupied array as marked for the location of the views
- for (View v: cluster.views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, true);
- }
-
- return foundSolution;
- }
-
- private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
- int[] direction, View dragView, ItemConfiguration currentState) {
- if (views.size() == 0) return true;
-
- boolean success = false;
- Rect boundingRect = new Rect();
- // We construct a rect which represents the entire group of views passed in
- currentState.getBoundingRectForViews(views, boundingRect);
-
- // Mark the occupied state as false for the group of views we want to move.
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, false);
- }
-
- GridOccupancy blockOccupied = new GridOccupancy(boundingRect.width(), boundingRect.height());
- int top = boundingRect.top;
- int left = boundingRect.left;
- // We mark more precisely which parts of the bounding rect are truly occupied, allowing
- // for interlocking.
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- blockOccupied.markCells(c.cellX - left, c.cellY - top, c.spanX, c.spanY, true);
- }
-
- mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
-
- findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
- boundingRect.height(), direction,
- mTmpOccupied.cells, blockOccupied.cells, mTempLocation);
-
- // If we successfuly found a location by pushing the block of views, we commit it
- if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
- int deltaX = mTempLocation[0] - boundingRect.left;
- int deltaY = mTempLocation[1] - boundingRect.top;
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- c.cellX += deltaX;
- c.cellY += deltaY;
- }
- success = true;
- }
-
- // In either case, we set the occupied array as marked for the location of the views
- for (View v: views) {
- CellAndSpan c = currentState.map.get(v);
- mTmpOccupied.markCells(c, true);
- }
- return success;
- }
-
- // This method tries to find a reordering solution which satisfies the push mechanic by trying
- // to push items in each of the cardinal directions, in an order based on the direction vector
- // passed.
- private boolean attemptPushInDirection(ArrayList<View> intersectingViews, Rect occupied,
- int[] direction, View ignoreView, ItemConfiguration solution) {
- if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
- // If the direction vector has two non-zero components, we try pushing
- // separately in each of the components.
- int temp = direction[1];
- direction[1] = 0;
-
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- direction[1] = temp;
- temp = direction[0];
- direction[0] = 0;
-
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Revert the direction
- direction[0] = temp;
-
- // Now we try pushing in each component of the opposite direction
- direction[0] *= -1;
- direction[1] *= -1;
- temp = direction[1];
- direction[1] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
-
- direction[1] = temp;
- temp = direction[0];
- direction[0] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // revert the direction
- direction[0] = temp;
- direction[0] *= -1;
- direction[1] *= -1;
-
- } else {
- // If the direction vector has a single non-zero component, we push first in the
- // direction of the vector
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Then we try the opposite direction
- direction[0] *= -1;
- direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Switch the direction back
- direction[0] *= -1;
- direction[1] *= -1;
-
- // If we have failed to find a push solution with the above, then we try
- // to find a solution by pushing along the perpendicular axis.
-
- // Swap the components
- int temp = direction[1];
- direction[1] = direction[0];
- direction[0] = temp;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
-
- // Then we try the opposite direction
- direction[0] *= -1;
- direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
- return true;
- }
- // Switch the direction back
- direction[0] *= -1;
- direction[1] *= -1;
-
- // Swap the components back
- temp = direction[1];
- direction[1] = direction[0];
- direction[0] = temp;
- }
- return false;
- }
-
- private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
- View ignoreView, ItemConfiguration solution) {
- // Return early if get invalid cell positions
- if (cellX < 0 || cellY < 0) return false;
-
- mIntersectingViews.clear();
- mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
-
- // Mark the desired location of the view currently being dragged.
- if (ignoreView != null) {
- CellAndSpan c = solution.map.get(ignoreView);
- if (c != null) {
- c.cellX = cellX;
- c.cellY = cellY;
- }
- }
- Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
- Rect r1 = new Rect();
- for (View child: solution.map.keySet()) {
- if (child == ignoreView) continue;
- CellAndSpan c = solution.map.get(child);
- CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
- r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
- if (Rect.intersects(r0, r1)) {
- if (!lp.canReorder) {
- return false;
- }
- mIntersectingViews.add(child);
- }
- }
-
- solution.intersectingViews = new ArrayList<>(mIntersectingViews);
-
- // First we try to find a solution which respects the push mechanic. That is,
- // we try to find a solution such that no displaced item travels through another item
- // without also displacing that item.
- if (attemptPushInDirection(mIntersectingViews, mOccupiedRect, direction, ignoreView,
- solution)) {
- return true;
- }
-
- // Next we try moving the views as a block, but without requiring the push mechanic.
- if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, ignoreView,
- solution)) {
- return true;
- }
-
- // Ok, they couldn't move as a block, let's move them individually
- for (View v : mIntersectingViews) {
- if (!addViewToTempLocation(v, mOccupiedRect, direction, solution)) {
- return false;
- }
- }
- return true;
- }
-
- /*
- * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
- * the provided point and the provided cell
- */
- private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
- double angle = Math.atan(deltaY / deltaX);
-
- result[0] = 0;
- result[1] = 0;
- if (Math.abs(Math.cos(angle)) > 0.5f) {
- result[0] = (int) Math.signum(deltaX);
- }
- if (Math.abs(Math.sin(angle)) > 0.5f) {
- result[1] = (int) Math.signum(deltaY);
- }
- }
-
- private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
- int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
- // Copy the current state into the solution. This solution will be manipulated as necessary.
- copyCurrentStateToSolution(solution, false);
- // Copy the current occupied array into the temporary occupied array. This array will be
- // manipulated as necessary to find a solution.
- mOccupied.copyTo(mTmpOccupied);
-
- // We find the nearest cell into which we would place the dragged item, assuming there's
- // nothing in its way.
- int result[] = new int[2];
- result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
-
- boolean success;
- // First we try the exact nearest position of the item being dragged,
- // we will then want to try to move this around to other neighbouring positions
- success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
- solution);
-
- if (!success) {
- // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
- // x, then 1 in y etc.
- if (spanX > minSpanX && (minSpanY == spanY || decX)) {
- return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY,
- direction, dragView, false, solution);
- } else if (spanY > minSpanY) {
- return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1,
- direction, dragView, true, solution);
- }
- solution.isSolution = false;
- } else {
- solution.isSolution = true;
- solution.cellX = result[0];
- solution.cellY = result[1];
- solution.spanX = spanX;
- solution.spanY = spanY;
- }
- return solution;
- }
-
- private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
- int childCount = mShortcutsAndWidgets.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mShortcutsAndWidgets.getChildAt(i);
- CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
- CellAndSpan c;
- if (temp) {
- c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
- } else {
- c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
- }
- solution.add(child, c);
- }
- }
-
private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
mTmpOccupied.clear();
@@ -2339,54 +1693,6 @@
return solution;
}
- /* This seems like it should be obvious and straight-forward, but when the direction vector
- needs to match with the notion of the dragView pushing other views, we have to employ
- a slightly more subtle notion of the direction vector. The question is what two points is
- the vector between? The center of the dragView and its desired destination? Not quite, as
- this doesn't necessarily coincide with the interaction of the dragView and items occupying
- those cells. Instead we use some heuristics to often lock the vector to up, down, left
- or right, which helps make pushing feel right.
- */
- private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
- int spanY, View dragView, int[] resultDirection) {
-
- //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
- int[] targetDestination = new int[2];
-
- findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
- Rect dragRect = new Rect();
- cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
- dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
-
- Rect dropRegionRect = new Rect();
- getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY,
- dragView, dropRegionRect, mIntersectingViews);
-
- int dropRegionSpanX = dropRegionRect.width();
- int dropRegionSpanY = dropRegionRect.height();
-
- cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
- dropRegionRect.height(), dropRegionRect);
-
- int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
- int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
-
- if (dropRegionSpanX == mCountX || spanX == mCountX) {
- deltaX = 0;
- }
- if (dropRegionSpanY == mCountY || spanY == mCountY) {
- deltaY = 0;
- }
-
- if (deltaX == 0 && deltaY == 0) {
- // No idea what to do, give a random direction.
- resultDirection[0] = 1;
- resultDirection[1] = 0;
- } else {
- computeDirectionVector(deltaX, deltaY, resultDirection);
- }
- }
-
// For a given cell and span, fetch the set of views intersecting the region.
private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY,
View dragView, Rect boundingRect, ArrayList<View> intersectingViews) {
@@ -2470,32 +1776,745 @@
return swapSolution.isSolution;
}
- int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
- View dragView, int[] result, int resultSpan[], int mode) {
- // First we determine if things have moved enough to cause a different layout
+ /**
+ * Find a vacant area that will fit the given bounds nearest the requested
+ * cell location, and will also weigh in a suggested direction vector of the
+ * desired location. This method computers distance based on unit grid distances,
+ * not pixel distances.
+ *
+ * @param cellX The X cell nearest to which you want to search for a vacant area.
+ * @param cellY The Y cell nearest which you want to search for a vacant area.
+ * @param spanX Horizontal span of the object.
+ * @param spanY Vertical span of the object.
+ * @param direction The favored direction in which the views should move from x, y
+ * @param occupied The array which represents which cells in the CellLayout are occupied
+ * @param blockOccupied The array which represents which cells in the specified block (cellX,
+ * cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
+ * @param result Array in which to place the result, or null (in which case a new array will
+ * be allocated)
+ * @return The X, Y cell of a vacant area that can contain this object,
+ * nearest the requested location.
+ */
+ private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
+ boolean[][] occupied, boolean blockOccupied[][], int[] result) {
+ // Keep track of best-scoring drop area
+ final int[] bestXY = result != null ? result : new int[2];
+ float bestDistance = Float.MAX_VALUE;
+ int bestDirectionScore = Integer.MIN_VALUE;
+
+ final int countX = mCountX;
+ final int countY = mCountY;
+
+ for (int y = 0; y < countY - (spanY - 1); y++) {
+ inner:
+ for (int x = 0; x < countX - (spanX - 1); x++) {
+ // First, let's see if this thing fits anywhere
+ for (int i = 0; i < spanX; i++) {
+ for (int j = 0; j < spanY; j++) {
+ if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
+ continue inner;
+ }
+ }
+ }
+
+ float distance = (float) Math.hypot(x - cellX, y - cellY);
+ int[] curDirection = mTmpPoint;
+ computeDirectionVector(x - cellX, y - cellY, curDirection);
+ // The direction score is just the dot product of the two candidate direction
+ // and that passed in.
+ int curDirectionScore = direction[0] * curDirection[0] +
+ direction[1] * curDirection[1];
+ if (Float.compare(distance, bestDistance) < 0 ||
+ (Float.compare(distance, bestDistance) == 0
+ && curDirectionScore > bestDirectionScore)) {
+ bestDistance = distance;
+ bestDirectionScore = curDirectionScore;
+ bestXY[0] = x;
+ bestXY[1] = y;
+ }
+ }
+ }
+
+ // Return -1, -1 if no suitable location found
+ if (bestDistance == Float.MAX_VALUE) {
+ bestXY[0] = -1;
+ bestXY[1] = -1;
+ }
+ return bestXY;
+ }
+
+ private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
+ int[] direction, ItemConfiguration currentState) {
+ CellAndSpan c = currentState.map.get(v);
+ boolean success = false;
+ mTmpOccupied.markCells(c, false);
+ mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
+
+ findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY, direction,
+ mTmpOccupied.cells, null, mTempLocation);
+
+ if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
+ c.cellX = mTempLocation[0];
+ c.cellY = mTempLocation[1];
+ success = true;
+ }
+ mTmpOccupied.markCells(c, true);
+ return success;
+ }
+
+ private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
+ int[] direction, View dragView, ItemConfiguration currentState) {
+
+ ViewCluster cluster = new ViewCluster(views, currentState);
+ Rect clusterRect = cluster.getBoundingRect();
+ int whichEdge;
+ int pushDistance;
+ boolean fail = false;
+
+ // Determine the edge of the cluster that will be leading the push and how far
+ // the cluster must be shifted.
+ if (direction[0] < 0) {
+ whichEdge = ViewCluster.LEFT;
+ pushDistance = clusterRect.right - rectOccupiedByPotentialDrop.left;
+ } else if (direction[0] > 0) {
+ whichEdge = ViewCluster.RIGHT;
+ pushDistance = rectOccupiedByPotentialDrop.right - clusterRect.left;
+ } else if (direction[1] < 0) {
+ whichEdge = ViewCluster.TOP;
+ pushDistance = clusterRect.bottom - rectOccupiedByPotentialDrop.top;
+ } else {
+ whichEdge = ViewCluster.BOTTOM;
+ pushDistance = rectOccupiedByPotentialDrop.bottom - clusterRect.top;
+ }
+
+ // Break early for invalid push distance.
+ if (pushDistance <= 0) {
+ return false;
+ }
+
+ // Mark the occupied state as false for the group of views we want to move.
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, false);
+ }
+
+ // We save the current configuration -- if we fail to find a solution we will revert
+ // to the initial state. The process of finding a solution modifies the configuration
+ // in place, hence the need for revert in the failure case.
+ currentState.save();
+
+ // The pushing algorithm is simplified by considering the views in the order in which
+ // they would be pushed by the cluster. For example, if the cluster is leading with its
+ // left edge, we consider sort the views by their right edge, from right to left.
+ cluster.sortConfigurationForEdgePush(whichEdge);
+
+ while (pushDistance > 0 && !fail) {
+ for (View v: currentState.sortedViews) {
+ // For each view that isn't in the cluster, we see if the leading edge of the
+ // cluster is contacting the edge of that view. If so, we add that view to the
+ // cluster.
+ if (!cluster.views.contains(v) && v != dragView) {
+ if (cluster.isViewTouchingEdge(v, whichEdge)) {
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
+ if (!lp.canReorder) {
+ // The push solution includes the all apps button, this is not viable.
+ fail = true;
+ break;
+ }
+ cluster.addView(v);
+ CellAndSpan c = currentState.map.get(v);
+
+ // Adding view to cluster, mark it as not occupied.
+ mTmpOccupied.markCells(c, false);
+ }
+ }
+ }
+ pushDistance--;
+
+ // The cluster has been completed, now we move the whole thing over in the appropriate
+ // direction.
+ cluster.shift(whichEdge, 1);
+ }
+
+ boolean foundSolution = false;
+ clusterRect = cluster.getBoundingRect();
+
+ // Due to the nature of the algorithm, the only check required to verify a valid solution
+ // is to ensure that completed shifted cluster lies completely within the cell layout.
+ if (!fail && clusterRect.left >= 0 && clusterRect.right <= mCountX && clusterRect.top >= 0 &&
+ clusterRect.bottom <= mCountY) {
+ foundSolution = true;
+ } else {
+ currentState.restore();
+ }
+
+ // In either case, we set the occupied array as marked for the location of the views
+ for (View v: cluster.views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, true);
+ }
+
+ return foundSolution;
+ }
+
+ /**
+ * This helper class defines a cluster of views. It helps with defining complex edges
+ * of the cluster and determining how those edges interact with other views. The edges
+ * essentially define a fine-grained boundary around the cluster of views -- like a more
+ * precise version of a bounding box.
+ */
+ private class ViewCluster {
+ final static int LEFT = 1 << 0;
+ final static int TOP = 1 << 1;
+ final static int RIGHT = 1 << 2;
+ final static int BOTTOM = 1 << 3;
+
+ final ArrayList<View> views;
+ final ItemConfiguration config;
+ final Rect boundingRect = new Rect();
+
+ final int[] leftEdge = new int[mCountY];
+ final int[] rightEdge = new int[mCountY];
+ final int[] topEdge = new int[mCountX];
+ final int[] bottomEdge = new int[mCountX];
+ int dirtyEdges;
+ boolean boundingRectDirty;
+
+ @SuppressWarnings("unchecked")
+ public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
+ this.views = (ArrayList<View>) views.clone();
+ this.config = config;
+ resetEdges();
+ }
+
+ void resetEdges() {
+ for (int i = 0; i < mCountX; i++) {
+ topEdge[i] = -1;
+ bottomEdge[i] = -1;
+ }
+ for (int i = 0; i < mCountY; i++) {
+ leftEdge[i] = -1;
+ rightEdge[i] = -1;
+ }
+ dirtyEdges = LEFT | TOP | RIGHT | BOTTOM;
+ boundingRectDirty = true;
+ }
+
+ void computeEdge(int which) {
+ int count = views.size();
+ for (int i = 0; i < count; i++) {
+ CellAndSpan cs = config.map.get(views.get(i));
+ switch (which) {
+ case LEFT:
+ int left = cs.cellX;
+ for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
+ if (left < leftEdge[j] || leftEdge[j] < 0) {
+ leftEdge[j] = left;
+ }
+ }
+ break;
+ case RIGHT:
+ int right = cs.cellX + cs.spanX;
+ for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
+ if (right > rightEdge[j]) {
+ rightEdge[j] = right;
+ }
+ }
+ break;
+ case TOP:
+ int top = cs.cellY;
+ for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
+ if (top < topEdge[j] || topEdge[j] < 0) {
+ topEdge[j] = top;
+ }
+ }
+ break;
+ case BOTTOM:
+ int bottom = cs.cellY + cs.spanY;
+ for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
+ if (bottom > bottomEdge[j]) {
+ bottomEdge[j] = bottom;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ boolean isViewTouchingEdge(View v, int whichEdge) {
+ CellAndSpan cs = config.map.get(v);
+
+ if ((dirtyEdges & whichEdge) == whichEdge) {
+ computeEdge(whichEdge);
+ dirtyEdges &= ~whichEdge;
+ }
+
+ switch (whichEdge) {
+ case LEFT:
+ for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
+ if (leftEdge[i] == cs.cellX + cs.spanX) {
+ return true;
+ }
+ }
+ break;
+ case RIGHT:
+ for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
+ if (rightEdge[i] == cs.cellX) {
+ return true;
+ }
+ }
+ break;
+ case TOP:
+ for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
+ if (topEdge[i] == cs.cellY + cs.spanY) {
+ return true;
+ }
+ }
+ break;
+ case BOTTOM:
+ for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
+ if (bottomEdge[i] == cs.cellY) {
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ void shift(int whichEdge, int delta) {
+ for (View v: views) {
+ CellAndSpan c = config.map.get(v);
+ switch (whichEdge) {
+ case LEFT:
+ c.cellX -= delta;
+ break;
+ case RIGHT:
+ c.cellX += delta;
+ break;
+ case TOP:
+ c.cellY -= delta;
+ break;
+ case BOTTOM:
+ default:
+ c.cellY += delta;
+ break;
+ }
+ }
+ resetEdges();
+ }
+
+ public void addView(View v) {
+ views.add(v);
+ resetEdges();
+ }
+
+ public Rect getBoundingRect() {
+ if (boundingRectDirty) {
+ config.getBoundingRectForViews(views, boundingRect);
+ }
+ return boundingRect;
+ }
+
+ final PositionComparator comparator = new PositionComparator();
+ class PositionComparator implements Comparator<View> {
+ int whichEdge = 0;
+ public int compare(View left, View right) {
+ CellAndSpan l = config.map.get(left);
+ CellAndSpan r = config.map.get(right);
+ switch (whichEdge) {
+ case LEFT:
+ return (r.cellX + r.spanX) - (l.cellX + l.spanX);
+ case RIGHT:
+ return l.cellX - r.cellX;
+ case TOP:
+ return (r.cellY + r.spanY) - (l.cellY + l.spanY);
+ case BOTTOM:
+ default:
+ return l.cellY - r.cellY;
+ }
+ }
+ }
+
+ public void sortConfigurationForEdgePush(int edge) {
+ comparator.whichEdge = edge;
+ Collections.sort(config.sortedViews, comparator);
+ }
+ }
+
+ // This method tries to find a reordering solution which satisfies the push mechanic by trying
+ // to push items in each of the cardinal directions, in an order based on the direction vector
+ // passed.
+ private boolean attemptPushInDirection(ArrayList<View> intersectingViews, Rect occupied,
+ int[] direction, View ignoreView, ItemConfiguration solution) {
+ if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
+ // If the direction vector has two non-zero components, we try pushing
+ // separately in each of the components.
+ int temp = direction[1];
+ direction[1] = 0;
+
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ direction[1] = temp;
+ temp = direction[0];
+ direction[0] = 0;
+
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Revert the direction
+ direction[0] = temp;
+
+ // Now we try pushing in each component of the opposite direction
+ direction[0] *= -1;
+ direction[1] *= -1;
+ temp = direction[1];
+ direction[1] = 0;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+
+ direction[1] = temp;
+ temp = direction[0];
+ direction[0] = 0;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // revert the direction
+ direction[0] = temp;
+ direction[0] *= -1;
+ direction[1] *= -1;
+
+ } else {
+ // If the direction vector has a single non-zero component, we push first in the
+ // direction of the vector
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Then we try the opposite direction
+ direction[0] *= -1;
+ direction[1] *= -1;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Switch the direction back
+ direction[0] *= -1;
+ direction[1] *= -1;
+
+ // If we have failed to find a push solution with the above, then we try
+ // to find a solution by pushing along the perpendicular axis.
+
+ // Swap the components
+ int temp = direction[1];
+ direction[1] = direction[0];
+ direction[0] = temp;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+
+ // Then we try the opposite direction
+ direction[0] *= -1;
+ direction[1] *= -1;
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction,
+ ignoreView, solution)) {
+ return true;
+ }
+ // Switch the direction back
+ direction[0] *= -1;
+ direction[1] *= -1;
+
+ // Swap the components back
+ temp = direction[1];
+ direction[1] = direction[0];
+ direction[0] = temp;
+ }
+ return false;
+ }
+
+ /*
+ * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
+ * the provided point and the provided cell
+ */
+ private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
+ double angle = Math.atan(deltaY / deltaX);
+
+ result[0] = 0;
+ result[1] = 0;
+ if (Math.abs(Math.cos(angle)) > 0.5f) {
+ result[0] = (int) Math.signum(deltaX);
+ }
+ if (Math.abs(Math.sin(angle)) > 0.5f) {
+ result[1] = (int) Math.signum(deltaY);
+ }
+ }
+
+ /* This seems like it should be obvious and straight-forward, but when the direction vector
+ needs to match with the notion of the dragView pushing other views, we have to employ
+ a slightly more subtle notion of the direction vector. The question is what two points is
+ the vector between? The center of the dragView and its desired destination? Not quite, as
+ this doesn't necessarily coincide with the interaction of the dragView and items occupying
+ those cells. Instead we use some heuristics to often lock the vector to up, down, left
+ or right, which helps make pushing feel right.
+ */
+ private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
+ int spanY, View dragView, int[] resultDirection) {
+
+ //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
+ int[] targetDestination = new int[2];
+
+ findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
+ Rect dragRect = new Rect();
+ cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
+ dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
+
+ Rect dropRegionRect = new Rect();
+ getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY,
+ dragView, dropRegionRect, mIntersectingViews);
+
+ int dropRegionSpanX = dropRegionRect.width();
+ int dropRegionSpanY = dropRegionRect.height();
+
+ cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
+ dropRegionRect.height(), dropRegionRect);
+
+ int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
+ int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
+
+ if (dropRegionSpanX == mCountX || spanX == mCountX) {
+ deltaX = 0;
+ }
+ if (dropRegionSpanY == mCountY || spanY == mCountY) {
+ deltaY = 0;
+ }
+
+ if (deltaX == 0 && deltaY == 0) {
+ // No idea what to do, give a random direction.
+ resultDirection[0] = 1;
+ resultDirection[1] = 0;
+ } else {
+ computeDirectionVector(deltaX, deltaY, resultDirection);
+ }
+ }
+
+ private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
+ int[] direction, View dragView, ItemConfiguration currentState) {
+ if (views.size() == 0) return true;
+
+ boolean success = false;
+ Rect boundingRect = new Rect();
+ // We construct a rect which represents the entire group of views passed in
+ currentState.getBoundingRectForViews(views, boundingRect);
+
+ // Mark the occupied state as false for the group of views we want to move.
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, false);
+ }
+
+ GridOccupancy blockOccupied = new GridOccupancy(boundingRect.width(), boundingRect.height());
+ int top = boundingRect.top;
+ int left = boundingRect.left;
+ // We mark more precisely which parts of the bounding rect are truly occupied, allowing
+ // for interlocking.
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ blockOccupied.markCells(c.cellX - left, c.cellY - top, c.spanX, c.spanY, true);
+ }
+
+ mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
+
+ findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
+ boundingRect.height(), direction,
+ mTmpOccupied.cells, blockOccupied.cells, mTempLocation);
+
+ // If we successfully found a location by pushing the block of views, we commit it
+ if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
+ int deltaX = mTempLocation[0] - boundingRect.left;
+ int deltaY = mTempLocation[1] - boundingRect.top;
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ c.cellX += deltaX;
+ c.cellY += deltaY;
+ }
+ success = true;
+ }
+
+ // In either case, we set the occupied array as marked for the location of the views
+ for (View v: views) {
+ CellAndSpan c = currentState.map.get(v);
+ mTmpOccupied.markCells(c, true);
+ }
+ return success;
+ }
+
+ private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
+ View ignoreView, ItemConfiguration solution) {
+ // Return early if get invalid cell positions
+ if (cellX < 0 || cellY < 0) return false;
+
+ mIntersectingViews.clear();
+ mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
+
+ // Mark the desired location of the view currently being dragged.
+ if (ignoreView != null) {
+ CellAndSpan c = solution.map.get(ignoreView);
+ if (c != null) {
+ c.cellX = cellX;
+ c.cellY = cellY;
+ }
+ }
+ Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
+ Rect r1 = new Rect();
+ for (View child: solution.map.keySet()) {
+ if (child == ignoreView) continue;
+ CellAndSpan c = solution.map.get(child);
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
+ if (Rect.intersects(r0, r1)) {
+ if (!lp.canReorder) {
+ return false;
+ }
+ mIntersectingViews.add(child);
+ }
+ }
+
+ solution.intersectingViews = new ArrayList<>(mIntersectingViews);
+
+ // First we try to find a solution which respects the push mechanic. That is,
+ // we try to find a solution such that no displaced item travels through another item
+ // without also displacing that item.
+ if (attemptPushInDirection(mIntersectingViews, mOccupiedRect, direction, ignoreView,
+ solution)) {
+ return true;
+ }
+
+ // Next we try moving the views as a block, but without requiring the push mechanic.
+ if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, ignoreView,
+ solution)) {
+ return true;
+ }
+
+ // Ok, they couldn't move as a block, let's move them individually
+ for (View v : mIntersectingViews) {
+ if (!addViewToTempLocation(v, mOccupiedRect, direction, solution)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
+ int spanX, int spanY, int[] direction, View dragView, boolean decX,
+ ItemConfiguration solution) {
+ // Copy the current state into the solution. This solution will be manipulated as necessary.
+ copyCurrentStateToSolution(solution, false);
+ // Copy the current occupied array into the temporary occupied array. This array will be
+ // manipulated as necessary to find a solution.
+ mOccupied.copyTo(mTmpOccupied);
+
+ // We find the nearest cell into which we would place the dragged item, assuming there's
+ // nothing in its way.
+ int result[] = new int[2];
result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
- if (resultSpan == null) {
- resultSpan = new int[2];
- }
+ boolean success;
+ // First we try the exact nearest position of the item being dragged,
+ // we will then want to try to move this around to other neighbouring positions
+ success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
+ solution);
- // When we are checking drop validity or actually dropping, we don't recompute the
- // direction vector, since we want the solution to match the preview, and it's possible
- // that the exact position of the item has changed to result in a new reordering outcome.
- if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
- && mPreviousReorderDirection[0] != INVALID_DIRECTION) {
- mDirectionVector[0] = mPreviousReorderDirection[0];
- mDirectionVector[1] = mPreviousReorderDirection[1];
- // We reset this vector after drop
- if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
- mPreviousReorderDirection[0] = INVALID_DIRECTION;
- mPreviousReorderDirection[1] = INVALID_DIRECTION;
+ if (!success) {
+ // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
+ // x, then 1 in y etc.
+ if (spanX > minSpanX && (minSpanY == spanY || decX)) {
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY,
+ direction, dragView, false, solution);
+ } else if (spanY > minSpanY) {
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1,
+ direction, dragView, true, solution);
}
+ solution.isSolution = false;
} else {
- getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
- mPreviousReorderDirection[0] = mDirectionVector[0];
- mPreviousReorderDirection[1] = mDirectionVector[1];
+ solution.isSolution = true;
+ solution.cellX = result[0];
+ solution.cellY = result[1];
+ solution.spanX = spanX;
+ solution.spanY = spanY;
}
+ return solution;
+ }
+
+ private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
+ int childCount = mShortcutsAndWidgets.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mShortcutsAndWidgets.getChildAt(i);
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
+ CellAndSpan c;
+ if (temp) {
+ c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
+ } else {
+ c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
+ }
+ solution.add(child, c);
+ }
+ }
+
+ /**
+ * Returns a "reorder" where we simply drop the item in the closest empty space, without moving
+ * any other item in the way.
+ *
+ * @param pixelX X coordinate in pixels in the screen
+ * @param pixelY Y coordinate in pixels in the screen
+ * @param spanX horizontal cell span
+ * @param spanY vertical cell span
+ * @return the configuration that represents the found reorder
+ */
+ public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int spanX,
+ int spanY) {
+ int[] result = new int[2];
+ result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
+ ItemConfiguration solution = new ItemConfiguration();
+ copyCurrentStateToSolution(solution, false);
+ solution.isSolution = result[0] != -1;
+ if (!solution.isSolution) {
+ return solution;
+ }
+ solution.cellX = result[0];
+ solution.cellY = result[1];
+ solution.spanX = spanX;
+ solution.spanY = spanY;
+ return solution;
+ }
+
+ /**
+ * When the user drags an Item in the workspace sometimes we need to move the items already in
+ * the workspace to make space for the new item, this function return a solution for that
+ * reorder.
+ *
+ * @param pixelX X coordinate in the screen of the dragView in pixels
+ * @param pixelY Y coordinate in the screen of the dragView in pixels
+ * @param minSpanX minimum horizontal span the item can be shrunk to
+ * @param minSpanY minimum vertical span the item can be shrunk to
+ * @param spanX occupied horizontal span
+ * @param spanY occupied vertical span
+ * @param dragView the view of the item being draged
+ * @return returns a solution for the given parameters, the solution contains all the icons and
+ * the locations they should be in the given solution.
+ */
+ public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
+ int spanX, int spanY, View dragView) {
+ getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
+
+ ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, spanX,
+ spanY);
// Find a solution involving pushing / displacing any items in the way
ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
@@ -2505,73 +2524,104 @@
ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
minSpanY, spanX, spanY, dragView, new ItemConfiguration());
- ItemConfiguration finalSolution = null;
-
// If the reorder solution requires resizing (shrinking) the item being dropped, we instead
// favor a solution in which the item is not resized, but
if (swapSolution.isSolution && swapSolution.area() >= noShuffleSolution.area()) {
- finalSolution = swapSolution;
+ return swapSolution;
} else if (noShuffleSolution.isSolution) {
- finalSolution = noShuffleSolution;
+ return noShuffleSolution;
+ } else if (closestSpaceSolution.isSolution) {
+ return closestSpaceSolution;
}
+ return null;
+ }
- if (mode == MODE_SHOW_REORDER_HINT) {
- if (finalSolution != null) {
- beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
- ReorderPreviewAnimation.MODE_HINT);
- result[0] = finalSolution.cellX;
- result[1] = finalSolution.cellY;
- resultSpan[0] = finalSolution.spanX;
- resultSpan[1] = finalSolution.spanY;
- } else {
- result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+ int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
+ View dragView, int[] result, int[] resultSpan, int mode) {
+ if (resultSpan == null) {
+ resultSpan = new int[]{-1, -1};
+ }
+ if (result == null) {
+ result = new int[]{-1, -1};
+ }
+ ItemConfiguration finalSolution;
+ // When we are checking drop validity or actually dropping, we don't recompute the
+ // direction vector, since we want the solution to match the preview, and it's possible
+ // that the exact position of the item has changed to result in a new reordering outcome.
+ if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
+ && mPreviousSolution != null) {
+ finalSolution = mPreviousSolution;
+ // We reset this vector after drop
+ if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
+ mPreviousSolution = null;
}
- return result;
+ } else {
+ finalSolution = calculateReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
+ dragView);
+ mPreviousSolution = finalSolution;
}
- boolean foundSolution = true;
- if (!DESTRUCTIVE_REORDER) {
- setUseTempCoords(true);
- }
-
- if (finalSolution != null) {
+ if (finalSolution == null || !finalSolution.isSolution) {
+ result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+ } else {
result[0] = finalSolution.cellX;
result[1] = finalSolution.cellY;
resultSpan[0] = finalSolution.spanX;
resultSpan[1] = finalSolution.spanY;
+ }
+ performReorder(finalSolution, dragView, mode);
+ return result;
+ }
- // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
- // committing anything or animating anything as we just want to determine if a solution
- // exists
- if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
- if (!DESTRUCTIVE_REORDER) {
- copySolutionToTempState(finalSolution, dragView);
- }
- setItemPlacementDirty(true);
- animateItemsToSolution(finalSolution, dragView, mode == MODE_ON_DROP);
-
- if (!DESTRUCTIVE_REORDER &&
- (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
- // Since the temp solution didn't update dragView, don't commit it either
- commitTempPlacement(dragView);
- completeAndClearReorderPreviewAnimations();
- setItemPlacementDirty(false);
- } else {
- beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
- ReorderPreviewAnimation.MODE_PREVIEW);
- }
+ /**
+ * Animates and submits in the DB the given ItemConfiguration depending of the mode.
+ *
+ * @param solution represents widgets on the screen which the Workspace will animate to and
+ * would be submitted to the database.
+ * @param dragView view which is being dragged over the workspace that trigger the reorder
+ * @param mode depending on the mode different animations would be played and depending on the
+ * mode the solution would be submitted or not the database.
+ * The possible modes are {@link MODE_SHOW_REORDER_HINT}, {@link MODE_DRAG_OVER},
+ * {@link MODE_ON_DROP}, {@link MODE_ON_DROP_EXTERNAL}, {@link MODE_ACCEPT_DROP}
+ * defined in {@link CellLayout}.
+ */
+ void performReorder(ItemConfiguration solution, View dragView, int mode) {
+ if (mode == MODE_SHOW_REORDER_HINT) {
+ beginOrAdjustReorderPreviewAnimations(solution, dragView,
+ ReorderPreviewAnimation.MODE_HINT);
+ return;
+ }
+ // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
+ // committing anything or animating anything as we just want to determine if a solution
+ // exists
+ if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
+ if (!DESTRUCTIVE_REORDER) {
+ setUseTempCoords(true);
}
- } else {
- foundSolution = false;
- result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+
+ if (!DESTRUCTIVE_REORDER) {
+ copySolutionToTempState(solution, dragView);
+ }
+ setItemPlacementDirty(true);
+ animateItemsToSolution(solution, dragView, mode == MODE_ON_DROP);
+
+ if (!DESTRUCTIVE_REORDER
+ && (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
+ // Since the temp solution didn't update dragView, don't commit it either
+ commitTempPlacement(dragView);
+ completeAndClearReorderPreviewAnimations();
+ setItemPlacementDirty(false);
+ } else {
+ beginOrAdjustReorderPreviewAnimations(solution, dragView,
+ ReorderPreviewAnimation.MODE_PREVIEW);
+ }
}
- if ((mode == MODE_ON_DROP || !foundSolution) && !DESTRUCTIVE_REORDER) {
+ if (mode == MODE_ON_DROP && !DESTRUCTIVE_REORDER) {
setUseTempCoords(false);
}
mShortcutsAndWidgets.requestLayout();
- return result;
}
void setItemPlacementDirty(boolean dirty) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7881a26..edd809c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -218,6 +218,9 @@
public int overviewRowSpacing;
public int overviewGridSideMargin;
+ // Split staging
+ public int splitPlaceholderInset;
+
// Widgets
private final ViewScaleProvider mViewScaleProvider;
@@ -309,7 +312,9 @@
}
if (isTaskbarPresent) {
- taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
+ taskbarSize = DisplayController.isTransientTaskbar(context)
+ ? res.getDimensionPixelSize(R.dimen.transient_taskbar_size)
+ : res.getDimensionPixelSize(R.dimen.taskbar_size);
stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
@@ -459,6 +464,8 @@
overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
overviewGridSideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
+ splitPlaceholderInset = res.getDimensionPixelSize(R.dimen.split_placeholder_inset);
+
// Calculate all of the remaining variables.
extraSpace = updateAvailableDimensions(res);
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 98ecf3a..5225731 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
+import static com.android.launcher3.config.FeatureFlags.HOME_GARDENING_WORKSPACE_BUTTONS;
import android.animation.TimeInterpolator;
import android.content.Context;
@@ -118,7 +119,13 @@
lp.rightMargin = (grid.widthPx - lp.width) / 2;
}
lp.height = grid.dropTargetBarSizePx;
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ // TODO: Add tablet support for DropTargetBar when HOME_GARDENING_WORKSPACE_BUTTONS flag
+ // is on
+ if (HOME_GARDENING_WORKSPACE_BUTTONS.get()) {
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ } else {
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ }
DeviceProfile dp = mLauncher.getDeviceProfile();
int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 747b755..2f927d3 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -24,7 +24,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -92,8 +91,7 @@
protected int getAvailableScrollHeight() {
// AvailableScrollHeight = Total height of the all items - first page height
int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
- int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
- int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
+ int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight;
return Math.max(0, availableScrollHeight);
}
@@ -146,10 +144,7 @@
// IF scroller is at the very top OR there is no scroll bar because there is probably not
// enough items to scroll, THEN it's okay for the container to be pulled down.
- if (getCurrentScrollY() == 0) {
- return true;
- }
- return getAdapter() == null || getAdapter().getItemCount() == 0;
+ return computeVerticalScrollOffset() == 0;
}
/**
@@ -160,53 +155,6 @@
}
/**
- * @return the scroll top of this recycler view.
- */
- public int getCurrentScrollY() {
- Adapter adapter = getAdapter();
- if (adapter == null) {
- return -1;
- }
- if (adapter.getItemCount() == 0 || getChildCount() == 0) {
- return -1;
- }
-
- int itemPosition = NO_POSITION;
- View child = null;
-
- LayoutManager layoutManager = getLayoutManager();
- if (layoutManager instanceof LinearLayoutManager) {
- // Use the LayoutManager as the source of truth for visible positions. During
- // animations, the view group child may not correspond to the visible views that appear
- // at the top.
- itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
- child = layoutManager.findViewByPosition(itemPosition);
- }
-
- if (child == null) {
- // If the layout manager returns null for any reason, which can happen before layout
- // has occurred for the position, then look at the child of this view as a ViewGroup.
- child = getChildAt(0);
- itemPosition = getChildAdapterPosition(child);
- }
- if (itemPosition == NO_POSITION) {
- return -1;
- }
- return getPaddingTop() + getItemsHeight(itemPosition)
- - layoutManager.getDecoratedTop(child);
- }
-
- /**
- * Returns the sum of the height, in pixels, of this list adapter's items from index
- * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
- * it returns the full height of all the items.
- *
- * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
- * sum of all items' height.
- */
- protected abstract int getItemsHeight(int untilIndex);
-
- /**
* Maps the touch (from 0..1) to the adapter position that should be visible.
* <p>Override in each subclass of this base class.
*/
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 6a262c3..635f400 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -64,6 +64,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -641,6 +643,18 @@
float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
int rotation = WindowManagerProxy.INSTANCE.get(context).getRotation(context);
+ if (Utilities.IS_DEBUG_DEVICE) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ DisplayController.INSTANCE.get(context).dump(printWriter);
+ printWriter.flush();
+ Log.d("b/253338238", "getDeviceProfile -"
+ + "\nconfig: " + config
+ + "\ndisplayMetrics: " + res.getDisplayMetrics()
+ + "\nrotation: " + rotation
+ + "\n" + stringWriter,
+ new Exception());
+ }
return getBestMatch(screenWidth, screenHeight, rotation);
}
@@ -1027,7 +1041,7 @@
R.styleable.ProfileDisplayOption_allAppsIconSize, iconSizes[INDEX_DEFAULT]);
allAppsIconSizes[INDEX_LANDSCAPE] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSizeLandscape,
- iconSizes[INDEX_DEFAULT]);
+ allAppsIconSizes[INDEX_DEFAULT]);
allAppsIconSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelPortrait,
allAppsIconSizes[INDEX_DEFAULT]);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5a6b3ec..9426ae9 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -43,6 +43,8 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -97,6 +99,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.util.SparseArray;
@@ -129,6 +132,7 @@
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.BaseAllAppsContainerView;
+import com.android.launcher3.allapps.BaseSearchConfig;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -139,6 +143,7 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.LauncherDragController;
+import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BitmapRenderer;
@@ -163,6 +168,7 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.ArrowPopup;
@@ -201,7 +207,6 @@
import com.android.launcher3.views.FloatingSurfaceView;
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.views.ScrimView;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -313,7 +318,7 @@
DragLayer mDragLayer;
private WidgetManagerHelper mAppWidgetManager;
- private LauncherAppWidgetHost mAppWidgetHost;
+ private LauncherWidgetHolder mAppWidgetHolder;
private final int[] mTmpAddItemCellCoordinates = new int[2];
@@ -392,6 +397,7 @@
private LauncherState mPrevLauncherState;
private StringCache mStringCache;
+ private BaseSearchConfig mBaseSearchConfig;
@Override
@TargetApi(Build.VERSION_CODES.S)
@@ -477,8 +483,8 @@
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
mAppWidgetManager = new WidgetManagerHelper(this);
- mAppWidgetHost = createAppWidgetHost();
- mAppWidgetHost.startListening();
+ mAppWidgetHolder = createAppWidgetHolder();
+ mAppWidgetHolder.startListening();
setupViews();
crossFadeWithPreviousAppearance();
@@ -542,6 +548,9 @@
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
setTitle(R.string.home_screen);
+
+ // TODO: move the SearchConfig to SearchState when new LauncherState is created.
+ mBaseSearchConfig = new BaseSearchConfig();
}
protected LauncherOverlayManager getDefaultOverlay() {
@@ -955,7 +964,7 @@
AppWidgetHostView boundWidget = null;
if (resultCode == RESULT_OK) {
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
- final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
+ final AppWidgetHostView layout = mAppWidgetHolder.createView(this, appWidgetId,
requestArgs.getWidgetHandler().getProviderInfo(this));
boundWidget = layout;
onCompleteRunnable = new Runnable() {
@@ -966,7 +975,7 @@
}
};
} else if (resultCode == RESULT_CANCELED) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
}
if (mDragLayer.getAnimatedView() != null) {
@@ -989,7 +998,7 @@
}
hideKeyboard();
logStopAndResume(false /* isResume */);
- mAppWidgetHost.setActivityStarted(false);
+ mAppWidgetHolder.setActivityStarted(false);
NotificationListener.removeNotificationsChangedListener(getPopupDataProvider());
}
@@ -1002,7 +1011,7 @@
mOverlayManager.onActivityStarted(this);
}
- mAppWidgetHost.setActivityStarted(true);
+ mAppWidgetHolder.setActivityStarted(true);
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -1022,7 +1031,7 @@
NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
DiscoveryBounce.showForHomeIfNeeded(this);
- mAppWidgetHost.setActivityResumed(true);
+ mAppWidgetHolder.setActivityResumed(true);
}
private void logStopAndResume(boolean isResume) {
@@ -1137,7 +1146,7 @@
@Override
public void onStateSetEnd(LauncherState state) {
super.onStateSetEnd(state);
- getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
+ getAppWidgetHolder().setStateIsNormal(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
finishAutoCancelActionMode();
@@ -1184,7 +1193,6 @@
mOverlayManager.onActivityResumed(this);
}
- AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
DragView.removeAllViews(this);
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -1202,7 +1210,7 @@
if (!mDeferOverlayCallbacks) {
mOverlayManager.onActivityPaused(this);
}
- mAppWidgetHost.setActivityResumed(false);
+ mAppWidgetHolder.setActivityResumed(false);
}
/**
@@ -1291,6 +1299,16 @@
mAllAppsController.setupViews(mScrimView, mAppsView);
}
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ if ((SHOW_DOT_PAGINATION.get() || SHOW_DELIGHTFUL_PAGINATION.get())
+ && WorkspacePageIndicator.class.getName().equals(name)) {
+ return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots,
+ (ViewGroup) parent, false);
+ }
+ return super.onCreateView(parent, name, context, attrs);
+ }
+
/**
* Creates a view representing a shortcut.
*
@@ -1314,7 +1332,7 @@
BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_icon, parent, false);
favorite.applyFromWorkspaceItem(info);
- favorite.setOnClickListener(ItemClickHandler.INSTANCE);
+ favorite.setOnClickListener(getItemOnClickListener());
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
@@ -1417,7 +1435,7 @@
if (hostView == null) {
// Perform actual inflation because we're live
- hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ hostView = mAppWidgetHolder.createView(this, appWidgetId, appWidgetInfo);
}
LauncherAppWidgetInfo launcherInfo;
@@ -1548,12 +1566,12 @@
return mScrimView;
}
- public LauncherAppWidgetHost getAppWidgetHost() {
- return mAppWidgetHost;
+ public LauncherWidgetHolder getAppWidgetHolder() {
+ return mAppWidgetHolder;
}
- protected LauncherAppWidgetHost createAppWidgetHost() {
- return new LauncherAppWidgetHost(this,
+ protected LauncherWidgetHolder createAppWidgetHolder() {
+ return new LauncherWidgetHolder(this,
appWidgetId -> getWorkspace().removeWidget(appWidgetId));
}
@@ -1579,12 +1597,8 @@
return mOldConfig.orientation;
}
- /**
- * Whether keyboard sync is enabled for transitions between Home and All Apps.
- * TODO(b/251387263): move this method inside an All Apps specific config class.
- */
- public boolean isKeyboardSyncEnabled() {
- return false;
+ public BaseSearchConfig getSearchConfig() {
+ return mBaseSearchConfig;
}
@Override
@@ -1691,6 +1705,10 @@
outState.remove(RUNTIME_STATE_WIDGET_PANEL);
}
+ // We close any open folders and shortcut containers that are not safe for rebind,
+ // and we need to make sure this state is reflected.
+ AbstractFloatingView.closeAllOpenViewsExcept(
+ this, isStarted() && !isForceInvisible(), TYPE_REBIND_SAFE);
finishAutoCancelActionMode();
if (mPendingRequestArgs != null) {
@@ -1719,7 +1737,7 @@
mRotationHelper.destroy();
try {
- mAppWidgetHost.stopListening();
+ mAppWidgetHolder.stopListening();
} catch (NullPointerException ex) {
Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
}
@@ -1894,7 +1912,7 @@
appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider(
info.componentName);
} else {
- appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+ appWidgetId = getAppWidgetHolder().allocateAppWidgetId();
}
Bundle options = info.bindOptions;
@@ -2008,7 +2026,7 @@
final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
mWorkspace.removeWorkspaceItem(v);
if (deleteFromDb) {
- getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost(), reason);
+ getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHolder(), reason);
}
} else {
return false;
@@ -2266,7 +2284,7 @@
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
- mAppWidgetHost.clearViews();
+ mAppWidgetHolder.clearViews();
if (mHotseat != null) {
mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
@@ -2573,7 +2591,7 @@
if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
// Id has not been allocated yet. Allocate a new id.
- item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ item.appWidgetId = mAppWidgetHolder.allocateAppWidgetId();
item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
// Also try to bind the widget. If the bind fails, the user will be shown
@@ -2635,18 +2653,18 @@
// Verify that we own the widget
if (appWidgetInfo == null) {
FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
- getModelWriter().deleteWidgetInfo(item, getAppWidgetHost(), removalReason);
+ getModelWriter().deleteWidgetInfo(item, getAppWidgetHolder(), removalReason);
return null;
}
item.minSpanX = appWidgetInfo.minSpanX;
item.minSpanY = appWidgetInfo.minSpanY;
- view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+ view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
} else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
&& appWidgetInfo != null) {
- mAppWidgetHost.addPendingView(item.appWidgetId,
+ mAppWidgetHolder.addPendingView(item.appWidgetId,
new PendingAppWidgetHostView(this, item, mIconCache, false));
- view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+ view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
} else {
view = new PendingAppWidgetHostView(this, item, mIconCache, false);
}
@@ -2790,7 +2808,7 @@
View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
preferredItem, packageAndUserAndApp);
- if (v != null && activeRecyclerView.getCurrentScrollY() > 0) {
+ if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
RectF locationBounds = new RectF();
FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
new Rect());
@@ -2801,16 +2819,30 @@
}
return v;
- } else {
- List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
- containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
- mWorkspace.forEachVisiblePage(page
- -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
-
- // Order: Preferred item by itself or in folder, then by matching package/user
- return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
- packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
}
+
+ // Look for the item inside the folder at the current page
+ Folder folder = Folder.getOpen(this);
+ if (folder != null) {
+ View v = getFirstMatch(Collections.singletonList(
+ folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
+ preferredItem,
+ packageAndUserAndApp);
+ if (v == null) {
+ folder.close(isStarted() && !isForceInvisible());
+ } else {
+ return v;
+ }
+ }
+
+ List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
+ containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
+ mWorkspace.forEachVisiblePage(page
+ -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
+
+ // Order: Preferred item by itself or in folder, then by matching package/user
+ return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
+ packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
}
/**
@@ -3004,7 +3036,8 @@
writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs
+ " mPendingActivityResult=" + mPendingActivityResult);
writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
- writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
+ writer.println(prefix + "\tmAppWidgetHolder.isListening: "
+ + mAppWidgetHolder.isListening());
// Extra logging for general debugging
mDragLayer.dump(prefix, writer);
diff --git a/src/com/android/launcher3/LauncherWidgetHolder.java b/src/com/android/launcher3/LauncherWidgetHolder.java
new file mode 100644
index 0000000..5fcd46f
--- /dev/null
+++ b/src/com/android/launcher3/LauncherWidgetHolder.java
@@ -0,0 +1,187 @@
+/**
+ * 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;
+
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
+
+import java.util.function.IntConsumer;
+
+/**
+ * A wrapper for LauncherAppWidgetHost. This class is created so the AppWidgetHost could run in
+ * background.
+ */
+public class LauncherWidgetHolder {
+ @NonNull
+ private final LauncherAppWidgetHost mWidgetHost;
+
+ public LauncherWidgetHolder(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public LauncherWidgetHolder(@NonNull Context context,
+ @Nullable IntConsumer appWidgetRemovedCallback) {
+ mWidgetHost = new LauncherAppWidgetHost(context, appWidgetRemovedCallback);
+ }
+
+ /**
+ * Starts listening to the widget updates from the server side
+ */
+ public void startListening() {
+ mWidgetHost.startListening();
+ }
+
+ /**
+ * Set the STARTED state of the widget host
+ * @param isStarted True if setting the host as started, false otherwise
+ */
+ public void setActivityStarted(boolean isStarted) {
+ mWidgetHost.setActivityStarted(isStarted);
+ }
+
+ /**
+ * Set the RESUMED state of the widget host
+ * @param isResumed True if setting the host as resumed, false otherwise
+ */
+ public void setActivityResumed(boolean isResumed) {
+ mWidgetHost.setActivityResumed(isResumed);
+ }
+
+ /**
+ * Set the NORMAL state of the widget host
+ * @param isNormal True if setting the host to be in normal state, false otherwise
+ */
+ public void setStateIsNormal(boolean isNormal) {
+ mWidgetHost.setStateIsNormal(isNormal);
+ }
+
+ /**
+ * Delete the specified app widget from the host
+ * @param appWidgetId The ID of the app widget to be deleted
+ */
+ public void deleteAppWidgetId(int appWidgetId) {
+ mWidgetHost.deleteAppWidgetId(appWidgetId);
+ }
+
+ /**
+ * Add the pending view to the host for complete configuration in further steps
+ * @param appWidgetId The ID of the specified app widget
+ * @param view The {@link PendingAppWidgetHostView} of the app widget
+ */
+ public void addPendingView(int appWidgetId, @NonNull PendingAppWidgetHostView view) {
+ mWidgetHost.addPendingView(appWidgetId, view);
+ }
+
+ /**
+ * @return True if the host is listening to the widget updates, false otherwise
+ */
+ public boolean isListening() {
+ return mWidgetHost.isListening();
+ }
+
+ /**
+ * @return The allocated app widget id if allocation is successful, returns -1 otherwise
+ */
+ public int allocateAppWidgetId() {
+ return mWidgetHost.allocateAppWidgetId();
+ }
+
+ /**
+ * Add a listener that is triggered when the providers of the widgets are changed
+ * @param listener The listener that notifies when the providers changed
+ */
+ public void addProviderChangeListener(
+ @NonNull LauncherAppWidgetHost.ProviderChangedListener listener) {
+ mWidgetHost.addProviderChangeListener(listener);
+ }
+
+ /**
+ * Remove the specified listener from the host
+ * @param listener The listener that is to be removed from the host
+ */
+ public void removeProviderChangeListener(
+ LauncherAppWidgetHost.ProviderChangedListener listener) {
+ mWidgetHost.removeProviderChangeListener(listener);
+ }
+
+ /**
+ * Starts the configuration activity for the widget
+ * @param activity The activity in which to start the configuration page
+ * @param widgetId The ID of the widget
+ * @param requestCode The request code
+ */
+ public void startConfigActivity(@NonNull BaseDraggingActivity activity, int widgetId,
+ int requestCode) {
+ mWidgetHost.startConfigActivity(activity, widgetId, requestCode);
+ }
+
+ /**
+ * Starts the binding flow for the widget
+ * @param activity The activity for which to bind the widget
+ * @param appWidgetId The ID of the widget
+ * @param info The {@link AppWidgetProviderInfo} of the widget
+ * @param requestCode The request code
+ */
+ public void startBindFlow(@NonNull BaseActivity activity,
+ int appWidgetId, @NonNull AppWidgetProviderInfo info, int requestCode) {
+ mWidgetHost.startBindFlow(activity, appWidgetId, info, requestCode);
+ }
+
+ /**
+ * Stop the host from listening to the widget updates
+ */
+ public void stopListening() {
+ mWidgetHost.stopListening();
+ }
+
+ /**
+ * Create a view for the specified app widget
+ * @param context The activity context for which the view is created
+ * @param appWidgetId The ID of the widget
+ * @param info The {@link LauncherAppWidgetProviderInfo} of the widget
+ * @return A view for the widget
+ */
+ @NonNull
+ public AppWidgetHostView createView(@NonNull Context context, int appWidgetId,
+ @NonNull LauncherAppWidgetProviderInfo info) {
+ return mWidgetHost.createView(context, appWidgetId, info);
+ }
+
+ /**
+ * Set the interaction handler for the widget host
+ * @param handler The interaction handler
+ */
+ public void setInteractionHandler(
+ @Nullable LauncherAppWidgetHost.LauncherWidgetInteractionHandler handler) {
+ ApiWrapper.setHostInteractionHandler(mWidgetHost, handler);
+ }
+
+ /**
+ * Clears all the views from the host
+ */
+ public void clearViews() {
+ mWidgetHost.clearViews();
+ }
+}
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 0ee7aae..791cfff 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -288,7 +288,7 @@
if (widgetId != INVALID_APPWIDGET_ID) {
mLauncher.setWaitingForResult(
PendingRequestArgs.forWidgetInfo(widgetId, null, info));
- mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId,
+ mLauncher.getAppWidgetHolder().startConfigActivity(mLauncher, widgetId,
REQUEST_RECONFIGURE_APPWIDGET);
}
return null;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 5d388dc..b6eb589 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -53,12 +53,14 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -107,7 +109,6 @@
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
@@ -222,8 +223,8 @@
// Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
private float mXDown;
private float mYDown;
- private View mQsb;
- private boolean mIsEventOverQsb;
+ private View mFirstPagePinnedItem;
+ private boolean mIsEventOverFirstPagePinnedItem;
final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
@@ -325,6 +326,26 @@
updateCellLayoutPadding();
updateWorkspaceWidgetsSizes();
+ setPageIndicatorInset();
+ }
+
+ private void setPageIndicatorInset() {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPageIndicator.getLayoutParams();
+
+ // Set insets for page indicator
+ Rect padding = grid.workspacePadding;
+ if (grid.isVerticalBarLayout()) {
+ lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
+ lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
+ lp.bottomMargin = padding.bottom;
+ } else {
+ lp.leftMargin = lp.rightMargin = 0;
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.bottomMargin = grid.hotseatBarSizePx;
+ }
+ mPageIndicator.setLayoutParams(lp);
}
private void updateCellLayoutPadding() {
@@ -553,20 +574,22 @@
// Add the first page
CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, getChildCount());
- // Always add a QSB on the first screen.
- if (mQsb == null) {
- // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
- // edges, we do not need a full width QSB.
- mQsb = LayoutInflater.from(getContext())
+ // Always add a first page pinned widget on the first screen.
+ if (mFirstPagePinnedItem == null) {
+ // In transposed layout, we add the first page pinned widget in the Grid.
+ // As workspace does not touch the edges, we do not need a full
+ // width first page pinned widget.
+ mFirstPagePinnedItem = LayoutInflater.from(getContext())
.inflate(R.layout.search_container_workspace, firstPage, false);
}
int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1);
lp.canReorder = false;
- if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
+ if (!firstPage.addViewToCellLayout(
+ mFirstPagePinnedItem, 0, R.id.search_container_workspace, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
- mQsb = null;
+ mFirstPagePinnedItem = null;
}
}
@@ -575,9 +598,9 @@
// transition animations competing with us changing the scroll when we add pages
disableLayoutTransitions();
- // Recycle the QSB widget
- if (mQsb != null) {
- ((ViewGroup) mQsb.getParent()).removeView(mQsb);
+ // Recycle the first page pinned widget
+ if (mFirstPagePinnedItem != null) {
+ ((ViewGroup) mFirstPagePinnedItem.getParent()).removeView(mFirstPagePinnedItem);
}
// Remove the pages and clear the screen models
@@ -897,6 +920,10 @@
return mScreenOrder;
}
+ protected View getFirstPagePinnedItem() {
+ return mFirstPagePinnedItem;
+ }
+
/**
* Returns the screen ID of a page that is shown together with the given page screen ID when the
* two panel UI is enabled.
@@ -1048,20 +1075,22 @@
mXDown = ev.getX();
mYDown = ev.getY();
- if (mQsb != null) {
+ if (mFirstPagePinnedItem != null) {
mTempFXY[0] = mXDown + getScrollX();
mTempFXY[1] = mYDown + getScrollY();
- Utilities.mapCoordInSelfToDescendant(mQsb, this, mTempFXY);
- mIsEventOverQsb = mQsb.getLeft() <= mTempFXY[0] && mQsb.getRight() >= mTempFXY[0]
- && mQsb.getTop() <= mTempFXY[1] && mQsb.getBottom() >= mTempFXY[1];
+ Utilities.mapCoordInSelfToDescendant(mFirstPagePinnedItem, this, mTempFXY);
+ mIsEventOverFirstPagePinnedItem = mFirstPagePinnedItem.getLeft() <= mTempFXY[0]
+ && mFirstPagePinnedItem.getRight() >= mTempFXY[0]
+ && mFirstPagePinnedItem.getTop() <= mTempFXY[1]
+ && mFirstPagePinnedItem.getBottom() >= mTempFXY[1];
} else {
- mIsEventOverQsb = false;
+ mIsEventOverFirstPagePinnedItem = false;
}
}
@Override
protected void determineScrollingStart(MotionEvent ev) {
- if (!isFinishedSwitchingState() || mIsEventOverQsb) return;
+ if (!isFinishedSwitchingState() || mIsEventOverFirstPagePinnedItem) return;
float deltaX = ev.getX() - mXDown;
float absDeltaX = Math.abs(deltaX);
@@ -2439,6 +2468,9 @@
int reorderX = mTargetCell[0];
int reorderY = mTargetCell[1];
if (!nearestDropOccupied) {
+ mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
+ (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
+ child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT);
mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
item.spanX, item.spanY, d);
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
@@ -2512,10 +2544,10 @@
}
private boolean isDragObjectOverSmartSpace(DragObject dragObject) {
- if (mQsb == null) {
+ if (mFirstPagePinnedItem == null) {
return false;
}
- getViewBoundsRelativeToWorkspace(mQsb, mTempRect);
+ getViewBoundsRelativeToWorkspace(mFirstPagePinnedItem, mTempRect);
return mTempRect.contains(dragObject.x, dragObject.y);
}
@@ -3361,7 +3393,7 @@
public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
if (!changedInfo.isEmpty()) {
DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
- mLauncher.getAppWidgetHost());
+ mLauncher.getAppWidgetHolder());
LauncherAppWidgetInfo item = changedInfo.get(0);
final AppWidgetProviderInfo widgetInfo;
@@ -3487,19 +3519,19 @@
*/
private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
private final ArrayList<LauncherAppWidgetInfo> mInfos;
- private final LauncherAppWidgetHost mHost;
+ private final LauncherWidgetHolder mWidgetHolder;
private final Handler mHandler;
private boolean mRefreshPending;
DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
- LauncherAppWidgetHost host) {
+ LauncherWidgetHolder holder) {
mInfos = infos;
- mHost = host;
+ mWidgetHolder = holder;
mHandler = mLauncher.mHandler;
mRefreshPending = true;
- mHost.addProviderChangeListener(this);
+ mWidgetHolder.addProviderChangeListener(this);
// Force refresh after 10 seconds, if we don't get the provider changed event.
// This could happen when the provider is no longer available in the app.
Message msg = Message.obtain(mHandler, this);
@@ -3509,7 +3541,7 @@
@Override
public void run() {
- mHost.removeProviderChangeListener(this);
+ mWidgetHolder.removeProviderChangeListener(this);
mHandler.removeCallbacks(this);
if (!mRefreshPending) {
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index a991c2f..62e7ef3 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -29,11 +29,14 @@
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.LauncherState.WORKSPACE_PAGE_INDICATOR;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.config.FeatureFlags.HOME_GARDENING_WORKSPACE_BUTTONS;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
@@ -69,6 +72,8 @@
*/
public class WorkspaceStateTransitionAnimation {
+ private static final float FIRST_PAGE_PINNED_WIDGET_DISABLED_ALPHA = 0.3f;
+
private static final FloatProperty<Workspace<?>> WORKSPACE_SCALE_PROPERTY =
WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE);
@@ -155,6 +160,30 @@
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, hotseatFadeInterpolator);
+ if (SHOW_HOME_GARDENING.get()) {
+ propertySetter.setViewAlpha(
+ mWorkspace.getFirstPagePinnedItem(),
+ state == SPRING_LOADED ? FIRST_PAGE_PINNED_WIDGET_DISABLED_ALPHA : 1,
+ workspaceFadeInterpolator);
+ propertySetter.addEndListener(success -> {
+ if (success) {
+ mWorkspace.getFirstPagePinnedItem().setClickable(state != SPRING_LOADED);
+ }
+ });
+ }
+
+ if (HOME_GARDENING_WORKSPACE_BUTTONS.get()) {
+ propertySetter.setViewAlpha(
+ mLauncher.getHotseat().getQsb(),
+ state == SPRING_LOADED ? 0 : 1,
+ workspaceFadeInterpolator);
+ propertySetter.addEndListener(success -> {
+ if (success) {
+ mLauncher.getHotseat().getQsb().setClickable(state != SPRING_LOADED);
+ }
+ });
+ }
+
// Update the accessibility flags for hotseat based on launcher state.
hotseat.setImportantForAccessibility(
state.hasFlag(FLAG_HOTSEAT_INACCESSIBLE)
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index fe0230a..368a373 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -26,7 +26,9 @@
import androidx.core.view.accessibility.AccessibilityRecordCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import com.android.launcher3.util.ScrollableLayoutManager;
import com.android.launcher3.views.ActivityContext;
import java.util.List;
@@ -66,10 +68,10 @@
/**
* A subclass of GridLayoutManager that overrides accessibility values during app search.
*/
- public class AppsGridLayoutManager extends GridLayoutManager {
+ public class AppsGridLayoutManager extends ScrollableLayoutManager {
public AppsGridLayoutManager(Context context) {
- super(context, 1, GridLayoutManager.VERTICAL, false);
+ super(context);
}
@Override
@@ -129,6 +131,15 @@
}
return extraRows;
}
+
+ @Override
+ protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+ AllAppsGridAdapter.AdapterItem item = mApps.getAdapterItems().get(position);
+ // only account for the first icon in the row since they are the same size within a row
+ return (isIconViewType(item.viewType) && item.rowAppIndex != 0)
+ ? heightUntilLastPos
+ : (heightUntilLastPos + mCachedSizes.get(item.viewType));
+ }
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 21a7dfb..ac10892 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,9 +15,11 @@
*/
package com.android.launcher3.allapps;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
@@ -26,11 +28,11 @@
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.SparseIntArray;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
@@ -49,40 +51,11 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
- protected AlphabeticalAppsList<?> mApps;
protected final int mNumAppsPerRow;
-
- // The specific view heights that we use to calculate scroll
- private final SparseIntArray mViewHeights = new SparseIntArray();
- private final SparseIntArray mCachedScrollPositions = new SparseIntArray();
private final AllAppsFastScrollHelper mFastScrollHelper;
+ private int mCumulativeVerticalScroll;
-
- private final AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
- public void onChanged() {
- mCachedScrollPositions.clear();
- }
-
- @Override
- public void onItemRangeChanged(int positionStart, int itemCount) {
- onChanged();
- }
-
- @Override
- public void onItemRangeInserted(int positionStart, int itemCount) {
- onChanged();
- }
-
- @Override
- public void onItemRangeRemoved(int positionStart, int itemCount) {
- onChanged();
- }
-
- @Override
- public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
- onChanged();
- }
- };
+ protected AlphabeticalAppsList<?> mApps;
public AllAppsRecyclerView(Context context) {
this(context, null);
@@ -122,12 +95,8 @@
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows
* (mNumAppsPerRow + 1));
-
- mViewHeights.clear();
- mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
}
-
@Override
public void onDraw(Canvas c) {
if (DEBUG) {
@@ -157,7 +126,7 @@
StatsLogManager mgr = ActivityContext.lookupContext(getContext()).getStatsLogManager();
switch (state) {
case SCROLL_STATE_DRAGGING:
- mgr.logger().log(LAUNCHER_ALLAPPS_SCROLLED);
+ mCumulativeVerticalScroll = 0;
requestFocus();
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
@@ -166,10 +135,17 @@
case SCROLL_STATE_IDLE:
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END, this);
+ logCumulativeVerticalScroll();
break;
}
}
+ @Override
+ public void onScrolled(int dx, int dy) {
+ super.onScrolled(dx, dy);
+ mCumulativeVerticalScroll += dy;
+ }
+
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
@@ -200,17 +176,6 @@
}
@Override
- public void setAdapter(Adapter adapter) {
- if (getAdapter() != null) {
- getAdapter().unregisterAdapterDataObserver(mObserver);
- }
- super.setAdapter(adapter);
- if (adapter != null) {
- adapter.registerAdapterDataObserver(mObserver);
- }
- }
-
- @Override
protected boolean isPaddingOffsetRequired() {
return true;
}
@@ -231,13 +196,13 @@
List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
- if (items.isEmpty() || mNumAppsPerRow == 0) {
+ if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
mScrollbar.setThumbOffsetY(-1);
return;
}
// Skip early if, there no child laid out in the container.
- int scrollY = getCurrentScrollY();
+ int scrollY = computeVerticalScrollOffset();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -292,51 +257,6 @@
}
}
- @Override
- protected int getItemsHeight(int position) {
- List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
- AllAppsGridAdapter.AdapterItem posItem = position < items.size()
- ? items.get(position) : null;
- int y = mCachedScrollPositions.get(position, -1);
- if (y < 0) {
- y = 0;
- for (int i = 0; i < position; i++) {
- AllAppsGridAdapter.AdapterItem item = items.get(i);
- if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
- // Break once we reach the desired row
- if (posItem != null && posItem.viewType == item.viewType &&
- posItem.rowIndex == item.rowIndex) {
- break;
- }
- // Otherwise, only account for the first icon in the row since they are the same
- // size within a row
- if (item.rowAppIndex == 0) {
- y += mViewHeights.get(item.viewType, 0);
- }
- } else {
- // Rest of the views span the full width
- int elHeight = mViewHeights.get(item.viewType);
- if (elHeight == 0) {
- ViewHolder holder = findViewHolderForAdapterPosition(i);
- if (holder == null) {
- holder = getAdapter().createViewHolder(this, item.viewType);
- getAdapter().onBindViewHolder(holder, i);
- holder.itemView.measure(UNSPECIFIED, UNSPECIFIED);
- elHeight = holder.itemView.getMeasuredHeight();
-
- getRecycledViewPool().putRecycledView(holder);
- } else {
- elHeight = holder.itemView.getMeasuredHeight();
- }
- }
- y += elHeight;
- }
- }
- mCachedScrollPositions.put(position, y);
- }
- return y;
- }
-
public int getScrollBarTop() {
return getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding);
}
@@ -349,4 +269,21 @@
public boolean hasOverlappingRendering() {
return false;
}
+
+ private void logCumulativeVerticalScroll() {
+ ActivityContext context = ActivityContext.lookupContext(getContext());
+ StatsLogManager mgr = context.getStatsLogManager();
+ ExtendedEditText editText = context.getAppsView().getSearchUiManager().getEditText();
+ ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer(
+ SearchResultContainer
+ .newBuilder()
+ .setQueryLength((editText == null) ? -1 : editText.length())).build();
+
+ // mCumulativeVerticalScroll == 0 when user comes back to original position, we don't
+ // know the direction of scrolling.
+ mgr.logger().withContainerInfo(containerInfo).log(
+ mCumulativeVerticalScroll == 0 ? LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION
+ : (mCumulativeVerticalScroll > 0) ? LAUNCHER_ALLAPPS_SCROLLED_DOWN
+ : LAUNCHER_ALLAPPS_SCROLLED_UP);
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 001b494..fa2c6e9 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -42,6 +43,7 @@
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.views.ScrimView;
@@ -74,6 +76,8 @@
}
};
+ private static final float ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT = 0f;
+
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_TRANSLATION =
new FloatProperty<AllAppsTransitionController>("allAppsPullBackTranslation") {
@@ -82,8 +86,7 @@
if (controller.mIsTablet) {
return controller.mAppsView.getActiveRecyclerView().getTranslationY();
} else {
- return controller.getAppsViewPullbackTranslationY().get(
- controller.mAppsView);
+ return controller.getAppsViewPullbackTranslationY().getValue();
}
}
@@ -91,13 +94,18 @@
public void setValue(AllAppsTransitionController controller, float translation) {
if (controller.mIsTablet) {
controller.mAppsView.getActiveRecyclerView().setTranslationY(translation);
+ controller.getAppsViewPullbackTranslationY().setValue(
+ ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
} else {
- controller.getAppsViewPullbackTranslationY().set(controller.mAppsView,
- translation);
+ controller.getAppsViewPullbackTranslationY().setValue(translation);
+ controller.mAppsView.getActiveRecyclerView().setTranslationY(
+ ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
}
}
};
+ private static final float ALL_APPS_PULL_BACK_ALPHA_DEFAULT = 1f;
+
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_ALPHA =
new FloatProperty<AllAppsTransitionController>("allAppsPullBackAlpha") {
@@ -114,8 +122,12 @@
public void setValue(AllAppsTransitionController controller, float alpha) {
if (controller.mIsTablet) {
controller.mAppsView.getActiveRecyclerView().setAlpha(alpha);
+ controller.getAppsViewPullbackAlpha().setValue(
+ ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
} else {
controller.getAppsViewPullbackAlpha().setValue(alpha);
+ controller.mAppsView.getActiveRecyclerView().setAlpha(
+ ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
}
}
};
@@ -140,10 +152,8 @@
private ScrimView mScrimView;
- private final MultiPropertyFactory<View>
- mAppsViewTranslationYPropertyFactory = new MultiPropertyFactory<>(
- "appsViewTranslationY", View.TRANSLATION_Y, Float::sum);
private MultiValueAlpha mAppsViewAlpha;
+ private MultiPropertyFactory<View> mAppsViewTranslationY;
private boolean mIsTablet;
@@ -184,7 +194,7 @@
*/
public void setProgress(float progress) {
mProgress = progress;
- getAppsViewProgressTranslationY().set(mAppsView, mProgress * mShiftRange);
+ getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange);
mLauncher.onAllAppsTransition(1 - progress);
}
@@ -192,20 +202,20 @@
return mProgress;
}
- private FloatProperty<View> getAppsViewProgressTranslationY() {
- return mAppsViewTranslationYPropertyFactory.get(INDEX_APPS_VIEW_PROGRESS);
+ private MultiProperty getAppsViewProgressTranslationY() {
+ return mAppsViewTranslationY.get(INDEX_APPS_VIEW_PROGRESS);
}
- private FloatProperty<View> getAppsViewPullbackTranslationY() {
- return mAppsViewTranslationYPropertyFactory.get(INDEX_APPS_VIEW_PULLBACK);
+ private MultiProperty getAppsViewPullbackTranslationY() {
+ return mAppsViewTranslationY.get(INDEX_APPS_VIEW_PULLBACK);
}
- private MultiValueAlpha.AlphaProperty getAppsViewProgressAlpha() {
- return mAppsViewAlpha.getProperty(INDEX_APPS_VIEW_PROGRESS);
+ private MultiProperty getAppsViewProgressAlpha() {
+ return mAppsViewAlpha.get(INDEX_APPS_VIEW_PROGRESS);
}
- private MultiValueAlpha.AlphaProperty getAppsViewPullbackAlpha() {
- return mAppsViewAlpha.getProperty(INDEX_APPS_VIEW_PULLBACK);
+ private MultiProperty getAppsViewPullbackAlpha() {
+ return mAppsViewAlpha.get(INDEX_APPS_VIEW_PULLBACK);
}
/**
@@ -228,14 +238,14 @@
StateAnimationConfig config, PendingAnimation builder) {
if (mLauncher.isInState(ALL_APPS) && !ALL_APPS.equals(toState)) {
// For atomic animations, we close the keyboard immediately.
- if (!config.userControlled && !mLauncher.isKeyboardSyncEnabled()) {
+ if (!config.userControlled && !mLauncher.getSearchConfig().isKeyboardSyncEnabled()) {
mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
}
builder.addEndListener(success -> {
// Reset pull back progress and alpha after switching states.
- ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f);
- ALL_APPS_PULL_BACK_ALPHA.set(this, 1f);
+ ALL_APPS_PULL_BACK_TRANSLATION.set(this, ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
+ ALL_APPS_PULL_BACK_ALPHA.set(this, ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
// We only want to close the keyboard if the animation has completed successfully.
// The reason is that with keyboard sync, if the user swipes down from All Apps with
@@ -283,7 +293,7 @@
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
- setter.setFloat(getAppsViewProgressAlpha(), MultiValueAlpha.VALUE,
+ setter.setFloat(getAppsViewProgressAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE,
hasAllAppsContent ? 1 : 0, allAppsFade);
boolean shouldProtectHeader =
@@ -302,8 +312,11 @@
mScrimView = scrimView;
mAppsView = appsView;
mAppsView.setScrimView(scrimView);
+
mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT);
mAppsViewAlpha.setUpdateVisibility(true);
+ mAppsViewTranslationY = new MultiPropertyFactory<>(
+ mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum);
}
/**
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 70c1e18..2ff992e 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
@@ -106,7 +107,8 @@
new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
+ updateHeaderScroll(
+ ((AllAppsRecyclerView) recyclerView).computeVerticalScrollOffset());
}
};
@@ -244,6 +246,10 @@
mWorkManager.reset();
}
}
+
+ mActivityContext.getStatsLogManager().logger()
+ .withCardinality(mAllAppsStore.getApps().length)
+ .log(LAUNCHER_ALLAPPS_COUNT);
}
/**
diff --git a/src/com/android/launcher3/allapps/BaseSearchConfig.java b/src/com/android/launcher3/allapps/BaseSearchConfig.java
new file mode 100644
index 0000000..9f47e8d
--- /dev/null
+++ b/src/com/android/launcher3/allapps/BaseSearchConfig.java
@@ -0,0 +1,28 @@
+/*
+ * 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.allapps;
+
+/** Base config values for search. */
+public class BaseSearchConfig {
+ public BaseSearchConfig() {}
+
+ /**
+ * Returns whether to enable the synchronized keyboard transition between Home and All Apps.
+ */
+ public boolean isKeyboardSyncEnabled() {
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index f31379e..1cbb0f9 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -68,7 +68,7 @@
mAnimator.cancel();
}
- int current = -mCurrentRV.getCurrentScrollY();
+ int current = -mCurrentRV.computeVerticalScrollOffset();
boolean headerCollapsed = mHeaderCollapsed;
moved(current);
applyVerticalMove();
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 6138bc4..228b02b 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -64,7 +64,8 @@
/**
* sets highlight result's title
*/
- default void setFocusedResultTitle(@Nullable CharSequence title) { }
+ default void setFocusedResultTitle(
+ @Nullable CharSequence title, @Nullable CharSequence subtitle) {}
/** Refresh the currently displayed list of results. */
default void refreshResults() {}
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index b0ed2d2..6ba58b6 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -38,6 +38,7 @@
public void add(Animator animatorSet) {
animatorSet.setDuration(0);
animatorSet.start();
+ animatorSet.end();
}
};
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 211ec77..32463a5 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -156,6 +156,11 @@
"ENABLE_SMARTSPACE_DISMISS", true,
"Adds a menu option to dismiss the current Enhanced Smartspace card.");
+ public static final BooleanFlag ENABLE_OVERLAY_CONNECTION_OPTIM = getDebugFlag(
+ "ENABLE_OVERLAY_CONNECTION_OPTIM",
+ false,
+ "Enable optimizing overlay service connection");
+
/**
* Enables region sampling for text color: Needs system health assessment before turning on
*/
@@ -201,10 +206,6 @@
public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
"ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
- public static final BooleanFlag ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER = new DeviceFlag(
- "ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER", true,
- "Enables a local filter for recommended widgets.");
-
public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false,
"Sends a notification whenever launcher encounters an uncaught exception.");
@@ -248,6 +249,10 @@
"ENABLE_SPLIT_FROM_WORKSPACE", true,
"Enable initiating split screen from workspace.");
+ public static final BooleanFlag ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS =
+ getDebugFlag("ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", false,
+ "Enable splitting from fullscreen app with keyboard shortcuts");
+
public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(
"ENABLE_NEW_MIGRATION_LOGIC", true,
"Enable the new grid migration logic, keeping pages when src < dest");
@@ -285,21 +290,31 @@
public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
"ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
- public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION_FOLDER = new DeviceFlag(
- "SHOW_DELIGHTFUL_PAGINATION_FOLDER", false,
- "Enable showing the new 'delightful pagination'"
- + " which is a brand new animation for folder pagination");
-
+ public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = getDebugFlag(
+ "SHOW_DELIGHTFUL_PAGINATION", false,
+ "Enable showing the new 'delightful pagination' which is a brand"
+ + " new animation for folder pagination and workspace pagination");
public static final BooleanFlag POPUP_MATERIAL_U = new DeviceFlag(
"POPUP_MATERIAL_U", false, "Switch popup UX to use material U");
- public static final BooleanFlag SHOW_HOME_GARDENING = new DeviceFlag(
+ public static final BooleanFlag SHOW_HOME_GARDENING = getDebugFlag(
"SHOW_HOME_GARDENING", false,
"Show the new home gardening mode");
+ public static final BooleanFlag HOME_GARDENING_WORKSPACE_BUTTONS = getDebugFlag(
+ "HOME_GARDENING_WORKSPACE_BUTTONS", false,
+ "Change workspace edit buttons to reflect home gardening");
+
public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
"ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar.");
+ public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(
+ "SECONDARY_DRAG_N_DROP_TO_PIN", false,
+ "Enable dragging and dropping to pin apps within secondary display");
+
+ public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag(
+ "SHOW_DOT_PAGINATION", false, "Enable showing dot pagination in workspace");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 0bcb97a..efd511d 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -113,6 +113,7 @@
public void setFolder(Folder folder) {
mFolder = folder;
mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
+ mPageIndicator.setShouldAutoHide(false);
initParentViews(folder);
}
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index d5a79dd..26ddc0b 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -76,7 +76,8 @@
Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
context, info, LauncherAppState.getIDP(context).fillResIconDpi);
if (unbadgedDrawable == null) return BitmapInfo.LOW_RES_INFO;
- return new BitmapInfo(li.createScaledBitmapWithoutShadow(unbadgedDrawable),
+ return new BitmapInfo(
+ li.createScaledBitmap(unbadgedDrawable, BaseIconFactory.MODE_WITH_SHADOW),
Themes.getColorAccent(context));
}
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 22627b4..fcc5d86 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -555,6 +555,20 @@
+ "result page etc.")
LAUNCHER_ALLAPPS_SCROLLED(985),
+ @UiEvent(doc = "User scrolled up on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc.")
+ LAUNCHER_ALLAPPS_SCROLLED_UP(1229),
+
+ @UiEvent(doc =
+ "User scrolled down on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc.")
+ LAUNCHER_ALLAPPS_SCROLLED_DOWN(1230),
+
+ @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc and we don't know the direction since user came back to "
+ + "original position from which they scrolled.")
+ LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION(1231),
+
@UiEvent(doc = "User tapped taskbar home button")
LAUNCHER_TASKBAR_HOME_BUTTON_TAP(1003),
@@ -598,7 +612,16 @@
LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM(1199),
@UiEvent(doc = "User has invoked split to left half from an app icon menu")
- LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP(1200)
+ LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP(1200),
+
+ @UiEvent(doc = "Number of apps in A-Z list (personal and work profile)")
+ LAUNCHER_ALLAPPS_COUNT(1225),
+
+ @UiEvent(doc = "User has invoked split to right half with a keyboard shortcut.")
+ LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM(1232),
+
+ @UiEvent(doc = "User has invoked split to left half with a keyboard shortcut.")
+ LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP(1233)
;
// ADD MORE
@@ -720,6 +743,13 @@
}
/**
+ * Sets cardinality of log message.
+ */
+ default StatsLogger withCardinality(int cardinality) {
+ return this;
+ }
+
+ /**
* Builds the final message and logs it as {@link EventEnum}.
*/
default void log(EventEnum event) {
@@ -756,7 +786,6 @@
public int getId() {
return mId;
}
-
}
/**
@@ -789,6 +818,13 @@
}
/**
+ * Sets sub event type.
+ */
+ default StatsLatencyLogger withSubEventType(int type) {
+ return this;
+ }
+
+ /**
* Sets packageId of log message.
*/
default StatsLatencyLogger withPackageId(int packageId) {
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 0a68d4a..514e7b2 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -36,6 +36,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
+import com.android.launcher3.LauncherWidgetHolder;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.FileLog;
@@ -48,7 +49,6 @@
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.widget.LauncherAppWidgetHost;
import java.util.ArrayList;
import java.util.Arrays;
@@ -333,13 +333,13 @@
/**
* Deletes the widget info and the widget id.
*/
- public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host,
+ public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherWidgetHolder holder,
@Nullable final String reason) {
notifyDelete(Collections.singleton(info));
- if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
+ if (holder != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
- enqueueDeleteRunnable(() -> host.deleteAppWidgetId(info.appWidgetId));
+ enqueueDeleteRunnable(() -> holder.deleteAppWidgetId(info.appWidgetId));
}
deleteItemFromDatabase(info, reason);
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index b4cb0ee..e9b6606 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,7 +16,8 @@
package com.android.launcher3.pageindicators;
-import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION_FOLDER;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -29,15 +30,22 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Paint.Style;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.util.AttributeSet;
-import android.util.Property;
+import android.util.FloatProperty;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewOutlineProvider;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -47,11 +55,13 @@
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
*/
-public class PageIndicatorDots extends View implements PageIndicator {
+public class PageIndicatorDots extends View implements Insettable, PageIndicator {
private static final float SHIFT_PER_ANIMATION = 0.5f;
private static final float SHIFT_THRESHOLD = 0.1f;
private static final long ANIMATION_DURATION = 150;
+ private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
+ private static final int ALPHA_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
private static final int ENTER_ANIMATION_START_DELAY = 300;
private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
@@ -61,6 +71,9 @@
private static final int DOT_ALPHA = 128;
private static final int DOT_GAP_FACTOR = 3;
private static final float DOT_GAP_FACTOR_FLOAT = 3.8f;
+ private static final int VISIBLE_ALPHA = 1;
+ private static final int INVISIBLE_ALPHA = 0;
+ private Paint mPaginationPaint;
// This value approximately overshoots to 1.5 times the original size.
private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
@@ -69,22 +82,36 @@
private static final RectF sTempRect = new RectF();
- private static final Property<PageIndicatorDots, Float> CURRENT_POSITION
- = new Property<PageIndicatorDots, Float>(float.class, "current_position") {
- @Override
- public Float get(PageIndicatorDots obj) {
- return obj.mCurrentPosition;
- }
+ private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION =
+ new FloatProperty<PageIndicatorDots>("current_position") {
+ @Override
+ public Float get(PageIndicatorDots obj) {
+ return obj.mCurrentPosition;
+ }
- @Override
- public void set(PageIndicatorDots obj, Float pos) {
- obj.mCurrentPosition = pos;
- obj.invalidate();
- obj.invalidateOutline();
- }
- };
+ @Override
+ public void setValue(PageIndicatorDots obj, float pos) {
+ obj.mCurrentPosition = pos;
+ obj.invalidate();
+ obj.invalidateOutline();
+ }
+ };
- private final Paint mPaginationPaint;
+ private static final FloatProperty<PageIndicatorDots> PAGINATION_ALPHA =
+ new FloatProperty<PageIndicatorDots>("pagination_alpha") {
+ @Override
+ public Float get(PageIndicatorDots obj) {
+ return obj.getAlpha();
+ }
+
+ @Override
+ public void setValue(PageIndicatorDots obj, float alpha) {
+ obj.setAlpha(alpha);
+ obj.invalidate();
+ }
+ };
+
+ private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
private final Drawable mPageIndicatorDrawable;
private final float mDotRadius;
private final float mCircleGap;
@@ -96,6 +123,8 @@
private int mActivePage;
private int mCurrentScroll;
private int mTotalScroll;
+ private boolean mShouldAutoHide = true;
+ private int mToAlpha;
/**
* The current position of the active dot including the animation progress.
@@ -109,9 +138,12 @@
private float mCurrentPosition;
private float mFinalPosition;
private ObjectAnimator mAnimator;
+ private @Nullable ObjectAnimator mAlphaAnimator;
private float[] mEntryAnimationRadiusFactors;
+ private Runnable mHidePaginationRunnable = () -> animatePaginationToAlpha(INVISIBLE_ALPHA);
+
public PageIndicatorDots(Context context) {
this(context, null);
}
@@ -128,8 +160,7 @@
mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
-
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
mPageIndicatorSize = getResources().getDimension(
R.dimen.page_indicator_size);
mPageIndicatorRadius = mPageIndicatorSize / 2;
@@ -144,7 +175,7 @@
mPageIndicatorDrawable = null;
mCircleGap = DOT_GAP_FACTOR * mDotRadius;
}
- if (!SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (!SHOW_DELIGHTFUL_PAGINATION.get()) {
setOutlineProvider(new MyOutlineProver());
}
mIsRtl = Utilities.isRtl(getResources());
@@ -152,6 +183,10 @@
@Override
public void setScroll(int currentScroll, int totalScroll) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get() || SHOW_DOT_PAGINATION.get()) {
+ animatePaginationToAlpha(VISIBLE_ALPHA);
+ }
+
if (mNumPages <= 1) {
mCurrentScroll = 0;
return;
@@ -161,10 +196,15 @@
currentScroll = totalScroll - currentScroll;
}
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ mTotalScroll = totalScroll;
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
mCurrentScroll = currentScroll;
- mTotalScroll = totalScroll;
invalidate();
+
+ if (mShouldAutoHide
+ && (getScrollPerPage() == 0 || mCurrentScroll % getScrollPerPage() == 0)) {
+ hideAfterDelay();
+ }
return;
}
@@ -177,15 +217,79 @@
if (currentScroll < pageToLeftScroll + scrollThreshold) {
// scroll is within the left page's threshold
animateToPosition(pageToLeft);
+ if (SHOW_DOT_PAGINATION.get()) {
+ hideAfterDelay();
+ }
} else if (currentScroll > pageToRightScroll - scrollThreshold) {
// scroll is far enough from left page to go to the right page
animateToPosition(pageToLeft + 1);
+ if (SHOW_DOT_PAGINATION.get()) {
+ hideAfterDelay();
+ }
} else {
// scroll is between left and right page
animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
}
}
+ @Override
+ public void setShouldAutoHide(boolean shouldAutoHide) {
+ mShouldAutoHide = shouldAutoHide;
+ if (shouldAutoHide && this.getAlpha() > INVISIBLE_ALPHA) {
+ hideAfterDelay();
+ } else if (!shouldAutoHide) {
+ mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+ }
+ }
+
+ private void hideAfterDelay() {
+ mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
+ mDelayedPaginationFadeHandler.postDelayed(mHidePaginationRunnable, PAGINATION_FADE_DELAY);
+ }
+
+ private void animatePaginationToAlpha(int alpha) {
+ if (alpha == mToAlpha) {
+ // Ignore the new animation if it is going to the same alpha as the current animation.
+ return;
+ }
+ mToAlpha = alpha;
+
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.cancel();
+ }
+ mAlphaAnimator = ObjectAnimator.ofFloat(this, PAGINATION_ALPHA,
+ alpha);
+ mAlphaAnimator.setDuration(ALPHA_ANIMATE_DURATION);
+ mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAlphaAnimator = null;
+ }
+ });
+ mAlphaAnimator.start();
+
+ }
+
+ /**
+ * Pauses all currently running animations.
+ */
+ @Override
+ public void pauseAnimations() {
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.pause();
+ }
+ }
+
+ /**
+ * Force-ends all currently running or paused animations.
+ */
+ @Override
+ public void skipAnimationsToEnd() {
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.end();
+ }
+ }
+
private void animateToPosition(float position) {
mFinalPosition = position;
if (Math.abs(mCurrentPosition - mFinalPosition) < SHIFT_THRESHOLD) {
@@ -281,6 +385,10 @@
@Override
protected void onDraw(Canvas canvas) {
+ if ((mShouldAutoHide && mTotalScroll == 0) || mNumPages < 2) {
+ return;
+ }
+
// Draw all page indicators;
float circleGap = mCircleGap;
float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2;
@@ -296,7 +404,7 @@
}
for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA);
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
if (i != mActivePage) {
canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
mPaginationPaint);
@@ -313,7 +421,7 @@
// Here we draw the dots
mPaginationPaint.setAlpha(DOT_ALPHA);
for (int i = 0; i < mNumPages; i++) {
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
canvas.drawCircle(x, y, getRadius(x), mPaginationPaint);
} else {
canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
@@ -323,7 +431,7 @@
// Here we draw the current page indicator
mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA);
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
drawPageIndicator(canvas, 1);
} else {
canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
@@ -389,7 +497,7 @@
float diameter = 2 * mDotRadius;
float startX;
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset();
sTempRect.top = (getHeight() - mPageIndicatorSize) * 0.5f;
sTempRect.bottom = (getHeight() + mPageIndicatorSize) * 0.5f;
@@ -483,4 +591,12 @@
}
}
}
+
+ /**
+ * We need to override setInsets to prevent InsettableFrameLayout from applying different
+ * margins on the pagination.
+ */
+ @Override
+ public void setInsets(Rect insets) {
+ }
}
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 87ae890..bde4e52 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -14,12 +14,9 @@
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Property;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -258,21 +255,11 @@
}
}
+ /**
+ * We need to override setInsets to prevent InsettableFrameLayout from applying different
+ * margins on the page indicator.
+ */
@Override
public void setInsets(Rect insets) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-
- if (grid.isVerticalBarLayout()) {
- Rect padding = grid.workspacePadding;
- lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
- lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
- lp.bottomMargin = padding.bottom;
- } else {
- lp.leftMargin = lp.rightMargin = 0;
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.bottomMargin = grid.hotseatBarSizePx;
- }
- setLayoutParams(lp);
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
index a0ed77e..f03c62a 100644
--- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
+++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
@@ -168,15 +168,18 @@
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
- private void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
+ /**
+ * Pins or unpins apps from home screen
+ */
+ public void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
ComponentKey key = new ComponentKey(info.getTargetComponent(), info.user);
if (op.apply(key)) {
createFilteredAppsList();
Set<ComponentKey> copy = new HashSet<>(mPinnedApps);
Executors.MODEL_EXECUTOR.submit(() ->
mPrefs.edit().putStringSet(PINNED_APPS_KEY,
- copy.stream().map(this::encode).collect(Collectors.toSet()))
- .apply());
+ copy.stream().map(this::encode).collect(Collectors.toSet()))
+ .apply());
}
}
@@ -210,6 +213,13 @@
mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user)));
}
+ /**
+ * Pins app to home screen
+ */
+ public void addPinnedApp(ItemInfo info) {
+ update(info, mPinnedApps::add);
+ }
+
private class PinUnPinShortcut extends SystemShortcut<SecondaryDisplayLauncher> {
private final boolean mIsPinned;
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index a55f7e3..7b32d8b 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -18,6 +18,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
@@ -26,6 +29,9 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
@@ -33,6 +39,11 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.AppInfo;
@@ -52,11 +63,11 @@
* Launcher activity for secondary displays
*/
public class SecondaryDisplayLauncher extends BaseDraggingActivity
- implements BgDataModel.Callbacks {
+ implements BgDataModel.Callbacks, DragController.DragListener {
private LauncherModel mModel;
-
private BaseDragLayer mDragLayer;
+ private SecondaryDragController mDragController;
private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView;
private View mAppsButton;
@@ -69,10 +80,13 @@
private boolean mBindingItems = false;
private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
+ private final int[] mTempXY = new int[2];
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mModel = LauncherAppState.getInstance(this).getModel();
+ mDragController = new SecondaryDragController(this);
mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
if (getWindow().getDecorView().isAttachedToWindow()) {
@@ -86,6 +100,12 @@
initUi();
}
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ this.getDragController().removeDragListener(this);
+ }
+
private void initUi() {
if (mDragLayer != null) {
return;
@@ -106,6 +126,7 @@
mAppsView = findViewById(R.id.apps_view);
mAppsButton = findViewById(R.id.all_apps_button);
+ mDragController.addDragListener(this);
mPopupDataProvider = new PopupDataProvider(
mAppsView.getAppsStore()::updateNotificationDots);
@@ -113,6 +134,12 @@
}
@Override
+ protected void onPause() {
+ super.onPause();
+ mDragController.cancelDrag();
+ }
+
+ @Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -129,12 +156,21 @@
showAppDrawer(false);
}
+ public DragController getDragController() {
+ return mDragController;
+ }
+
@Override
public void onBackPressed() {
if (finishAutoCancelActionMode()) {
return;
}
+ if (mDragController.isDragging()) {
+ mDragController.cancelDrag();
+ return;
+ }
+
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -202,7 +238,7 @@
float closeR = Themes.getDialogCornerRadius(this);
float startR = mAppsButton.getWidth() / 2f;
- float[] buttonPos = new float[] { startR, startR};
+ float[] buttonPos = new float[]{startR, startR};
mDragLayer.getDescendantCoordRelativeToSelf(mAppsButton, buttonPos);
mDragLayer.mapCoordInSelfToDescendant(mAppsView, buttonPos);
final Animator animator = ViewAnimationUtils.createCircularReveal(mAppsView,
@@ -236,6 +272,7 @@
@Override
public void startBinding() {
mBindingItems = true;
+ mDragController.cancelDrag();
}
@Override
@@ -266,6 +303,10 @@
}
}
+ public SecondaryDisplayPredictions getSecondaryDisplayPredictions() {
+ return mSecondaryDisplayPredictions;
+ }
+
@Override
public StringCache getStringCache() {
return mStringCache;
@@ -308,4 +349,101 @@
startActivitySafely(v, intent, item);
}
}
+
+ /**
+ * Core functionality for beginning a drag operation for an item that will be dropped within
+ * the secondary display grid home screen
+ */
+ public void beginDragShared(View child, DragSource source, DragOptions options) {
+ Object dragObject = child.getTag();
+ if (!(dragObject instanceof ItemInfo)) {
+ String msg = "Drag started with a view that has no tag set. This "
+ + "will cause a crash (issue 11627249) down the line. "
+ + "View: " + child + " tag: " + child.getTag();
+ throw new IllegalStateException(msg);
+ }
+ beginDragShared(child, source, (ItemInfo) dragObject,
+ new DragPreviewProvider(child), options);
+ }
+
+ private void beginDragShared(View child, DragSource source,
+ ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions options) {
+
+ float iconScale = 1f;
+ if (child instanceof BubbleTextView) {
+ FastBitmapDrawable icon = ((BubbleTextView) child).getIcon();
+ if (icon != null) {
+ iconScale = icon.getAnimatedScale();
+ }
+ }
+
+ // clear pressed state if necessary
+ child.clearFocus();
+ child.setPressed(false);
+ if (child instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) child;
+ icon.clearPressedBackground();
+ }
+
+ DraggableView draggableView = null;
+ if (child instanceof DraggableView) {
+ draggableView = (DraggableView) child;
+ }
+
+ final View contentView = previewProvider.getContentView();
+ final float scale;
+ // The draggable drawable follows the touch point around on the screen
+ final Drawable drawable;
+ if (contentView == null) {
+ drawable = previewProvider.createDrawable();
+ scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
+ } else {
+ drawable = null;
+ scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
+ }
+ int halfPadding = previewProvider.previewPadding / 2;
+ int dragLayerX = mTempXY[0];
+ int dragLayerY = mTempXY[1];
+
+ Point dragVisualizeOffset = null;
+ Rect dragRect = new Rect();
+ if (draggableView != null) {
+ draggableView.getSourceVisualDragBounds(dragRect);
+ dragLayerY += dragRect.top;
+ dragVisualizeOffset = new Point(-halfPadding, halfPadding);
+ }
+ if (contentView != null) {
+ mDragController.startDrag(
+ contentView,
+ draggableView,
+ dragLayerX,
+ dragLayerY,
+ source,
+ dragObject,
+ dragVisualizeOffset,
+ dragRect,
+ scale * iconScale,
+ scale,
+ options);
+ } else {
+ mDragController.startDrag(
+ drawable,
+ draggableView,
+ dragLayerX,
+ dragLayerY,
+ source,
+ dragObject,
+ dragVisualizeOffset,
+ dragRect,
+ scale * iconScale,
+ scale,
+ options);
+ }
+ }
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
+
+ @Override
+ public void onDragEnd() { }
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
index a58916a..21c50d3 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
@@ -16,8 +16,10 @@
package com.android.launcher3.secondarydisplay;
import android.content.Context;
+import android.view.View;
import com.android.launcher3.R;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -45,4 +47,12 @@
*/
public void setPredictedApps(BgDataModel.FixedContainerItems item) {
}
+
+ /**
+ * Set long click listener for predicted apps in top of app drawer.
+ */
+ public void setLongClickListener(
+ ActivityAllAppsContainerView<?> appsView,
+ View.OnLongClickListener onIconLongClickListener) {
+ }
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
new file mode 100644
index 0000000..9bf2764
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
@@ -0,0 +1,194 @@
+/*
+ * 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.secondarydisplay;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.R;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragDriver;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.testing.shared.TestProtocol;
+
+/**
+ * Drag controller for Secondary Launcher activity
+ */
+public class SecondaryDragController extends DragController<SecondaryDisplayLauncher> {
+
+ private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+
+ public SecondaryDragController(SecondaryDisplayLauncher secondaryLauncher) {
+ super(secondaryLauncher);
+ }
+
+ @Override
+ protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
+ DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
+ ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
+ float dragViewScaleOnDrop, DragOptions options) {
+
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DROP_TARGET, "5");
+ }
+
+ if (PROFILE_DRAWING_DURING_DRAG) {
+ android.os.Debug.startMethodTracing("Launcher");
+ }
+ mActivity.hideKeyboard();
+
+ mOptions = options;
+ if (mOptions.simulatedDndStartPoint != null) {
+ mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
+ mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
+ }
+
+ final int registrationX = mMotionDown.x - dragLayerX;
+ final int registrationY = mMotionDown.y - dragLayerY;
+
+ final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+ final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+
+ mLastDropTarget = null;
+
+ mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
+ mDragObject.originalView = originalView;
+
+ mIsInPreDrag = mOptions.preDragCondition != null
+ && !mOptions.preDragCondition.shouldStartDrag(0);
+
+ final Resources res = mActivity.getResources();
+ final float scaleDps = mIsInPreDrag
+ ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+
+ final DragView dragView = mDragObject.dragView = drawable != null
+ ? new SecondaryDragView(
+ mActivity,
+ drawable,
+ registrationX,
+ registrationY,
+ initialDragViewScale,
+ dragViewScaleOnDrop,
+ scaleDps)
+ : new SecondaryDragView(
+ mActivity,
+ view,
+ view.getMeasuredWidth(),
+ view.getMeasuredHeight(),
+ registrationX,
+ registrationY,
+ initialDragViewScale,
+ dragViewScaleOnDrop,
+ scaleDps);
+ dragView.setItemInfo(dragInfo);
+ mDragObject.dragComplete = false;
+
+ mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+ mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+ mDragDriver = DragDriver.create(this, mOptions, ev -> {
+ });
+ if (!mOptions.isAccessibleDrag) {
+ mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+ }
+
+ mDragObject.dragSource = source;
+ mDragObject.dragInfo = dragInfo;
+ mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
+
+ if (dragOffset != null) {
+ dragView.setDragVisualizeOffset(new Point(dragOffset));
+ }
+ if (dragRegion != null) {
+ dragView.setDragRegion(new Rect(dragRegion));
+ }
+
+ mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ dragView.show(mLastTouch.x, mLastTouch.y);
+ mDistanceSinceScroll = 0;
+
+ if (!mIsInPreDrag) {
+ callOnDragStart();
+ } else if (mOptions.preDragCondition != null) {
+ mOptions.preDragCondition.onPreDragStart(mDragObject);
+ }
+
+ handleMoveEvent(mLastTouch.x, mLastTouch.y);
+ return dragView;
+ }
+
+ @Override
+ protected void exitDrag() { }
+
+ @Override
+ protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+ DropTarget target = new DropTarget() {
+ @Override
+ public boolean isDropEnabled() {
+ return true;
+ }
+
+ @Override
+ public void onDrop(DragObject dragObject, DragOptions options) {
+ ((SecondaryDragLayer) mActivity.getDragLayer()).getPinnedAppsAdapter().addPinnedApp(
+ dragObject.dragInfo);
+ dragObject.dragView.remove();
+ }
+
+ @Override
+ public void onDragEnter(DragObject dragObject) {
+ if (getDistanceDragged() > mActivity.getResources().getDimensionPixelSize(
+ R.dimen.drag_distanceThreshold)) {
+ mActivity.showAppDrawer(false);
+ AbstractFloatingView.closeAllOpenViews(mActivity);
+ }
+ }
+
+ @Override
+ public void onDragOver(DragObject dragObject) { }
+
+ @Override
+ public void onDragExit(DragObject dragObject) { }
+
+ @Override
+ public boolean acceptDrop(DragObject dragObject) {
+ return true;
+ }
+
+ @Override
+ public void prepareAccessibilityDrop() { }
+
+ @Override
+ public void getHitRectRelativeToDragLayer(Rect outRect) { }
+ };
+ return target;
+ }
+}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index c79d70d..5eac01e 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -29,8 +29,12 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
@@ -59,7 +63,8 @@
@Override
public void recreateControllers() {
- mControllers = new TouchController[] {new CloseAllAppsTouchController()};
+ mControllers = new TouchController[]{new CloseAllAppsTouchController(),
+ mActivity.getDragController()};
}
/**
@@ -72,7 +77,8 @@
mAppsView = findViewById(R.id.apps_view);
mAppsView.setOnIconLongClickListener(this::onIconLongClicked);
-
+ mActivity.getSecondaryDisplayPredictions()
+ .setLongClickListener(mAppsView, this::onIconLongClicked);
// Setup workspace
mWorkspace = findViewById(R.id.workspace_grid);
mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
@@ -166,6 +172,10 @@
}
}
+ public PinnedAppsAdapter getPinnedAppsAdapter() {
+ return mPinnedAppsAdapter;
+ }
+
private boolean onIconLongClicked(View v) {
if (!(v instanceof BubbleTextView)) {
return false;
@@ -183,6 +193,7 @@
if (popupDataProvider == null) {
return false;
}
+
final PopupContainerWithArrow container =
(PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
R.layout.popup_container, mActivity.getDragLayer(), false);
@@ -192,7 +203,42 @@
Collections.emptyList(),
Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v),
APP_INFO.getShortcut(mActivity, item, v)));
- v.getParent().requestDisallowInterceptTouchEvent(true);
+ container.requestFocus();
+
+ if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
+ return true;
+ }
+
+ DragOptions options = new DragOptions();
+ DeviceProfile grid = mActivity.getDeviceProfile();
+ options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ options.preDragCondition = container.createPreDragCondition(false);
+ if (options.preDragCondition == null) {
+ options.preDragCondition = new DragOptions.PreDragCondition() {
+ private DragView<SecondaryDisplayLauncher> mDragView;
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ return mDragView != null && mDragView.isAnimationFinished();
+ }
+
+ @Override
+ public void onPreDragStart(DropTarget.DragObject dragObject) {
+ mDragView = dragObject.dragView;
+ if (!shouldStartDrag(0)) {
+ mDragView.setOnAnimationEndCallback(() -> {
+ mActivity.beginDragShared(v, mActivity.getAppsView(), options);
+ });
+ }
+ }
+
+ @Override
+ public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+ mDragView = null;
+ }
+ };
+ }
+ mActivity.beginDragShared(v, mActivity.getAppsView(), options);
return true;
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
new file mode 100644
index 0000000..0168b8f
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
@@ -0,0 +1,66 @@
+/*
+ * 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.secondarydisplay;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragView;
+
+/**
+ * A DragView drawn/used by the Secondary Launcher activity.
+ */
+public class SecondaryDragView extends DragView<SecondaryDisplayLauncher> {
+
+ public SecondaryDragView(SecondaryDisplayLauncher launcher,
+ Drawable drawable,
+ int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+ float finalScaleDps) {
+ super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
+ finalScaleDps);
+ }
+
+ public SecondaryDragView(SecondaryDisplayLauncher launcher, View content, int width, int height,
+ int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+ float finalScaleDps) {
+ super(launcher, content, width, height, registrationX, registrationY, initialScale,
+ scaleOnDrop, finalScaleDps);
+ }
+
+ @Override
+ public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+ Runnable onAnimationEnd = () -> {
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ mActivity.getDragLayer().removeView(this);
+ };
+
+ duration = Math.max(duration,
+ getResources().getInteger(R.integer.config_dropAnimMinDuration));
+
+ animate()
+ .translationX(toTouchX - mRegistrationX)
+ .translationY(toTouchY - mRegistrationY)
+ .scaleX(mScaleOnDrop)
+ .scaleY(mScaleOnDrop)
+ .withEndAction(onAnimationEnd)
+ .setDuration(duration)
+ .start();
+ }
+}
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 2a890c3..520f33c 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -231,4 +231,10 @@
* etc.)
*/
protected abstract void onHandleConfigurationChanged();
+
+ /**
+ * Enter staged split directly from the current running app.
+ * @param leftOrTop if the staged split will be positioned left or top.
+ */
+ public void enterStageSplitFromRunningApp(boolean leftOrTop) { }
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d3c9bc9..fdd30e1 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -112,12 +112,12 @@
case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
- l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
+ l -> l.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset());
}
case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
- l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+ l -> WidgetsFullSheet.getWidgetsView(l).computeVerticalScrollOffset());
}
case TestProtocol.REQUEST_TARGET_INSETS: {
@@ -219,6 +219,11 @@
return response;
}
+ case TestProtocol.REQUEST_ALL_APPS_TOP_PADDING: {
+ return getLauncherUIProperty(Bundle::putInt,
+ l -> l.getAppsView().getActiveRecyclerView().getClipBounds().top);
+ }
+
default:
return null;
}
diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java
index 5116b01..792d475 100644
--- a/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -112,6 +112,9 @@
"get-activities-created-count";
public static final String REQUEST_GET_ACTIVITIES = "get-activities";
public static final String REQUEST_HAS_TIS = "has-touch-interaction-service";
+ public static final String REQUEST_TASKBAR_ALL_APPS_TOP_PADDING =
+ "taskbar-all-apps-top-padding";
+ public static final String REQUEST_ALL_APPS_TOP_PADDING = "all-apps-top-padding";
public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size";
public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center";
@@ -124,6 +127,7 @@
"get-grid-task-size-rect-for-tablet";
public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
+ public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index ceebc2e..820162c 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -30,6 +30,7 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.content.res.Resources;
@@ -265,20 +266,32 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll,
- DeviceProfile deviceProfile) {
- return thumbnailView.getMeasuredWidth() + x;
+ public float getTaskMenuX(float x, View thumbnailView,
+ DeviceProfile deviceProfile, float taskInsetMargin) {
+ return thumbnailView.getMeasuredWidth() + x - taskInsetMargin;
}
@Override
- public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y + overScroll +
- (thumbnailView.getMeasuredHeight() - thumbnailView.getMeasuredWidth()) / 2f;
+ public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin) {
+ BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
+ int taskMenuWidth = lp.width;
+ if (stagePosition == STAGE_POSITION_UNDEFINED) {
+ return y + taskInsetMargin
+ + (thumbnailView.getMeasuredHeight() - taskMenuWidth) / 2f;
+ } else {
+ return y + taskInsetMargin;
+ }
}
@Override
- public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
- return view.getMeasuredWidth();
+ public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+ @StagePosition int stagePosition) {
+ if (stagePosition == SplitConfigurationOptions.STAGE_POSITION_UNDEFINED) {
+ return thumbnailView.getMeasuredWidth();
+ } else {
+ return thumbnailView.getMeasuredHeight();
+ }
}
@Override
@@ -300,17 +313,6 @@
}
@Override
- public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
- lp.topMargin += margin;
- }
-
- @Override
- public PointF getAdditionalInsetForTaskMenu(float margin) {
- return new PointF(margin, 0);
- }
-
- @Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
@@ -376,19 +378,6 @@
return isRtl ? 1 : -1;
}
- @Override
- public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
- DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
- float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
- ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
- : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
- FrameLayout.LayoutParams snapshotParams =
- (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
- float additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
- * topLeftTaskPlusDividerPercent;
- taskMenuView.setY(taskMenuView.getY() + additionalOffset);
- }
-
/* -------------------- */
@Override
@@ -424,8 +413,8 @@
// In fake land/seascape, the placeholder always needs to go to the "top" of the device,
// which is the same bounds as 0 rotation.
int width = dp.widthPx;
- int insetThickness = dp.getInsets().top;
- out.set(0, 0, width, placeholderHeight + insetThickness);
+ int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp);
+ out.set(0, 0, width, placeholderHeight + insetSizeAdjustment);
out.inset(placeholderInset, 0);
// Adjust the top to account for content off screen. This will help to animate the view in
@@ -442,13 +431,21 @@
float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
- float inset = dp.getInsets().top;
+ float insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f;
out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
- 1.0f * drawableWidth / 2));
- out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+ out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
- 1.0f * drawableHeight / 2));
}
+ /**
+ * The split placeholder comes with a default inset to buffer the icon from the top of the
+ * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+ */
+ private int getPlaceholderSizeAdjustment(DeviceProfile dp) {
+ return Math.max(dp.getInsets().top - dp.splitPlaceholderInset, 0);
+ }
+
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index cbcb700..6234462 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -184,9 +184,12 @@
* taskMenu width is the same size as the thumbnail width (what got set below in
* getTaskMenuWidth()), so we directly use that in the calculations.
*/
- float getTaskMenuX(float x, View thumbnailView, int overScroll, DeviceProfile deviceProfile);
- float getTaskMenuY(float y, View thumbnailView, int overScroll);
- int getTaskMenuWidth(View view, DeviceProfile deviceProfile);
+ float getTaskMenuX(float x, View thumbnailView, DeviceProfile deviceProfile,
+ float taskInsetMargin);
+ float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin);
+ int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+ @StagePosition int stagePosition);
/**
* Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items
* inside task menu view.
@@ -200,16 +203,6 @@
*/
void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
LinearLayout viewGroup, DeviceProfile deviceProfile);
- /**
- * Adjusts margins for the entire task menu view itself, which comprises of both app title and
- * shortcut options.
- */
- void setTaskMenuAroundTaskView(LinearLayout taskView, float margin);
- /**
- * Since the task menu layout is manually positioned on top of recents view, this method returns
- * additional adjustments to the positioning based on fake land/seascape
- */
- PointF getAdditionalInsetForTaskMenu(float margin);
/**
* Calculates the position where a Digital Wellbeing Banner should be placed on its parent
@@ -231,14 +224,6 @@
int getTaskDragDisplacementFactor(boolean isRtl);
/**
- * Calls the corresponding {@link View#setX(float)} or {@link View#setY(float)}
- * on {@param taskMenuView} by taking the space needed by {@param primarySnapshotView} into
- * account.
- * This is expected to only be called for secondary (bottom/right) tasks.
- */
- void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
- DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView);
- /**
* Maps the velocity from the coordinate plane of the foreground app to that
* of Launcher's (which now will always be portrait)
*/
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 5efebaa..af689dc 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -56,7 +56,6 @@
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.views.BaseDragLayer;
import java.util.List;
@@ -265,26 +264,28 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll,
- DeviceProfile deviceProfile) {
+ public float getTaskMenuX(float x, View thumbnailView,
+ DeviceProfile deviceProfile, float taskInsetMargin) {
if (deviceProfile.isLandscape) {
- return x + overScroll
+ return x + taskInsetMargin
+ (thumbnailView.getMeasuredWidth() - thumbnailView.getMeasuredHeight()) / 2f;
} else {
- return x + overScroll;
+ return x + taskInsetMargin;
}
}
@Override
- public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y;
+ public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin) {
+ return y + taskInsetMargin;
}
@Override
- public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
+ public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
+ @StagePosition int stagePosition) {
return deviceProfile.isLandscape && !deviceProfile.isTablet
- ? view.getMeasuredHeight()
- : view.getMeasuredWidth();
+ ? thumbnailView.getMeasuredHeight()
+ : thumbnailView.getMeasuredWidth();
}
@Override
@@ -305,38 +306,6 @@
}
@Override
- public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
- lp.topMargin += margin;
- lp.leftMargin += margin;
- }
-
- @Override
- public PointF getAdditionalInsetForTaskMenu(float margin) {
- return new PointF(0, 0);
- }
-
- @Override
- public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
- DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
- float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
- ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
- : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
- FrameLayout.LayoutParams snapshotParams =
- (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
- float additionalOffset;
- if (deviceProfile.isLandscape) {
- additionalOffset = (taskView.getWidth() - snapshotParams.leftMargin)
- * topLeftTaskPlusDividerPercent;
- taskMenuView.setX(taskMenuView.getX() + additionalOffset);
- } else {
- additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
- * topLeftTaskPlusDividerPercent;
- taskMenuView.setY(taskMenuView.getY() + additionalOffset);
- }
- }
-
- @Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
@@ -445,13 +414,9 @@
int screenWidth = dp.widthPx;
int screenHeight = dp.heightPx;
boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
- int insetThickness;
- if (!dp.isLandscape) {
- insetThickness = dp.getInsets().top;
- } else {
- insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
- }
- out.set(0, 0, screenWidth, placeholderHeight + insetThickness);
+ int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight);
+
+ out.set(0, 0, screenWidth, placeholderHeight + insetSizeAdjustment);
if (!dp.isLandscape) {
// portrait, phone or tablet - spans width of screen, nothing else to do
out.inset(placeholderInset, 0);
@@ -496,20 +461,18 @@
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+ float insetAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight) / 2f;
if (!dp.isLandscape) {
- float inset = dp.getInsets().top;
out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
- 1.0f * drawableWidth / 2));
- out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+ out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
- 1.0f * drawableHeight / 2));
} else {
if (pinToRight) {
- float inset = dp.getInsets().right;
- out.setX(Math.round((onScreenRectCenterX - (inset / 2f)) / fullscreenScaleX
+ out.setX(Math.round((onScreenRectCenterX - insetAdjustment) / fullscreenScaleX
- 1.0f * drawableWidth / 2));
} else {
- float inset = dp.getInsets().left;
- out.setX(Math.round((onScreenRectCenterX + (inset / 2f)) / fullscreenScaleX
+ out.setX(Math.round((onScreenRectCenterX + insetAdjustment) / fullscreenScaleX
- 1.0f * drawableWidth / 2));
}
out.setY(Math.round(onScreenRectCenterY / fullscreenScaleY
@@ -517,6 +480,20 @@
}
}
+ /**
+ * The split placeholder comes with a default inset to buffer the icon from the top of the
+ * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+ */
+ private int getPlaceholderSizeAdjustment(DeviceProfile dp, boolean pinToRight) {
+ int insetThickness;
+ if (!dp.isLandscape) {
+ insetThickness = dp.getInsets().top;
+ } else {
+ insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
+ }
+ return Math.max(insetThickness - dp.splitPlaceholderInset, 0);
+ }
+
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index a616a8b..05683bd 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.content.res.Resources;
@@ -33,7 +34,6 @@
import android.view.Surface;
import android.view.View;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -85,26 +85,22 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll,
- DeviceProfile deviceProfile) {
- return x;
+ public float getTaskMenuX(float x, View thumbnailView,
+ DeviceProfile deviceProfile, float taskInsetMargin) {
+ return x + taskInsetMargin;
}
@Override
- public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y + overScroll +
- (thumbnailView.getMeasuredHeight() + thumbnailView.getMeasuredWidth()) / 2f;
- }
-
- @Override
- public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
- lp.bottomMargin += margin;
- }
-
- @Override
- public PointF getAdditionalInsetForTaskMenu(float margin) {
- return new PointF(-margin, margin);
+ public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
+ View taskMenuView, float taskInsetMargin) {
+ BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
+ int taskMenuWidth = lp.width;
+ if (stagePosition == STAGE_POSITION_UNDEFINED) {
+ return y + taskInsetMargin
+ + (thumbnailView.getMeasuredHeight() + taskMenuWidth) / 2f;
+ } else {
+ return y + taskMenuWidth + taskInsetMargin;
+ }
}
@Override
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index e57c88d..f9f7ac0 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TRANSIENT_TASKBAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
@@ -123,6 +124,14 @@
return INSTANCE.get(context).getInfo().navigationMode;
}
+ /**
+ * Returns whether taskbar is transient.
+ */
+ public static boolean isTransientTaskbar(Context context) {
+ return ENABLE_TRANSIENT_TASKBAR.get()
+ && getNavigationMode(context) == NavigationMode.NO_BUTTON;
+ }
+
@Override
public void close() {
mDestroyed = true;
diff --git a/src/com/android/launcher3/util/MultiPropertyFactory.java b/src/com/android/launcher3/util/MultiPropertyFactory.java
index 43daf08..f34c4c2 100644
--- a/src/com/android/launcher3/util/MultiPropertyFactory.java
+++ b/src/com/android/launcher3/util/MultiPropertyFactory.java
@@ -16,10 +16,13 @@
package com.android.launcher3.util;
-import android.util.ArrayMap;
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
import android.util.FloatProperty;
import android.util.Log;
-import android.util.Property;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Allows to combine multiple values set by several sources.
@@ -35,15 +38,30 @@
*/
public class MultiPropertyFactory<T> {
+ public static final FloatProperty<MultiPropertyFactory<?>.MultiProperty> MULTI_PROPERTY_VALUE =
+ new FloatProperty<MultiPropertyFactory<?>.MultiProperty>("value") {
+
+ @Override
+ public Float get(MultiPropertyFactory<?>.MultiProperty property) {
+ return property.mValue;
+ }
+
+ @Override
+ public void setValue(MultiPropertyFactory<?>.MultiProperty property, float value) {
+ property.setValue(value);
+ }
+ };
+
private static final boolean DEBUG = false;
private static final String TAG = "MultiPropertyFactory";
- private final String mName;
- private final ArrayMap<Integer, MultiProperty> mProperties = new ArrayMap<>();
+ private final MultiPropertyFactory<?>.MultiProperty[] mProperties;
// This is an optimization for cases when set is called repeatedly with the same setterIndex.
private float mAggregationOfOthers = 0f;
- private Integer mLastIndexSet = -1;
- private final Property<T, Float> mProperty;
+ private int mLastIndexSet = -1;
+
+ protected final T mTarget;
+ private final FloatProperty<T> mProperty;
private final FloatBiFunction mAggregator;
/**
@@ -56,59 +74,99 @@
float apply(float a, float b);
}
- public MultiPropertyFactory(String name, Property<T, Float> property,
+ public MultiPropertyFactory(T target, FloatProperty<T> property, int size,
FloatBiFunction aggregator) {
- mName = name;
+ this(target, property, size, aggregator, 0);
+ }
+
+ public MultiPropertyFactory(T target, FloatProperty<T> property, int size,
+ FloatBiFunction aggregator, float defaultPropertyValue) {
+ mTarget = target;
mProperty = property;
mAggregator = aggregator;
+
+ mProperties = new MultiPropertyFactory<?>.MultiProperty[size];
+ for (int i = 0; i < size; i++) {
+ mProperties[i] = new MultiProperty(i, defaultPropertyValue);
+ }
}
/** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
- public MultiProperty get(Integer index) {
- return mProperties.computeIfAbsent(index,
- (k) -> new MultiProperty(index, mName + "_" + index));
+ public MultiProperty get(int index) {
+ return (MultiProperty) mProperties[index];
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(mProperties);
+ }
+
+ /**
+ * Dumps the alpha channel values to the given PrintWriter
+ *
+ * @param prefix String to be used before every line
+ * @param pw PrintWriter where the logs should be dumped
+ * @param label String used to help identify this object
+ * @param alphaIndexLabels Strings that represent each alpha channel, these should be entered
+ * in the order of the indexes they represent, starting from 0.
+ */
+ public void dump(String prefix, PrintWriter pw, String label, String... alphaIndexLabels) {
+ pw.println(prefix + label);
+
+ String innerPrefix = prefix + '\t';
+ for (int i = 0; i < alphaIndexLabels.length; i++) {
+ if (i >= mProperties.length) {
+ pw.println(innerPrefix + alphaIndexLabels[i] + " given for alpha index " + i
+ + " however there are only " + mProperties.length + " alpha channels.");
+ continue;
+ }
+ pw.println(innerPrefix + alphaIndexLabels[i] + "=" + get(i).getValue());
+ }
}
/**
* Each [setValue] will be aggregated with the other properties values created by the
* corresponding factory.
*/
- class MultiProperty extends FloatProperty<T> {
- private final int mInx;
- private float mValue = 0f;
+ public class MultiProperty {
- MultiProperty(int inx, String name) {
- super(name);
+ private final int mInx;
+ private final float mDefaultValue;
+ private float mValue;
+
+ MultiProperty(int inx, float defaultValue) {
mInx = inx;
+ mDefaultValue = defaultValue;
+ mValue = defaultValue;
}
- @Override
- public void setValue(T obj, float newValue) {
+ public void setValue(float newValue) {
if (mLastIndexSet != mInx) {
- mAggregationOfOthers = 0f;
- mProperties.forEach((key, property) -> {
- if (key != mInx) {
+ mAggregationOfOthers = mDefaultValue;
+ for (MultiPropertyFactory<?>.MultiProperty other : mProperties) {
+ if (other.mInx != mInx) {
mAggregationOfOthers =
- mAggregator.apply(mAggregationOfOthers, property.mValue);
+ mAggregator.apply(mAggregationOfOthers, other.mValue);
}
- });
+ }
+
mLastIndexSet = mInx;
}
float lastAggregatedValue = mAggregator.apply(mAggregationOfOthers, newValue);
mValue = newValue;
- apply(obj, lastAggregatedValue);
+ apply(lastAggregatedValue);
if (DEBUG) {
- Log.d(TAG, "name=" + mName
- + " newValue=" + newValue + " mInx=" + mInx
- + " aggregated=" + lastAggregatedValue + " others= " + mProperties);
+ Log.d(TAG, "name=" + mProperty.getName()
+ + " target=" + mTarget.getClass()
+ + " newValue=" + newValue
+ + " mInx=" + mInx
+ + " aggregated=" + lastAggregatedValue
+ + " others= " + Arrays.deepToString(mProperties));
}
}
- @Override
- public Float get(T object) {
- // Callers of MultiProperty should only care about the sub-property that it sets. If
- // the overall value is needed, mProperty.get should be called directly.
+ public float getValue() {
return mValue;
}
@@ -116,9 +174,19 @@
public String toString() {
return String.valueOf(mValue);
}
+
+ /**
+ * Creates and returns an Animator from the current value to the given value. Future
+ * animator on the same target automatically cancels the previous one.
+ */
+ public Animator animateToValue(float value) {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(this, MULTI_PROPERTY_VALUE, value);
+ animator.setAutoCancel(true);
+ return animator;
+ }
}
- protected void apply(T object, float value) {
- mProperty.set(object, value);
+ protected void apply(float value) {
+ mProperty.set(mTarget, value);
}
}
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index 4b46a0a..ac016a8 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -16,62 +16,24 @@
package com.android.launcher3.util;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.util.FloatProperty;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+
import android.view.View;
import com.android.launcher3.anim.AlphaUpdateListener;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.function.Consumer;
-
/**
* Utility class to handle separating a single value as a factor of multiple values
*/
-public class MultiValueAlpha {
+public class MultiValueAlpha extends MultiPropertyFactory<View> {
- public static final FloatProperty<AlphaProperty> VALUE =
- new FloatProperty<AlphaProperty>("value") {
+ private static final FloatBiFunction ALPHA_AGGREGATOR = (a, b) -> a * b;
- @Override
- public Float get(AlphaProperty alphaProperty) {
- return alphaProperty.mValue;
- }
-
- @Override
- public void setValue(AlphaProperty object, float value) {
- object.setValue(value);
- }
- };
-
- private final View mView;
- private final AlphaProperty[] mMyProperties;
-
- private int mValidMask;
// Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values.
private boolean mUpdateVisibility;
public MultiValueAlpha(View view, int size) {
- mView = view;
- mMyProperties = new AlphaProperty[size];
-
- mValidMask = 0;
- for (int i = 0; i < size; i++) {
- int myMask = 1 << i;
- mValidMask |= myMask;
- mMyProperties[i] = new AlphaProperty(myMask);
- }
- }
-
- @Override
- public String toString() {
- return Arrays.toString(mMyProperties);
- }
-
- public AlphaProperty getProperty(int index) {
- return mMyProperties[index];
+ super(view, VIEW_ALPHA, size, ALPHA_AGGREGATOR, 1f);
}
/** Sets whether we should update between INVISIBLE and VISIBLE based on alpha. */
@@ -79,97 +41,11 @@
mUpdateVisibility = updateVisibility;
}
- /**
- * Dumps the alpha channel values to the given PrintWriter
- *
- * @param prefix String to be used before every line
- * @param pw PrintWriter where the logs should be dumped
- * @param label String used to help identify this object
- * @param alphaIndexLabels Strings that represent each alpha channel, these should be entered
- * in the order of the indexes they represent, starting from 0.
- */
- public void dump(String prefix, PrintWriter pw, String label, String... alphaIndexLabels) {
- pw.println(prefix + label);
-
- String innerPrefix = prefix + '\t';
- for (int i = 0; i < alphaIndexLabels.length; i++) {
- if (i >= mMyProperties.length) {
- pw.println(innerPrefix + alphaIndexLabels[i] + " given for alpha index " + i
- + " however there are only " + mMyProperties.length + " alpha channels.");
- continue;
- }
- pw.println(innerPrefix + alphaIndexLabels[i] + "=" + getProperty(i).getValue());
- }
- }
-
- public class AlphaProperty {
-
- private final int mMyMask;
-
- private float mValue = 1;
- // Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
- private float mOthers = 1;
-
- private Consumer<Float> mConsumer;
-
- AlphaProperty(int myMask) {
- mMyMask = myMask;
- }
-
- public void setValue(float value) {
- if (mValue == value) {
- return;
- }
-
- if ((mValidMask & mMyMask) == 0) {
- // Our cache value is not correct, recompute it.
- mOthers = 1;
- for (AlphaProperty prop : mMyProperties) {
- if (prop != this) {
- mOthers *= prop.mValue;
- }
- }
- }
-
- // Since we have changed our value, all other caches except our own need to be
- // recomputed. Change mValidMask to indicate the new valid caches (only our own).
- mValidMask = mMyMask;
- mValue = value;
-
- final float alpha = mOthers * mValue;
- mView.setAlpha(alpha);
- if (mUpdateVisibility) {
- AlphaUpdateListener.updateVisibility(mView);
- }
- if (mConsumer != null) {
- mConsumer.accept(mValue);
- }
- }
-
- public float getValue() {
- return mValue;
- }
-
- public void setConsumer(Consumer<Float> consumer) {
- mConsumer = consumer;
- if (mConsumer != null) {
- mConsumer.accept(mValue);
- }
- }
-
- @Override
- public String toString() {
- return Float.toString(mValue);
- }
-
- /**
- * Creates and returns an Animator from the current value to the given value. Future
- * animator on the same target automatically cancels the previous one.
- */
- public Animator animateToValue(float value) {
- ObjectAnimator animator = ObjectAnimator.ofFloat(this, VALUE, value);
- animator.setAutoCancel(true);
- return animator;
+ @Override
+ protected void apply(float value) {
+ super.apply(value);
+ if (mUpdateVisibility) {
+ AlphaUpdateListener.updateVisibility(mTarget);
}
}
}
diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
new file mode 100644
index 0000000..9bc4ddc
--- /dev/null
+++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java
@@ -0,0 +1,188 @@
+/*
+ * 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.util;
+
+import android.content.Context;
+import android.util.SparseIntArray;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.State;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Extension of {@link GridLayoutManager} with support for smooth scrolling
+ */
+public class ScrollableLayoutManager extends GridLayoutManager {
+
+ // keyed on item type
+ protected final SparseIntArray mCachedSizes = new SparseIntArray();
+
+ private RecyclerView mRv;
+
+ /**
+ * Precalculated total height keyed on the item position. This is always incremental.
+ * Subclass can override {@link #incrementTotalHeight} to incorporate the layout logic.
+ * For example all-apps should have same values for items in same row,
+ * sample values: 0, 10, 10, 10, 10, 20, 20, 20, 20
+ * whereas widgets will have strictly increasing values
+ * sample values: 0, 10, 50, 60, 110
+ */
+ private int[] mTotalHeightCache = new int[1];
+ private int mLastValidHeightIndex = 0;
+
+ public ScrollableLayoutManager(Context context) {
+ super(context, 1, GridLayoutManager.VERTICAL, false);
+ }
+
+ @Override
+ public void onAttachedToWindow(RecyclerView view) {
+ super.onAttachedToWindow(view);
+ mRv = view;
+ }
+
+ @Override
+ public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
+ super.layoutDecorated(child, left, top, right, bottom);
+ updateCachedSize(child);
+ }
+
+ @Override
+ public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
+ int bottom) {
+ super.layoutDecoratedWithMargins(child, left, top, right, bottom);
+ updateCachedSize(child);
+ }
+
+ private void updateCachedSize(@NonNull View child) {
+ int viewType = mRv.getChildViewHolder(child).getItemViewType();
+ int size = child.getMeasuredHeight();
+ if (mCachedSizes.get(viewType, -1) != size) {
+ invalidateScrollCache();
+ }
+ mCachedSizes.put(viewType, size);
+ }
+
+ @Override
+ public int computeVerticalScrollExtent(State state) {
+ return mRv == null ? 0 : mRv.getHeight();
+ }
+
+ @Override
+ public int computeVerticalScrollOffset(State state) {
+ Adapter adapter = mRv == null ? null : mRv.getAdapter();
+ if (adapter == null) {
+ return 0;
+ }
+ if (adapter.getItemCount() == 0 || getChildCount() == 0) {
+ return 0;
+ }
+ View child = getChildAt(0);
+ ViewHolder holder = mRv.findContainingViewHolder(child);
+ if (holder == null) {
+ return 0;
+ }
+ int itemPosition = holder.getLayoutPosition();
+ if (itemPosition < 0) {
+ return 0;
+ }
+ return getPaddingTop() + getItemsHeight(adapter, itemPosition) - getDecoratedTop(child);
+ }
+
+ @Override
+ public int computeVerticalScrollRange(State state) {
+ Adapter adapter = mRv == null ? null : mRv.getAdapter();
+ return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount());
+ }
+
+ /**
+ * Returns the sum of the height, in pixels, of this list adapter's items from index
+ * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
+ * it returns the full height of all the items.
+ *
+ * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+ * sum of all items' height.
+ */
+ private int getItemsHeight(Adapter adapter, int untilIndex) {
+ final int totalItems = adapter.getItemCount();
+ if (mTotalHeightCache.length < (totalItems + 1)) {
+ mTotalHeightCache = new int[totalItems + 1];
+ mLastValidHeightIndex = 0;
+ }
+ if (untilIndex > totalItems) {
+ untilIndex = totalItems;
+ } else if (untilIndex < 0) {
+ untilIndex = 0;
+ }
+ if (untilIndex <= mLastValidHeightIndex) {
+ return mTotalHeightCache[untilIndex];
+ }
+
+ int totalItemsHeight = mTotalHeightCache[mLastValidHeightIndex];
+ for (int i = mLastValidHeightIndex; i < untilIndex; i++) {
+ totalItemsHeight = incrementTotalHeight(adapter, i, totalItemsHeight);
+ mTotalHeightCache[i + 1] = totalItemsHeight;
+ }
+ mLastValidHeightIndex = untilIndex;
+ return totalItemsHeight;
+ }
+
+ /**
+ * The current implementation assumes a linear list with every item taking up the whole row.
+ * Subclasses should override this method to account for any spanning logic
+ */
+ protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+ return heightUntilLastPos + mCachedSizes.get(adapter.getItemViewType(position));
+ }
+
+ private void invalidateScrollCache() {
+ mLastValidHeightIndex = 0;
+ }
+
+ @Override
+ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
+ super.onItemsAdded(recyclerView, positionStart, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsChanged(RecyclerView recyclerView) {
+ super.onItemsChanged(recyclerView);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
+ super.onItemsRemoved(recyclerView, positionStart, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
+ super.onItemsMoved(recyclerView, from, to, itemCount);
+ invalidateScrollCache();
+ }
+
+ @Override
+ public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
+ Object payload) {
+ super.onItemsUpdated(recyclerView, positionStart, itemCount, payload);
+ invalidateScrollCache();
+ }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 88e1b22..19a3948 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -100,6 +100,7 @@
* with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
*
* If you make changes here, consider making the same changes there
+ * TODO(b/254378592): We really need to consolidate this
*/
public static class SplitBounds {
public final Rect leftTopBounds;
@@ -181,4 +182,12 @@
? LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP
: LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
}
+
+ public static @StagePosition int getOppositeStagePosition(@StagePosition int position) {
+ if (position == STAGE_POSITION_UNDEFINED) {
+ return position;
+ }
+ return position == STAGE_POSITION_TOP_OR_LEFT ? STAGE_POSITION_BOTTOM_OR_RIGHT
+ : STAGE_POSITION_TOP_OR_LEFT;
+ }
}
diff --git a/src/com/android/launcher3/views/AllAppsButton.java b/src/com/android/launcher3/views/AllAppsButton.java
deleted file mode 100644
index ab8e5db..0000000
--- a/src/com/android/launcher3/views/AllAppsButton.java
+++ /dev/null
@@ -1,50 +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.views;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.FastBitmapDrawable;
-
-/**
- * Button in Taskbar that opens All Apps.
- */
-public class AllAppsButton extends BubbleTextView {
-
- public AllAppsButton(Context context) {
- this(context, null);
- }
-
- public AllAppsButton(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AllAppsButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
- Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
- .createScaledBitmapWithShadow(theme.getDrawable(R.drawable.ic_all_apps_button));
- setIcon(new FastBitmapDrawable(bitmap));
- setContentDescription(context.getString(R.string.all_apps_button_label));
- }
-}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 1e154a2..8ff6888 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
-import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -41,8 +40,8 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.TouchController;
import java.io.PrintWriter;
@@ -108,7 +107,6 @@
protected final T mActivity;
private final MultiValueAlpha mMultiValueAlpha;
- private final WallpaperManager mWallpaperManager;
// All the touch controllers for the view
protected TouchController[] mControllers;
@@ -121,9 +119,8 @@
public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
super(context, attrs);
- mActivity = (T) ActivityContext.lookupContext(context);
+ mActivity = ActivityContext.lookupContext(context);
mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
- mWallpaperManager = context.getSystemService(WallpaperManager.class);
}
/**
@@ -273,12 +270,6 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getActionIndex() > 0) {
- // This means there is multiple touch inputs, ignore it, we could also cancel the
- // previous touch but the user might cancel the drag by accident.
- return true;
- }
-
switch (ev.getAction()) {
case ACTION_DOWN: {
if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
@@ -508,8 +499,8 @@
return new LayoutParams(p);
}
- public AlphaProperty getAlphaProperty(int index) {
- return mMultiValueAlpha.getProperty(index);
+ public MultiProperty getAlphaProperty(int index) {
+ return mMultiValueAlpha.get(index);
}
public void dump(String prefix, PrintWriter writer) {
diff --git a/src/com/android/launcher3/views/IconButtonView.java b/src/com/android/launcher3/views/IconButtonView.java
new file mode 100644
index 0000000..dd48c99
--- /dev/null
+++ b/src/com/android/launcher3/views/IconButtonView.java
@@ -0,0 +1,94 @@
+/*
+ * 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.views;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BlendMode;
+import android.graphics.BlendModeColorFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.icons.LauncherIcons;
+
+/**
+ * Button in Taskbar that shows a tinted background and foreground.
+ */
+public class IconButtonView extends BubbleTextView {
+
+ private static final int[] ATTRS = {android.R.attr.icon};
+
+ public IconButtonView(Context context) {
+ this(context, null);
+ }
+
+ public IconButtonView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public IconButtonView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, ATTRS, defStyle, 0);
+ Drawable fg = a.getDrawable(0);
+ a.recycle();
+
+ ColorStateList tintList = getBackgroundTintList();
+ int tint = tintList == null ? Color.WHITE : tintList.getDefaultColor();
+
+ if (fg == null) {
+ fg = new ColorDrawable(Color.TRANSPARENT);
+ }
+ try (BaseIconFactory factory = LauncherIcons.obtain(context)) {
+ setIcon(new IconDrawable(factory.getWhiteShadowLayer(), tint, fg));
+ }
+ }
+
+ private static class IconDrawable extends FastBitmapDrawable {
+
+ private final Drawable mFg;
+
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
+ IconDrawable(Bitmap b, int colorBg, Drawable fg) {
+ super(b);
+ mPaint.setColorFilter(new BlendModeColorFilter(colorBg, BlendMode.SRC_IN));
+ mFg = fg;
+ }
+
+ @Override
+ protected void drawInternal(Canvas canvas, Rect bounds) {
+ super.drawInternal(canvas, bounds);
+ mFg.draw(canvas);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mFg.setBounds(bounds);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 40e4ce1..3af2e3c 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -119,7 +119,6 @@
// prevent jumping, this offset is applied as the user scrolls.
protected int mTouchOffsetY;
protected int mThumbOffsetY;
- protected int mRvOffsetY;
// Fast scroller popup
private TextView mPopupView;
@@ -207,16 +206,11 @@
public void setThumbOffsetY(int y) {
if (mThumbOffsetY == y) {
- int rvCurrentOffsetY = mRv.getCurrentScrollY();
- if (mRvOffsetY != rvCurrentOffsetY) {
- mRvOffsetY = mRv.getCurrentScrollY();
- }
return;
}
updatePopupY(y);
mThumbOffsetY = y;
invalidate();
- mRvOffsetY = mRv.getCurrentScrollY();
}
public int getThumbOffsetY() {
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 3e80699..fff8fbb 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -18,6 +18,7 @@
import static android.app.Activity.RESULT_CANCELED;
+import android.app.PendingIntent;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -28,6 +29,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.util.SparseArray;
+import android.view.View;
import android.widget.RemoteViews;
import android.widget.Toast;
@@ -80,6 +82,24 @@
private IntConsumer mAppWidgetRemovedCallback = null;
+ /**
+ * This serves for the purpose of getting rid of the hidden API calling of InteractionHandler
+ */
+ public interface LauncherWidgetInteractionHandler {
+ /**
+ * Invoked when the user performs an interaction on the View.
+ *
+ * @param view the View with which the user interacted
+ * @param pendingIntent the base PendingIntent associated with the view
+ * @param response the response to the interaction, which knows how to fill in the
+ * attached PendingIntent
+ */
+ boolean onInteraction(
+ View view,
+ PendingIntent pendingIntent,
+ RemoteViews.RemoteResponse response);
+ }
+
public LauncherAppWidgetHost(Context context) {
this(context, null);
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index fc1e880..bc3889f 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -92,10 +92,6 @@
// The following member variables are only used during drag-n-drop.
private boolean mIsInDragMode = false;
- /** The drag content width which is only set when the drag content scale is not 1f. */
- private int mDragContentWidth = 0;
- /** The drag content height which is only set when the drag content scale is not 1f. */
- private int mDragContentHeight = 0;
private boolean mTrackingWidgetUpdate = false;
@@ -314,27 +310,9 @@
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mIsInDragMode && mDragContentWidth > 0 && mDragContentHeight > 0
- && getChildCount() == 1) {
- measureChild(getChildAt(0), MeasureSpec.getSize(mDragContentWidth),
- MeasureSpec.getSize(mDragContentHeight));
- }
- }
-
/** Starts the drag mode. */
public void startDrag() {
mIsInDragMode = true;
- // In the case of dragging a scaled preview from widgets picker, we should reuse the
- // previously measured dimension from WidgetCell#measureAndComputeWidgetPreviewScale, which
- // measures the dimension of a widget preview without its parent's bound before scaling
- // down.
- if ((getScaleX() != 1f || getScaleY() != 1f) && getChildCount() == 1) {
- mDragContentWidth = getChildAt(0).getMeasuredWidth();
- mDragContentHeight = getChildAt(0).getMeasuredHeight();
- }
}
/** Handles a drag event occurred on a workspace page corresponding to the {@code screenId}. */
@@ -347,8 +325,6 @@
/** Ends the drag mode. */
public void endDrag() {
mIsInDragMode = false;
- mDragContentWidth = 0;
- mDragContentHeight = 0;
requestLayout();
}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index c8d528b..bbbc329 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -39,6 +39,7 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.RoundDrawableWrapper;
@@ -181,7 +182,8 @@
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
LauncherIcons li = LauncherIcons.obtain(launcher);
- preview = new FastBitmapDrawable(li.createScaledBitmapWithoutShadow(icon));
+ preview = new FastBitmapDrawable(
+ li.createScaledBitmap(icon, BaseIconFactory.MODE_DEFAULT));
previewWidth = preview.getIntrinsicWidth();
previewHeight = preview.getIntrinsicHeight();
li.recycle();
diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
index 9313266..9319a9c 100644
--- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
+++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
@@ -55,7 +55,7 @@
public void startBindFlow(Launcher launcher, int appWidgetId, ItemInfo info, int requestCode) {
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
- launcher.getAppWidgetHost()
+ launcher.getAppWidgetHolder()
.startBindFlow(launcher, appWidgetId, mProviderInfo, requestCode);
}
@@ -77,7 +77,7 @@
return false;
}
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
- launcher.getAppWidgetHost().startConfigActivity(launcher, appWidgetId, requestCode);
+ launcher.getAppWidgetHolder().startConfigActivity(launcher, appWidgetId, requestCode);
return true;
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 2796721..ce47d70 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -284,6 +284,40 @@
ensurePreviewWithCallback(callback, cachedPreview);
}
+ private static class ScaledAppWidgetHostView extends LauncherAppWidgetHostView {
+ private boolean mKeepOrigForDragging = true;
+
+ ScaledAppWidgetHostView(Context context) {
+ super(context);
+ }
+
+ /**
+ * Set if the view will keep its original scale when dragged
+ * @param isKeepOrig True if keep original scale when dragged, false otherwise
+ */
+ public void setKeepOrigForDragging(boolean isKeepOrig) {
+ mKeepOrigForDragging = isKeepOrig;
+ }
+
+ /**
+ * @return True if the view is set to preserve original scale when dragged, false otherwise
+ */
+ public boolean isKeepOrigForDragging() {
+ return mKeepOrigForDragging;
+ }
+
+ @Override
+ public void startDrag() {
+ super.startDrag();
+ if (!isKeepOrigForDragging()) {
+ // restore to original scale when being dragged, if set to do so
+ setScaleToFit(1.0f);
+ }
+ // When the drag start, translations need to be set to zero to center the view
+ setTranslationForCentering(0f, 0f);
+ }
+ }
+
private void applyPreviewOnAppWidgetHostView(WidgetItem item) {
if (mRemoteViewsPreview != null) {
mAppWidgetHostViewPreview = createAppWidgetHostView(getContext());
@@ -299,7 +333,7 @@
// a preview during drag & drop. And thus, we should use LauncherAppWidgetHostView, which
// supports applying local color extraction during drag & drop.
mAppWidgetHostViewPreview = isLauncherContext(context)
- ? new LauncherAppWidgetHostView(context)
+ ? new ScaledAppWidgetHostView(context)
: createAppWidgetHostView(context);
LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
LauncherAppWidgetProviderInfo.fromProviderInfo(context, item.widgetInfo.clone());
@@ -398,23 +432,41 @@
int containerWidth = (int) (mTargetPreviewWidth * mPreviewContainerScale);
int containerHeight = (int) (mTargetPreviewHeight * mPreviewContainerScale);
setContainerSize(containerWidth, containerHeight);
+ boolean shouldMeasureAndScale = false;
if (mAppWidgetHostViewPreview.getChildCount() == 1) {
View widgetContent = mAppWidgetHostViewPreview.getChildAt(0);
ViewGroup.LayoutParams layoutParams = widgetContent.getLayoutParams();
// We only scale preview if both the width & height of the outermost view group are
// not set to MATCH_PARENT.
- boolean shouldScale =
+ shouldMeasureAndScale =
layoutParams.width != MATCH_PARENT && layoutParams.height != MATCH_PARENT;
- if (shouldScale) {
+ if (shouldMeasureAndScale) {
setNoClip(mWidgetImageContainer);
setNoClip(mAppWidgetHostViewPreview);
mAppWidgetHostViewScale = measureAndComputeWidgetPreviewScale();
- mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
}
}
+
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- containerWidth, containerHeight, Gravity.FILL);
+ mTargetPreviewWidth, mTargetPreviewHeight, Gravity.FILL);
mAppWidgetHostViewPreview.setLayoutParams(params);
+
+ if (!shouldMeasureAndScale
+ && mAppWidgetHostViewPreview instanceof ScaledAppWidgetHostView) {
+ // If the view is not measured & scaled, at least one side will match the grid size,
+ // so it should be safe to restore the original scale once it is dragged.
+ ScaledAppWidgetHostView tempView =
+ (ScaledAppWidgetHostView) mAppWidgetHostViewPreview;
+ tempView.setKeepOrigForDragging(false);
+ tempView.setScaleToFit(mPreviewContainerScale);
+ } else if (!shouldMeasureAndScale) {
+ mAppWidgetHostViewPreview.setScaleToFit(mPreviewContainerScale);
+ } else {
+ mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
+ }
+ mAppWidgetHostViewPreview.setTranslationForCentering(
+ -(params.width - (params.width * mPreviewContainerScale)) / 2.0f,
+ -(params.height - (params.height * mPreviewContainerScale)) / 2.0f);
mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
mWidgetImage.setVisibility(View.GONE);
applyPreview(null);
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 46141e0..b18cd47 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -59,7 +59,7 @@
// Cleanup widget id
if (mWidgetLoadingId != -1) {
- mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
+ mLauncher.getAppWidgetHolder().deleteAppWidgetId(mWidgetLoadingId);
mWidgetLoadingId = -1;
}
@@ -69,7 +69,7 @@
Log.d(TAG, "...removing widget from drag layer");
}
mLauncher.getDragLayer().removeView(mInfo.boundWidget);
- mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
+ mLauncher.getAppWidgetHolder().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
mInfo.boundWidget = null;
}
}
@@ -94,7 +94,7 @@
mBindWidgetRunnable = new Runnable() {
@Override
public void run() {
- mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
+ mWidgetLoadingId = mLauncher.getAppWidgetHolder().allocateAppWidgetId();
if (LOGD) {
Log.d(TAG, "Binding widget, id: " + mWidgetLoadingId);
}
@@ -116,7 +116,7 @@
if (mWidgetLoadingId == -1) {
return;
}
- AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
+ AppWidgetHostView hostView = mLauncher.getAppWidgetHolder().createView(
(Context) mLauncher, mWidgetLoadingId, pInfo);
mInfo.boundWidget = hostView;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index da8e25c..21b2647 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -321,7 +321,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mActivityContext.getAppWidgetHost().addProviderChangeListener(this);
+ mActivityContext.getAppWidgetHolder().addProviderChangeListener(this);
notifyWidgetProvidersChanged();
onRecommendedWidgetsBound();
}
@@ -329,7 +329,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mActivityContext.getAppWidgetHost().removeProviderChangeListener(this);
+ mActivityContext.getAppWidgetHolder().removeProviderChangeListener(this);
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView
.removeOnAttachStateChangeListener(mBindScrollbarInSearchMode);
if (mHasWorkProfile) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 35fa7a4..5969e3e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
-import android.util.SparseIntArray;
import android.view.MotionEvent;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -28,6 +27,7 @@
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.R;
+import com.android.launcher3.util.ScrollableLayoutManager;
/**
* The widgets recycler view.
@@ -42,14 +42,6 @@
private boolean mTouchDownOnScroller;
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
- /**
- * There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
- * the size can be used for all other items of same type. Caching the last know size for
- * VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
- * VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
- */
- private final SparseIntArray mCachedSizes = new SparseIntArray();
-
public WidgetsRecyclerView(Context context) {
this(context, null);
}
@@ -68,9 +60,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- // create a layout manager with Launcher's context so that scroll position
- // can be preserved during screen rotation.
- setLayoutManager(new LinearLayoutManager(getContext()));
+ setLayoutManager(new ScrollableLayoutManager(getContext()));
}
@Override
@@ -114,7 +104,7 @@
}
// Skip early if, there no child laid out in the container.
- int scrollY = getCurrentScrollY();
+ int scrollY = computeVerticalScrollOffset();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -164,39 +154,6 @@
}
/**
- * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
- * {@code untilIndex}.
- *
- * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
- * sum of all items' height.
- */
- @Override
- protected int getItemsHeight(int untilIndex) {
- // Initialize cache
- int childCount = getChildCount();
- int startPosition;
- if (childCount > 0
- && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
- int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
- for (int i = 0; i < loopCount; i++) {
- mCachedSizes.put(
- mAdapter.getItemViewType(startPosition + i),
- getChildAt(i).getMeasuredHeight());
- }
- }
-
- if (untilIndex > mAdapter.getItems().size()) {
- untilIndex = mAdapter.getItems().size();
- }
- int totalItemsHeight = 0;
- for (int i = 0; i < untilIndex; i++) {
- int type = mAdapter.getItemViewType(i);
- totalItemsHeight += mCachedSizes.get(type);
- }
- return totalItemsHeight;
- }
-
- /**
* Provides dimensions of the header view that is shown at the top of a
* {@link WidgetsRecyclerView}.
*/
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index c8b5e2f..ea0f5a3 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,10 +17,18 @@
package com.android.launcher3.uioverrides;
import android.app.Person;
+import android.appwidget.AppWidgetHost;
import android.content.pm.ShortcutInfo;
-import com.android.launcher3.Utilities;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+
+/**
+ * A wrapper for the hidden API calls
+ */
public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
@@ -28,4 +36,14 @@
public static Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY;
}
+
+ /**
+ * Set the interaction handler for the host
+ * @param host AppWidgetHost that needs the interaction handler
+ * @param handler InteractionHandler for the views in the host
+ */
+ public static void setHostInteractionHandler(@NonNull AppWidgetHost host,
+ @Nullable LauncherAppWidgetHost.LauncherWidgetInteractionHandler handler) {
+ // No-op
+ }
}
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index ae1060e..4af8468 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -62,6 +62,17 @@
</receiver>
<receiver
+ android:name="com.android.launcher3.testcomponent.AppWidgetWithDialog"
+ android:exported="true"
+ android:label="With Dialog">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/appwidget_no_config"/>
+ </receiver>
+
+ <receiver
android:name="com.android.launcher3.testcomponent.AppWidgetDynamicColors"
android:exported="true"
android:label="Dynamic Colors">
@@ -276,7 +287,17 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
-
+ <activity
+ android:name="com.android.launcher3.testcomponent.DialogTestActivity"
+ android:label="Dialog Activity"
+ android:theme="@android:style/Theme.Dialog"
+ android:exported="true"
+ android:taskAffinity="com.android.launcher3.testcomponent.Affinity2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<activity android:name="com.android.launcher3.testcomponent.ImeTestActivity"
android:label="ImeTestActivity"
android:icon="@drawable/test_theme_icon"
diff --git a/tests/res/layout/test_layout_appwidget_blue.xml b/tests/res/layout/test_layout_appwidget_blue.xml
index 8111978..f33e575 100644
--- a/tests/res/layout/test_layout_appwidget_blue.xml
+++ b/tests/res/layout/test_layout_appwidget_blue.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/content"
android:orientation="vertical"
android:background="#FF0000FF"
android:layout_width="match_parent"
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java b/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java
new file mode 100644
index 0000000..6d617fa
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/AppWidgetWithDialog.java
@@ -0,0 +1,41 @@
+/*
+ * 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.testcomponent;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+
+/**
+ * A simple app widget with shows a dialog on clicking.
+ */
+public class AppWidgetWithDialog extends AppWidgetNoConfig {
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ int layoutId = context.getResources().getIdentifier(
+ "test_layout_appwidget_blue", "layout", context.getPackageName());
+ RemoteViews views = new RemoteViews(context.getPackageName(), layoutId);
+
+ PendingIntent pi = PendingIntent.getActivity(context, 0,
+ new Intent(context, DialogTestActivity.class), PendingIntent.FLAG_IMMUTABLE);
+ views.setOnClickPendingIntent(android.R.id.content, pi);
+ AppWidgetManager.getInstance(context).updateAppWidget(appWidgetIds, views);
+ }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java
new file mode 100644
index 0000000..9e5a274
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/DialogTestActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.testcomponent;
+
+
+/**
+ * Extension of BaseTestingActivity with a Dialog theme
+ */
+public class DialogTestActivity extends BaseTestingActivity {}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 32b62fb..70d122b 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -537,7 +537,7 @@
}
protected int getAllAppsScroll(Launcher launcher) {
- return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+ return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
}
private void checkLauncherIntegrity(
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index a3eee00..01e6ed7 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.graphics.Point;
+import android.os.SystemClock;
import android.platform.test.annotations.IwTest;
import androidx.test.filters.LargeTest;
@@ -302,7 +303,7 @@
}
private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).getCurrentScrollY();
+ return getWidgetsView(launcher).computeVerticalScrollOffset();
}
private boolean isOptionsPopupVisible(Launcher launcher) {
@@ -489,9 +490,12 @@
@Test
@PortraitLandscape
+ @ScreenRecord // (b/256659409)
public void testUninstallFromAllApps() throws Exception {
TestUtil.installDummyApp();
try {
+ // b/256659409
+ SystemClock.sleep(5000);
Workspace workspace = mLauncher.getWorkspace();
final HomeAllApps allApps = workspace.switchToAllApps();
allApps.freeze();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 561f3cc..1f5590e 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -54,6 +54,8 @@
clearHomescreen();
mDevice.pressHome();
+ waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
+
final LauncherAppWidgetProviderInfo widgetInfo =
TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index fa39ce0..0f861eb 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -190,7 +190,7 @@
waitForLauncherCondition("App widget options did not update",
l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
- executeOnLauncher(l -> l.getAppWidgetHost().startListening());
+ executeOnLauncher(l -> l.getAppWidgetHolder().startListening());
verifyWidgetPresent(info);
assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
}
diff --git a/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
index a4f189c..bf3a092 100644
--- a/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
+++ b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
@@ -39,21 +39,21 @@
}
}
- private val factory = MultiPropertyFactory("depth_property", receiveProperty) {
+ private val factory = MultiPropertyFactory(null, receiveProperty, 3) {
x: Float, y: Float -> x + y
}
- private val p1 = factory.get(1)
- private val p2 = factory.get(2)
- private val p3 = factory.get(3)
+ private val p1 = factory.get(0)
+ private val p2 = factory.get(1)
+ private val p3 = factory.get(2)
@Test
fun set_sameIndexes_allApplied() {
val v1 = 50f
val v2 = 100f
- p1.set(null, v1)
- p1.set(null, v1)
- p1.set(null, v2)
+ p1.value = v1
+ p1.value = v1
+ p1.value = v2
assertThat(received).containsExactly(v1, v1, v2)
}
@@ -63,9 +63,9 @@
val v1 = 50f
val v2 = 100f
val v3 = 150f
- p1.set(null, v1)
- p2.set(null, v2)
- p3.set(null, v3)
+ p1.value = v1
+ p2.value = v2
+ p3.value = v3
assertThat(received).containsExactly(v1, v1 + v2, v1 + v2 + v3)
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 4791846..6bbdf48 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -46,8 +46,7 @@
super(launcher);
final UiObject2 allAppsContainer = verifyActiveContainer();
mHeight = mLauncher.getVisibleBounds(allAppsContainer).height();
- final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
- "apps_list_view");
+ final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
// Wait for the recycler to populate.
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
verifyNotFrozen("All apps freeze flags upon opening all apps");
@@ -78,6 +77,11 @@
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
+ if (iconCenterInRecyclerTopPadding(appListRecycler, icon)) {
+ LauncherInstrumentation.log(
+ "hasClickableIcon: icon center is under the app list recycler's top padding.");
+ return false;
+ }
if (iconBounds.bottom > displayBottom) {
LauncherInstrumentation.log("hasClickableIcon: icon bottom below bottom offset");
return false;
@@ -92,6 +96,13 @@
iconCenter.x, iconCenter.y);
}
+ private boolean iconCenterInRecyclerTopPadding(UiObject2 appListRecycler, UiObject2 icon) {
+ final Point iconCenter = icon.getVisibleCenter();
+
+ return iconCenter.y <= mLauncher.getVisibleBounds(appListRecycler).top
+ + getAppsListRecyclerTopPadding();
+ }
+
/**
* Finds an icon. If the icon doesn't exist, return null.
* Scrolls the app list when needed to make sure the icon is visible.
@@ -105,9 +116,7 @@
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"getting app icon " + appName + " on all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
- final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
- "apps_list_view");
- final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
+ final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
int deviceHeight = mLauncher.getRealDisplaySize().y;
int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
@@ -128,10 +137,9 @@
mLauncher.getVisibleBounds(icon).top
< bottomGestureStartOnScreen)
.collect(Collectors.toList()),
- hasSearchBox()
- ? mLauncher.getVisibleBounds(searchBox).bottom
- - mLauncher.getVisibleBounds(allAppsContainer).top
- : 0);
+ mLauncher.getVisibleBounds(appListRecycler).top
+ + getAppsListRecyclerTopPadding()
+ - mLauncher.getVisibleBounds(allAppsContainer).top);
verifyActiveContainer();
final int newScroll = getAllAppsScroll();
mLauncher.assertTrue(
@@ -180,16 +188,22 @@
protected abstract boolean hasSearchBox();
+ protected abstract int getAppsListRecyclerTopPadding();
+
private void scrollBackToBeginning() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to scroll back in all apps")) {
LauncherInstrumentation.log("Scrolling to the beginning");
final UiObject2 allAppsContainer = verifyActiveContainer();
- final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
+ final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
int attempts = 0;
final Rect margins = new Rect(
- 0, hasSearchBox() ? mLauncher.getVisibleBounds(searchBox).bottom + 1 : 0, 0, 5);
+ /* left= */ 0,
+ mLauncher.getVisibleBounds(appListRecycler).top
+ + getAppsListRecyclerTopPadding() + 1,
+ /* right= */ 0,
+ /* bottom= */ 5);
for (int scroll = getAllAppsScroll();
scroll != 0;
@@ -220,6 +234,10 @@
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ private UiObject2 getAppListRecycler(UiObject2 allAppsContainer) {
+ return mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view");
+ }
+
private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
return mLauncher.waitForObjectInContainer(allAppsContainer, "search_container_all_apps");
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
index 5164025..f804e28 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
@@ -18,6 +18,8 @@
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
/**
* Operations on AllApps opened from the Taskbar.
*/
@@ -48,4 +50,10 @@
protected boolean hasSearchBox() {
return false;
}
+
+ @Override
+ protected int getAppsListRecyclerTopPadding() {
+ return mLauncher.getTestInfo(TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index f47f710..afeb8d7 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -244,43 +244,39 @@
* Returns if clear all button is visible.
*/
public boolean isClearAllVisible() {
- return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
+ return verifyActiveContainer().hasObject(
+ mLauncher.getOverviewObjectSelector("clear_all"));
}
protected boolean isActionsViewVisible() {
+ if (!hasTasks() || isClearAllVisible()) {
+ return false;
+ }
OverviewTask task = mLauncher.isTablet() ? getFocusedTaskForTablet() : getCurrentTask();
if (task == null) {
return false;
}
+ // In tablets, if focused task is not in center, overview actions aren't visible.
+ if (mLauncher.isTablet()
+ && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) {
+ return false;
+ }
+ // Overview actions aren't visible for split screen tasks.
return !task.isTaskSplit();
}
private void verifyActionsViewVisibility() {
- if (!hasTasks() || !isActionsViewVisible()) {
- return;
- }
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to assert overview actions view visibility")) {
- if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) {
- mLauncher.waitUntilOverviewObjectGone("action_buttons");
- } else {
+ if (isActionsViewVisible()) {
mLauncher.waitForOverviewObject("action_buttons");
+ } else {
+ mLauncher.waitUntilOverviewObjectGone("action_buttons");
}
}
}
/**
- * Returns if focused task is currently snapped task in tablet grid overview.
- */
- private boolean isOverviewSnappedToFocusedTaskForTablet() {
- OverviewTask focusedTask = getFocusedTaskForTablet();
- if (focusedTask == null) {
- return false;
- }
- return Math.abs(focusedTask.getExactCenterX() - mLauncher.getExactScreenCenterX()) < 1;
- }
-
- /**
* Returns Overview focused task if it exists.
*
* @throws IllegalStateException if not run on a tablet device.
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 7123de4..9a4c6d4 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -18,6 +18,8 @@
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
public class HomeAllApps extends AllApps {
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
@@ -47,6 +49,12 @@
return true;
}
+ @Override
+ protected int getAppsListRecyclerTopPadding() {
+ return mLauncher.getTestInfo(TestProtocol.REQUEST_ALL_APPS_TOP_PADDING)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
/**
* Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
* @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 41bb7f3..449b7b7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -391,6 +391,10 @@
getTestInfo(TestProtocol.REQUEST_ENABLE_ROTATION, Boolean.toString(on));
}
+ public void setEnableSuggestion(boolean enableSuggestion) {
+ getTestInfo(TestProtocol.REQUEST_ENABLE_SUGGESTION, Boolean.toString(enableSuggestion));
+ }
+
public boolean hadNontestEvents() {
return getTestInfo(TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS)
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);