Merge "Support different task icon margin for grid tasks" into sc-v2-dev
diff --git a/buglist_with_title.txt b/buglist_with_title.txt
deleted file mode 100644
index aa8b413..0000000
--- a/buglist_with_title.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-144170434 twickham P1 FIXED Improve Overview -> Home transition ----
-149934536 twickham P2 FIXED Update gesture nav pullback logic ----
-154951045 peanutbutter P1 FIXED Odd animation occuring at times when swiping to home ----
-154964045 awickham P2 FIXED "Clear all" text is not in the middle of app's window vertically ----
-158701272 twickham P4 FIXED Discontinuities when long-swiping to home ----
-160361464 tracyzhou P2 FIXED Place launcher above the target app in live tile mode ----
-160568387 twickham P2 FIXED Can't get to app switcher by swiping up (motion pause not detected) ----
-160718310 xuqiu P1 FIXED With "Select" overview action selected, App icon is missing in other overview apps after orientation change ----
-160748731 sunnygoyal P2 ASSIGNED Unify prediction model with Launcher model ----
-160759508 twickham P2 FIXED Swipe up cannot back to home screen in overview. ----
-161273376 xuqiu P2 FIXED [Overview Actions] Add logging and helpful messages ----
-161536946 twickham P2 FIXED Haptics don't indicate snap-to in overview, ----
-161685099 winsonc P2 FIXED Screen still stay at the quick settings/notification when I swipe up with 3 finger to check the all apps. ----
-161801331 hyunyoungs P2 FIXED Change AllAppsSearch plugin to support only data fetch ----
-161901771 xuqiu P1 FIXED Overlapping layer of highlights with app layout getting darker when keep rotating the device from "Feedback" viewpoint in split screen ----
-161939759 sunnygoyal P2 FIXED RD1A: Going to overview in landscape mode clips the screen content ----
-162012217 perumaal P2 ASSIGNED Leaked Activity Caused by Gleams ----
-162454040 bookatz P2 ASSIGNED Create multiuser test that checks that opening an app works properly ----
-162480567 sfufa P4 FIXED Enable Item Decorations for search items ----
-162564471 tracyzhou P2 FIXED [Live tile] Handle tapping overview actions in live tile mode ----
-162623012 zakcohen P1 ASSIGNED Enable chips flag ----
-162812884 winsonc P2 ASSIGNED [R]The color have not changed in some page after turning on the dark theme. ----
-162861289 hyunyoungs P2 FIXED Add FocusIndicator support to DEVICE_SEARCH feature in S ----
-162871508 sfufa P2 ASSIGNED Introduce support for Hero app section ----
diff --git a/quickstep/res/drawable/button_taskbar_edu_bordered.xml b/quickstep/res/drawable/button_taskbar_edu_bordered.xml
new file mode 100644
index 0000000..47f8e8f
--- /dev/null
+++ b/quickstep/res/drawable/button_taskbar_edu_bordered.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<inset
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="16dp" />
+ <solid android:color="?androidprv:attr/colorSurface"/>
+ <stroke
+ android:width="1dp"
+ android:color="?androidprv:attr/colorAccentPrimaryVariant"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/quickstep/res/drawable/button_taskbar_edu_colored.xml b/quickstep/res/drawable/button_taskbar_edu_colored.xml
new file mode 100644
index 0000000..70bfc9f
--- /dev/null
+++ b/quickstep/res/drawable/button_taskbar_edu_colored.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<inset
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="16dp"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/quickstep/res/drawable/taskbar_edu_splitscreen.webp b/quickstep/res/drawable/taskbar_edu_splitscreen.webp
new file mode 100644
index 0000000..2f4402f
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_edu_splitscreen.webp
Binary files differ
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
new file mode 100644
index 0000000..069ff86
--- /dev/null
+++ b/quickstep/res/layout/task_grouped.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2021 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.
+-->
+<!-- NOTE! don't add dimensions for margins / paddings / sizes that change per orientation to this
+ file, they need to be loaded at runtime. -->
+
+<!-- DOUBLE NOTE! Don't deviate IDs from task.xml since this layout acts as a "subclass" (read as
+ "bad code"). How can we use the view pool in RecentsView to use task.xml layout with using
+ GroupedTaskView.java class? Is that possible (while still keeping code in separate class) ? -->
+
+<com.android.quickstep.views.GroupedTaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:defaultFocusHighlightEnabled="false"
+ android:focusable="true">
+
+ <com.android.quickstep.views.TaskThumbnailView
+ android:id="@+id/snapshot"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <com.android.quickstep.views.TaskThumbnailView
+ android:id="@+id/bottomright_snapshot"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <com.android.quickstep.views.IconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/task_thumbnail_icon_size"
+ android:layout_height="@dimen/task_thumbnail_icon_size"
+ android:focusable="false"
+ android:importantForAccessibility="no"/>
+</com.android.quickstep.views.GroupedTaskView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml
new file mode 100644
index 0000000..b7717b7
--- /dev/null
+++ b/quickstep/res/layout/taskbar_edu.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.TaskbarEduView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:gravity="bottom"
+ android:orientation="vertical"
+ android:layout_marginHorizontal="108dp">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/edu_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/bg_rounded_corner_bottom_sheet"
+ android:gravity="center_horizontal"
+ android:paddingHorizontal="36dp"
+ android:paddingTop="64dp">
+
+ <TextView
+ android:id="@+id/edu_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="16dp"
+ app:layout_constraintTop_toTopOf="parent"
+ android:gravity="center_horizontal"
+ style="@style/TextHeadline"
+ android:text="@string/taskbar_edu_header_1"
+ android:fontFamily="google-sans"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="24sp"
+ android:maxLines="2"/>
+
+ <ImageView
+ android:id="@+id/edu_illustration"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ app:layout_constraintTop_toBottomOf="@id/edu_header"
+ android:src="@drawable/taskbar_edu_splitscreen"/>
+
+ <Button
+ android:id="@+id/edu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:layout_marginBottom="92dp"
+ app:layout_constraintTop_toBottomOf="@id/edu_illustration"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ android:text="@string/taskbar_edu_close"
+ style="@style/TaskbarEdu.Button.Close"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <Button
+ android:id="@+id/edu_next_button"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:layout_marginBottom="92dp"
+ app:layout_constraintTop_toBottomOf="@id/edu_illustration"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:text="@string/taskbar_edu_next"
+ style="@style/TaskbarEdu.Button.Next"
+ android:textColor="?androidprv:attr/textColorOnAccent"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.launcher3.taskbar.TaskbarEduView>
\ No newline at end of file
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 73e4e0c..ef8f3ba 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -33,13 +33,13 @@
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"మీ సూచించబడిన యాప్లు"</string>
<string name="hotseat_edu_title_migrate" msgid="306578144424489980">"మీ మొదటి స్క్రీన్ దిగువ వరుసలో యాప్ సలహాలను పొందండి"</string>
<string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"మీ హోమ్ స్క్రీన్లోని ఇష్టమైన వాటి వరుసలో యాప్ సూచనలు పొందండి"</string>
- <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ కార్యకలాపాలను బట్టి సూచనలు మారతాయి. దిగువ వరుసలోని యాప్లు మీ మొదటి స్క్రీన్ పైకి చేరుకుంటాయి."</string>
- <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ కార్యకలాపాలను బట్టి సూచనలు మారతాయి. ఇష్టమైన వాటి వరుసలోని యాప్లు మీ మొదటి స్క్రీన్కు చేరుకుంటాయి."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ కార్యకలాపాలను బట్టి సూచనలు మారతాయి. దిగువ వరుసలోని యాప్లు కొత్త ఫోల్డర్కు తరలించబడతాయి."</string>
+ <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ యాక్టివిటీలను బట్టి సూచనలు మారతాయి. దిగువ వరుసలోని యాప్లు మీ మొదటి స్క్రీన్ పైకి చేరుకుంటాయి."</string>
+ <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ యాక్టివిటీలను బట్టి సూచనలు మారతాయి. ఇష్టమైన వాటి వరుసలోని యాప్లు మీ మొదటి స్క్రీన్కు చేరుకుంటాయి."</string>
+ <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"మీరు ఎక్కువగా ఉపయోగించే యాప్లను నేరుగా మొదటి స్క్రీన్లోనే సులభంగా యాక్సెస్ చేయండి. మీ రోజువారీ యాక్టివిటీలను బట్టి సూచనలు మారతాయి. దిగువ వరుసలోని యాప్లు కొత్త ఫోల్డర్కు తరలించబడతాయి."</string>
<string name="hotseat_edu_accept" msgid="1611544083278999837">"యాప్ సూచనలను పొందండి"</string>
<string name="hotseat_edu_dismiss" msgid="2781161822780201689">"వద్దు"</string>
<string name="hotseat_prediction_settings" msgid="6246554993566070818">"సెట్టింగ్లు"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ఎక్కువగా ఉపయోగించిన యాప్లు ఇక్కడ కనిపిస్తాయి, అవి రోజువారీ కార్యకలాపాలను బట్టి మారుతూ ఉంటాయి"</string>
+ <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ఎక్కువగా ఉపయోగించిన యాప్లు ఇక్కడ కనిపిస్తాయి, అవి రోజువారీ యాక్టివిటీలను బట్టి మారుతూ ఉంటాయి"</string>
<string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"యాప్ సలహాలను పొందడానికి దిగువ వరుస నుండి యాప్లను లాగండి"</string>
<string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"యాప్ సూచనలు ఖాళీ స్పేస్కు జోడించబడ్డాయి"</string>
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"యాప్ సలహాలు ఎనేబుల్ చేయబడ్డాయి"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 658556e..f1f23c4 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -166,4 +166,5 @@
<dimen name="taskbar_stashed_size">24dp</dimen>
<dimen name="taskbar_stashed_handle_width">220dp</dimen>
<dimen name="taskbar_stashed_handle_height">6dp</dimen>
+ <dimen name="taskbar_edu_bg_corner_radius">28dp</dimen>
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 4aee2a9..da19c95 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -203,4 +203,13 @@
<string name="gesture_tutorial_action_button_label_cancel">Cancel</string>
<!-- Button text shown on a button on the tutorial skip dialog to exit the tutorial. [CHAR LIMIT=14] -->
<string name="gesture_tutorial_action_button_label_skip">Skip</string>
+
+ <!-- ******* Taskbar Edu ******* -->
+ <!-- Text in dialog that lets a user know how they can use the taskbar on their device.
+ [CHAR_LIMIT=NONE] -->
+ <string name="taskbar_edu_header_1">Use 2 apps at once and switch apps with the taskbar</string>
+ <!-- Text on button to go to the next screen of a tutorial [CHAR_LIMIT=30] -->
+ <string name="taskbar_edu_next">Next</string>
+ <!-- Text on button to exit a tutorial [CHAR_LIMIT=30] -->
+ <string name="taskbar_edu_close">Close</string>
</resources>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 07c448d..c23a3a0 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -139,4 +139,16 @@
<style name="BaseIcon.Workspace.Taskbar" >
<item name="iconDisplay">taskbar</item>
</style>
+
+ <style name="TaskbarEdu.Button.Close" parent="@android:style/Widget.Material.Button">
+ <item name="android:background">@drawable/button_taskbar_edu_bordered</item>
+ <item name="android:stateListAnimator">@null</item>
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="TaskbarEdu.Button.Next" parent="@android:style/Widget.Material.Button">
+ <item name="android:background">@drawable/button_taskbar_edu_colored</item>
+ <item name="android:stateListAnimator">@null</item>
+ <item name="android:textSize">16sp</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
index 9df9ab1..82e8903 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -63,7 +63,7 @@
RunningTaskInfo placeholderTask = new RunningTaskInfo();
placeholderTask.taskId = 22;
- frv.showCurrentTask(placeholderTask);
+ frv.showCurrentTask(new RunningTaskInfo[]{placeholderTask});
doLayout(activity);
ThumbnailData thumbnailData = new ThumbnailData();
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index d41590b..433ae3c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -670,6 +670,10 @@
if (v instanceof BubbleTextView) {
((BubbleTextView) v).setStayPressed(false);
}
+ LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
+ if (taskbarController != null) {
+ taskbarController.showEdu();
+ }
openingTargets.release();
}
});
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 95c7d62..da10bfb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -23,6 +23,7 @@
import android.animation.ObjectAnimator;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.View;
import androidx.annotation.NonNull;
@@ -32,8 +33,10 @@
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
@@ -239,7 +242,11 @@
return mContext.getDragController().isDragging();
}
- void setTaskbarViewVisible(boolean isVisible) {
+ public View getRootView() {
+ return mTaskbarDragLayer;
+ }
+
+ private void setTaskbarViewVisible(boolean isVisible) {
mIconAlphaForHome.setValue(isVisible ? 1 : 0);
mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
}
@@ -261,6 +268,30 @@
mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
}
+ /**
+ * Starts the taskbar education flow, if the user hasn't seen it yet.
+ */
+ public void showEdu() {
+ if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()
+ || mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN)) {
+ return;
+ }
+ mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.TASKBAR_EDU_SEEN);
+
+ mControllers.taskbarEduController.showEdu();
+ }
+
+ /**
+ * Manually ends the taskbar education flow.
+ */
+ public void hideEdu() {
+ if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()) {
+ return;
+ }
+
+ mControllers.taskbarEduController.hideEdu();
+ }
+
private final class TaskBarRecentsAnimationListener implements RecentsAnimationListener {
private final RecentsAnimationCallbacks mCallbacks;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index dbe528f..a90f17d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -36,6 +36,7 @@
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.RoundedCorner;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -83,6 +84,7 @@
private final TaskbarControllers mControllers;
private final WindowManager mWindowManager;
+ private final RoundedCorner mLeftCorner, mRightCorner;
private WindowManager.LayoutParams mWindowLayoutParams;
private boolean mIsFullscreen;
// The size we should return to when we call setTaskbarWindowFullscreen(false)
@@ -128,17 +130,20 @@
new TaskbarViewController(this, taskbarView),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
- new TaskbarStashController(this));
+ new TaskbarStashController(this),
+ new TaskbarEduController(this));
Display display = windowContext.getDisplay();
Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
? windowContext.getApplicationContext()
: windowContext.getApplicationContext().createDisplayContext(display);
mWindowManager = c.getSystemService(WindowManager.class);
+ mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
}
public void init() {
- mLastRequestedNonFullscreenHeight = mDeviceProfile.taskbarSize;
+ mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
mWindowLayoutParams = new WindowManager.LayoutParams(
MATCH_PARENT,
mLastRequestedNonFullscreenHeight,
@@ -168,6 +173,14 @@
return mNavMode == Mode.THREE_BUTTONS;
}
+ public RoundedCorner getLeftCorner() {
+ return mLeftCorner;
+ }
+
+ public RoundedCorner getRightCorner() {
+ return mRightCorner;
+ }
+
@Override
public LayoutInflater getLayoutInflater() {
return mLayoutInflater;
@@ -249,8 +262,12 @@
setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
}
+ public boolean isTaskbarWindowFullscreen() {
+ return mIsFullscreen;
+ }
+
/**
- * Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset).
+ * Updates the TaskbarContainer height (pass {@link #getDefaultTaskbarWindowHeight()} to reset).
*/
public void setTaskbarWindowHeight(int height) {
if (mWindowLayoutParams.height == height || mIsDestroyed) {
@@ -270,6 +287,14 @@
mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
}
+ /**
+ * Returns the default height of the window, including the static corner radii above taskbar.
+ */
+ public int getDefaultTaskbarWindowHeight() {
+ return mDeviceProfile.taskbarSize
+ + Math.max(mLeftCorner.getRadius(), mRightCorner.getRadius());
+ }
+
protected void onTaskbarIconClicked(View view) {
Object tag = view.getTag();
if (tag instanceof Task) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index be26913..2927a63 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -34,6 +34,7 @@
public final TaskbarKeyguardController taskbarKeyguardController;
public final StashedHandleViewController stashedHandleViewController;
public final TaskbarStashController taskbarStashController;
+ public final TaskbarEduController taskbarEduController;
/** Do not store this controller, as it may change at runtime. */
@NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
@@ -47,7 +48,8 @@
TaskbarViewController taskbarViewController,
TaskbarKeyguardController taskbarKeyguardController,
StashedHandleViewController stashedHandleViewController,
- TaskbarStashController taskbarStashController) {
+ TaskbarStashController taskbarStashController,
+ TaskbarEduController taskbarEduController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -58,6 +60,7 @@
this.taskbarKeyguardController = taskbarKeyguardController;
this.stashedHandleViewController = stashedHandleViewController;
this.taskbarStashController = taskbarStashController;
+ this.taskbarEduController = taskbarEduController;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index cd1baf7..0848c35 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -40,9 +41,12 @@
public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
private final Paint mTaskbarBackgroundPaint;
+ private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
+ // Initialized in init.
private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
+ private float mLeftCornerRadius, mRightCornerRadius;
private float mTaskbarBackgroundOffset;
@@ -65,10 +69,32 @@
mTaskbarBackgroundPaint = new Paint();
mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
mTaskbarBackgroundPaint.setAlpha(0);
+ mTaskbarBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ mTaskbarBackgroundPaint.setStyle(Paint.Style.FILL);
+
+ // Will be set in init(), but this ensures they are always non-null.
+ mInvertedLeftCornerPath = new Path();
+ mInvertedRightCornerPath = new Path();
}
public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
mControllerCallbacks = callbacks;
+
+ // Create the paths for the inverted rounded corners above the taskbar. Start with a filled
+ // square, and then subtracting out a circle from the appropriate corner.
+ mLeftCornerRadius = mActivity.getLeftCorner().getRadius();
+ mRightCornerRadius = mActivity.getRightCorner().getRadius();
+ Path square = new Path();
+ square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW);
+ Path circle = new Path();
+ circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW);
+ mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+ square.reset();
+ square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW);
+ circle.reset();
+ circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW);
+ mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+
recreateControllers();
}
@@ -121,8 +147,20 @@
protected void dispatchDraw(Canvas canvas) {
float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
* (1f - mTaskbarBackgroundOffset);
- canvas.drawRect(0, canvas.getHeight() - backgroundHeight, canvas.getWidth(),
- canvas.getHeight(), mTaskbarBackgroundPaint);
+ canvas.save();
+ canvas.translate(0, canvas.getHeight() - backgroundHeight);
+
+ // Draw the background behind taskbar content.
+ canvas.drawRect(0, 0, canvas.getWidth(), backgroundHeight, mTaskbarBackgroundPaint);
+
+ // Draw the inverted rounded corners above the taskbar.
+ canvas.translate(0, -mLeftCornerRadius);
+ canvas.drawPath(mInvertedLeftCornerPath, mTaskbarBackgroundPaint);
+ canvas.translate(0, mLeftCornerRadius);
+ canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius);
+ canvas.drawPath(mInvertedRightCornerPath, mTaskbarBackgroundPaint);
+
+ canvas.restore();
super.dispatchDraw(canvas);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 14150b9..0afa480 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -132,13 +133,14 @@
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
} else if (mControllers.navbarButtonsViewController.isImeVisible()) {
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_CONTENT);
} else if (!mControllers.uiController.isTaskbarTouchable()) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
} else if (mControllers.taskbarViewController.areIconsVisible()) {
// Buttons are visible, take over the full taskbar area
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
+ insetsInfo.setTouchableInsets(mActivity.isTaskbarWindowFullscreen()
+ ? TOUCHABLE_INSETS_FRAME : TOUCHABLE_INSETS_CONTENT);
} else {
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
new file mode 100644
index 0000000..c5a0fc1
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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 com.android.launcher3.R;
+
+/** Handles the Taskbar Education flow. */
+public class TaskbarEduController {
+
+ private final TaskbarActivityContext mActivity;
+ private TaskbarEduView mTaskbarEduView;
+
+ public TaskbarEduController(TaskbarActivityContext activity) {
+ mActivity = activity;
+ }
+
+ void showEdu() {
+ mActivity.setTaskbarWindowFullscreen(true);
+ mActivity.getDragLayer().post(() -> {
+ mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
+ R.layout.taskbar_edu, mActivity.getDragLayer(), false);
+ mTaskbarEduView.addOnCloseListener(() -> {
+ mTaskbarEduView = null;
+ });
+ mTaskbarEduView.show();
+ });
+ }
+
+ void hideEdu() {
+ if (mTaskbarEduView != null) {
+ mTaskbarEduView.close(true /* animate */);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
new file mode 100644
index 0000000..b5dab7e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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 com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import com.android.launcher3.Insettable;
+import com.android.launcher3.R;
+import com.android.launcher3.views.AbstractSlideInView;
+
+/** Education view about the Taskbar. */
+public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
+ implements Insettable {
+
+ private static final int DEFAULT_CLOSE_DURATION = 200;
+
+ private final Rect mInsets = new Rect();
+
+ public TaskbarEduView(Context context, AttributeSet attr) {
+ this(context, attr, 0);
+ }
+
+ public TaskbarEduView(Context context, AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ handleClose(animate, DEFAULT_CLOSE_DURATION);
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_TASKBAR_EDUCATION_DIALOG) != 0;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mContent = findViewById(R.id.edu_view);
+ findViewById(R.id.edu_close_button).setOnClickListener(v -> close(true /* animate */));
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ mContent.setPadding(mContent.getPaddingStart(),
+ mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
+ }
+
+ @Override
+ protected void attachToContainer() {
+ if (mColorScrim != null) {
+ getPopupContainer().addView(mColorScrim, 0);
+ }
+ getPopupContainer().addView(this, 1);
+ }
+
+ /** Show the Education flow. */
+ public void show() {
+ attachToContainer();
+ animateOpen();
+ }
+
+ @Override
+ protected int getScrimColor(Context context) {
+ return context.getResources().getColor(R.color.widgets_picker_scrim);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int width = r - l;
+ int height = b - t;
+
+ // Lay out the content as center bottom aligned.
+ int contentWidth = mContent.getMeasuredWidth();
+ int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
+ mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
+ contentLeft + contentWidth, height);
+
+ setTranslationShift(mTranslationShift);
+ }
+
+ private void animateOpen() {
+ if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ mIsOpen = true;
+ mOpenCloseAnimator.setValues(
+ PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+ mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ mOpenCloseAnimator.start();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 6b95f08..1882762 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -165,8 +165,9 @@
int offsetY = launcherDp.getTaskbarOffsetY();
setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
- int collapsedHeight = mActivity.getDeviceProfile().taskbarSize;
- int expandedHeight = collapsedHeight + offsetY;
+ int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight();
+ int expandedHeight = Math.max(collapsedHeight,
+ mActivity.getDeviceProfile().taskbarSize + offsetY);
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 42c89fd..fe16b33 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -99,6 +99,7 @@
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.InputProxyHandlerFactory;
+import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.RecentsOrientedState;
@@ -106,6 +107,7 @@
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -222,7 +224,7 @@
protected final TaskAnimationManager mTaskAnimationManager;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
- private RunningWindowAnim mRunningWindowAnim;
+ private RunningWindowAnim[] mRunningWindowAnim;
// Possible second animation running at the same time as mRunningWindowAnim
private Animator mParallelRunningAnim;
private boolean mIsMotionPaused;
@@ -253,6 +255,10 @@
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
protected boolean mIsSwipingPipToHome;
+ // TODO(b/195473090) no split PIP for now, remove once we have more clarity
+ // can try to have RectFSpringAnim evaluate multiple rects at once
+ private final SwipePipToHomeAnimator[] mSwipePipToHomeAnimators =
+ new SwipePipToHomeAnimator[2];
// Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
private final float mQuickSwitchScaleScrollThreshold;
@@ -426,7 +432,8 @@
// RecentsView never updates the display rotation until swipe-up, force update
// RecentsOrientedState before passing to TaskViewSimulator.
mRecentsView.updateRecentsRotation();
- mTaskViewSimulator.setOrientationState(mRecentsView.getPagedViewOrientedState());
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ .setOrientationState(mRecentsView.getPagedViewOrientedState()));
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
@@ -519,7 +526,21 @@
}
protected void notifyGestureAnimationStartToRecents() {
- mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
+ ActivityManager.RunningTaskInfo[] runningTasks;
+ if (mIsSwipeForStagedSplit) {
+ int[] splitTaskIds =
+ LauncherSplitScreenListener.INSTANCE.getNoCreate().getSplitTaskIds();
+ runningTasks = new ActivityManager.RunningTaskInfo[splitTaskIds.length];
+ for (int i = 0; i < splitTaskIds.length; i++) {
+ int taskId = splitTaskIds[i];
+ ActivityManager.RunningTaskInfo rti = new ActivityManager.RunningTaskInfo();
+ rti.taskId = taskId;
+ runningTasks[i] = rti;
+ }
+ } else {
+ runningTasks = new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()};
+ }
+ mRecentsView.onGestureAnimationStart(runningTasks);
}
private void launcherFrameDrawn() {
@@ -606,15 +627,15 @@
if (animate) {
ValueAnimator reapplyWindowTransformAnim = ValueAnimator.ofFloat(0, 1);
reapplyWindowTransformAnim.addUpdateListener(anim -> {
- if (mRunningWindowAnim == null) {
- applyWindowTransform();
+ if (mRunningWindowAnim == null || mRunningWindowAnim.length == 0) {
+ applyScrollAndTransform();
}
});
reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
reapplyWindowTransformAnim::cancel);
} else {
- applyWindowTransform();
+ applyScrollAndTransform();
}
}
@@ -655,8 +676,13 @@
private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) {
mLauncherTransitionController = anim;
- mLauncherTransitionController.getNormalController().dispatchOnStart();
- updateLauncherTransitionProgress();
+ mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, () -> {
+ // Wait until the gesture is started (touch slop was passed) to start in sync with
+ // mWindowTransitionController. This ensures we don't hide the taskbar background when
+ // long pressing to stash it, for instance.
+ mLauncherTransitionController.getNormalController().dispatchOnStart();
+ updateLauncherTransitionProgress();
+ });
}
public Intent getLaunchIntent() {
@@ -678,7 +704,7 @@
}
updateSysUiFlags(mCurrentShift.value);
- applyWindowTransform();
+ applyScrollAndTransform();
updateLauncherTransitionProgress();
}
@@ -724,24 +750,23 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
- ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
+ super.onRecentsAnimationStart(controller, targets);
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
- mTransformParams.setTargetSet(mRecentsAnimationTargets);
- RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
- mGestureState.getRunningTaskId());
-
- if (runningTaskTarget != null) {
- mTaskViewSimulator.setPreview(runningTaskTarget);
- }
// 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) {
- DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
- if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
+ RemoteAnimationTargetCompat 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
+ RecentsOrientedState orientationState = mRemoteTargetHandles[0].mTaskViewSimulator
+ .getOrientationState();
+ DeviceProfile dp = orientationState.getLauncherDeviceProfile();
+ if (targets.minimizedHomeBounds != null && primaryTaskTarget != null) {
Rect overviewStackBounds = mActivityInterface
- .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
+ .getOverviewWindowBounds(targets.minimizedHomeBounds, primaryTaskTarget);
dp = dp.getMultiWindowProfile(mContext,
new WindowBounds(overviewStackBounds, targets.homeContentInsets));
} else {
@@ -751,7 +776,7 @@
dp.updateInsets(targets.homeContentInsets);
dp.updateIsSeascape(mContext);
initTransitionEndpoints(dp);
- mTaskViewSimulator.getOrientationState().setMultiWindowMode(dp.isMultiWindowMode);
+ orientationState.setMultiWindowMode(dp.isMultiWindowMode);
}
// Notify when the animation starts
@@ -869,9 +894,17 @@
private void endRunningWindowAnim(boolean cancel) {
if (mRunningWindowAnim != null) {
if (cancel) {
- mRunningWindowAnim.cancel();
+ for (RunningWindowAnim r : mRunningWindowAnim) {
+ if (r != null) {
+ r.cancel();
+ }
+ }
} else {
- mRunningWindowAnim.end();
+ for (RunningWindowAnim r : mRunningWindowAnim) {
+ if (r != null) {
+ r.end();
+ }
+ }
}
}
if (mParallelRunningAnim != null) {
@@ -885,6 +918,9 @@
// Fast-finish the attaching animation if it's still running.
maybeUpdateRecentsAttachedState(false);
final GestureEndTarget endTarget = mGestureState.getEndTarget();
+ // Wait until the given View (if supplied) draws before resuming the last task.
+ View postResumeLastTask = mActivityInterface.onSettledOnEndTarget(endTarget);
+
if (endTarget != NEW_TASK) {
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
@@ -893,6 +929,7 @@
InteractionJankMonitorWrapper.cancel(
InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
}
+
switch (endTarget) {
case HOME:
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
@@ -907,7 +944,12 @@
mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT);
break;
case LAST_TASK:
- mStateCallback.setState(STATE_RESUME_LAST_TASK);
+ if (postResumeLastTask != null) {
+ ViewUtils.postFrameDrawn(postResumeLastTask,
+ () -> mStateCallback.setState(STATE_RESUME_LAST_TASK));
+ } else {
+ mStateCallback.setState(STATE_RESUME_LAST_TASK);
+ }
TaskViewUtils.setDividerBarShown(mRecentsAnimationTargets.nonApps, true);
break;
}
@@ -1181,15 +1223,17 @@
createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
runningTaskTarget);
mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
- final RectFSpringAnim windowAnim;
+ final RectFSpringAnim[] windowAnim;
if (mIsSwipingPipToHome) {
mSwipePipToHomeAnimator = createWindowAnimationToPip(
homeAnimFactory, runningTaskTarget, start);
- windowAnim = mSwipePipToHomeAnimator;
+ mSwipePipToHomeAnimators[0] = mSwipePipToHomeAnimator;
+ windowAnim = mSwipePipToHomeAnimators;
} else {
mSwipePipToHomeAnimator = null;
windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
- windowAnim.addAnimatorListener(new AnimationSuccessListener() {
+
+ windowAnim[0].addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (mRecentsAnimationController == null) {
@@ -1203,15 +1247,22 @@
}
});
}
- windowAnim.start(mContext, velocityPxPerMs);
- mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+ mRunningWindowAnim = new RunningWindowAnim[windowAnim.length];
+ for (int i = 0, windowAnimLength = windowAnim.length; i < windowAnimLength; i++) {
+ RectFSpringAnim windowAnimation = windowAnim[i];
+ if (windowAnimation == null) {
+ continue;
+ }
+ windowAnimation.start(mContext, velocityPxPerMs);
+ mRunningWindowAnim[i] = RunningWindowAnim.wrap(windowAnimation);
+ }
homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
mLauncherTransitionController = null;
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(),
- mTaskViewSimulator);
+ getRemoteTaskViewSimulators());
}
} else {
AnimatorSet animatorSet = new AnimatorSet();
@@ -1253,11 +1304,12 @@
animatorSet.play(windowAnim);
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(
- animatorSet, mGestureState.getEndTarget(), mTaskViewSimulator);
+ animatorSet, mGestureState.getEndTarget(),
+ getRemoteTaskViewSimulators());
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
animatorSet.start();
- mRunningWindowAnim = RunningWindowAnim.wrap(animatorSet);
+ mRunningWindowAnim = new RunningWindowAnim[]{RunningWindowAnim.wrap(animatorSet)};
}
}
@@ -1272,16 +1324,21 @@
}
}
+ /**
+ * TODO(b/195473090) handle multiple task simulators (if needed) for PIP
+ */
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
- final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
+ final RecentsOrientedState orientationState = mRemoteTargetHandles[0].mTaskViewSimulator
+ .getOrientationState();
final int windowRotation = calculateWindowRotation(runningTaskTarget, orientationState);
final int homeRotation = orientationState.getRecentsActivityRotation();
final Matrix homeToWindowPositionMap = new Matrix();
- final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
+ final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap,
+ startProgress)[0];
// Move the startRect to Launcher space as floatingIconView runs in Launcher
final Matrix windowToHomePositionMap = new Matrix();
homeToWindowPositionMap.invert(windowToHomePositionMap);
@@ -1310,7 +1367,7 @@
// is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
if (homeRotation == ROTATION_0
&& (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
- builder.setFromRotation(mTaskViewSimulator, windowRotation,
+ builder.setFromRotation(mRemoteTargetHandles[0].mTaskViewSimulator, windowRotation,
taskInfo.displayCutoutInsets);
}
final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
@@ -1340,7 +1397,7 @@
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
- setupWindowAnimation(swipePipToHomeAnimator);
+ setupWindowAnimation(new RectFSpringAnim[]{swipePipToHomeAnimator});
return swipePipToHomeAnimator;
}
@@ -1367,19 +1424,19 @@
* @param homeAnimationFactory The home animation factory.
*/
@Override
- protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
+ protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
- RectFSpringAnim anim =
+ RectFSpringAnim[] anim =
super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
setupWindowAnimation(anim);
return anim;
}
- private void setupWindowAnimation(RectFSpringAnim anim) {
- anim.addOnUpdateListener((v, r, p) -> {
+ private void setupWindowAnimation(RectFSpringAnim[] anims) {
+ anims[0].addOnUpdateListener((v, r, p) -> {
updateSysUiFlags(Math.max(p, mCurrentShift.value));
});
- anim.addAnimatorListener(new AnimationSuccessListener() {
+ anims[0].addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (mRecentsView != null) {
@@ -1391,7 +1448,7 @@
}
});
if (mRecentsAnimationTargets != null) {
- mRecentsAnimationTargets.addReleaseCheck(anim);
+ mRecentsAnimationTargets.addReleaseCheck(anims[0]);
}
}
@@ -1639,7 +1696,7 @@
* if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
*/
private void maybeFinishSwipePipToHome() {
- if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
+ if (mIsSwipingPipToHome && mSwipePipToHomeAnimators[0] != null) {
SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
mSwipePipToHomeAnimator.getComponentName(),
mSwipePipToHomeAnimator.getDestinationBounds(),
@@ -1680,8 +1737,8 @@
* depend on proper class initialization.
*/
protected void initAfterSubclassConstructor() {
- initTransitionEndpoints(
- mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
+ initTransitionEndpoints(mRemoteTargetHandles[0].mTaskViewSimulator
+ .getOrientationState().getLauncherDeviceProfile());
}
protected void performHapticFeedback() {
@@ -1698,7 +1755,8 @@
protected void linkRecentsViewScroll() {
SurfaceTransactionApplier.create(mRecentsView, applier -> {
- mTransformParams.setSyncTransactionApplier(applier);
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ .setSyncTransactionApplier(applier));
runOnRecentsAnimationStart(() ->
mRecentsAnimationTargets.addReleaseCheck(applier));
});
@@ -1824,19 +1882,25 @@
/**
* Applies the transform on the recents animation
*/
- protected void applyWindowTransform() {
- if (mWindowTransitionController != null) {
- mWindowTransitionController.setProgress(
- Math.max(mCurrentShift.value, getScaleProgressDueToScroll()),
- mDragLengthFactor);
- }
+ protected void applyScrollAndTransform() {
// No need to apply any transform if there is ongoing swipe-pip-to-home animator since
// that animator handles the leash solely.
- if (mRecentsAnimationTargets != null && !mIsSwipingPipToHome) {
- if (mRecentsViewScrollLinked && mRecentsView != null) {
- mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+ boolean notSwipingPipToHome = mRecentsAnimationTargets != null && !mIsSwipingPipToHome;
+ boolean setRecentsScroll = mRecentsViewScrollLinked && mRecentsView != null;
+ for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
+ AnimatorControllerWithResistance playbackController = remoteHandle.mPlaybackController;
+ if (playbackController != null) {
+ playbackController.setProgress(Math.max(mCurrentShift.value,
+ getScaleProgressDueToScroll()), mDragLengthFactor);
}
- mTaskViewSimulator.apply(mTransformParams);
+
+ if (notSwipingPipToHome) {
+ TaskViewSimulator taskViewSimulator = remoteHandle.mTaskViewSimulator;
+ if (setRecentsScroll) {
+ taskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+ }
+ taskViewSimulator.apply(remoteHandle.mTransformParams);
+ }
}
ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
}
@@ -1891,7 +1955,6 @@
}
public interface Factory {
-
AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs);
}
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 571ffc2..e781160 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.View;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -52,6 +53,7 @@
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -201,10 +203,36 @@
}
/**
+ * Sets the task size in {@param outRect} taking split screened windows into account.
+ * We assume combined height of both tasks will be same as one normal task, then we'll modify
+ * the task height/width based on the ratio of task screen space bounds from
+ * {@param splitInfo}
+ *
+ * @param desiredStageBounds whether task size for top/left or bottom/right needs to be computed
+ */
+ public final void calculateStagedSplitTaskSize(Context context, DeviceProfile dp, Rect outRect,
+ SplitConfigurationOptions.StagedSplitBounds splitInfo,
+ @SplitConfigurationOptions.StagePosition int desiredStageBounds) {
+ calculateTaskSize(context, dp, outRect);
+
+ // TODO(b/181705607) Change for landscape vs portrait
+ float totalHeight = splitInfo.mLeftTopBounds.height()
+ + splitInfo.mRightBottomBounds.height()
+ + splitInfo.mDividerBounds.height() / 2f;
+ float topTaskPercent = splitInfo.mLeftTopBounds.height() / totalHeight;
+ if (desiredStageBounds == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+ float diff = outRect.height() * (1f - topTaskPercent);
+ outRect.bottom -= diff;
+ } else {
+ float diff = outRect.height() * topTaskPercent;
+ outRect.top += diff;
+ }
+ }
+
+ /**
* Calculates the taskView size for the provided device configuration.
*/
- public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
- PagedOrientationHandler orientedState) {
+ public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
Resources res = context.getResources();
if (dp.overviewShowAsGrid) {
Rect gridRect = new Rect();
@@ -385,6 +413,15 @@
*/
public abstract STATE_TYPE stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget);
+ /**
+ * Called when the animation to the target has finished, but right before updating the state.
+ * @return A View that needs to draw before ending the recents animation to LAST_TASK.
+ * (This is a hack to ensure Taskbar draws its background first to avoid flickering.)
+ */
+ public @Nullable View onSettledOnEndTarget(GestureState.GestureEndTarget endTarget) {
+ return null;
+ }
+
public interface AnimationFactory {
void createActivityInterface(long transitionLength);
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 7fb8e16..4df1aad 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -57,7 +57,7 @@
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
PagedOrientationHandler orientationHandler) {
- calculateTaskSize(context, dp, outRect, orientationHandler);
+ calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout()
&& SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index e2f198c..ec6032d 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -28,6 +28,7 @@
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -101,7 +102,9 @@
mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
if (mRunningOverHome) {
- mTransformParams.setHomeBuilderProxy(this::updateHomeActivityTransformDuringSwipeUp);
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ FallbackSwipeHandler.this::updateHomeActivityTransformDuringSwipeUp));
}
}
@@ -109,7 +112,8 @@
protected void initTransitionEndpoints(DeviceProfile dp) {
super.initTransitionEndpoints(dp);
if (mRunningOverHome) {
- mMaxLauncherScale = 1 / mTaskViewSimulator.getFullScreenScale();
+ // Full screen scale should be independent of remote target handle
+ mMaxLauncherScale = 1 / mRemoteTargetHandles[0].mTaskViewSimulator.getFullScreenScale();
}
}
@@ -174,7 +178,8 @@
protected void notifyGestureAnimationStartToRecents() {
if (mRunningOverHome) {
if (SysUINavigationMode.getMode(mContext).hasGestures) {
- mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
+ mRecentsView.onGestureAnimationStartOnHome(
+ new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()});
}
} else {
super.notifyGestureAnimationStartToRecents();
@@ -202,19 +207,24 @@
mHomeAlpha = new AnimatedFloat();
mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1);
mVerticalShiftForScale.value = mCurrentShift.value;
- mTransformParams.setHomeBuilderProxy(
- this::updateHomeActivityTransformDuringHomeAnim);
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ FallbackHomeAnimationFactory.this
+ ::updateHomeActivityTransformDuringHomeAnim));
} else {
mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
mHomeAlpha.value = 0;
-
- mHomeAlphaParams.setHomeBuilderProxy(
- this::updateHomeActivityTransformDuringHomeAnim);
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ FallbackHomeAnimationFactory.this
+ ::updateHomeActivityTransformDuringHomeAnim));
}
mRecentsAlpha.value = 1;
- mTransformParams.setBaseBuilderProxy(
- this::updateRecentsActivityTransformDuringHomeAnim);
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
+ FallbackHomeAnimationFactory.this
+ ::updateRecentsActivityTransformDuringHomeAnim));
}
@NonNull
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index d91d5b0..ae6ea79 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -25,10 +25,12 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -71,7 +73,7 @@
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
PagedOrientationHandler orientationHandler) {
- calculateTaskSize(context, dp, outRect, orientationHandler);
+ calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
} else {
@@ -131,6 +133,18 @@
new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
fromDepthRatio, toDepthRatio, LINEAR);
+ pa.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ LauncherTaskbarUIController taskbarUIController =
+ activity.getTaskbarUIController();
+ if (taskbarUIController != null) {
+ // Launcher's ScrimView will draw the background throughout the gesture.
+ taskbarUIController.forceHideBackground(true);
+ }
+ }
+ });
+
}
};
@@ -288,6 +302,10 @@
} else {
om.hideOverlay(150);
}
+ LauncherTaskbarUIController taskbarController = getTaskbarController();
+ if (taskbarController != null) {
+ taskbarController.hideEdu();
+ }
}
@Override
@@ -354,4 +372,16 @@
return NORMAL;
}
}
+
+ @Override
+ public View onSettledOnEndTarget(@Nullable GestureEndTarget endTarget) {
+ View superRet = super.onSettledOnEndTarget(endTarget);
+ LauncherTaskbarUIController taskbarUIController = getTaskbarController();
+ if (taskbarUIController != null) {
+ // Start drawing taskbar's background again since launcher might stop drawing.
+ taskbarUIController.forceHideBackground(false);
+ return taskbarUIController.getRootView();
+ }
+ return superRet;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 3239b00..ce3406c 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -110,7 +110,7 @@
mActivity.setHintUserWillBeActive();
}
- if (!canUseWorkspaceView || appCanEnterPip) {
+ if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForStagedSplit) {
return new LauncherHomeAnimationFactory();
}
if (workspaceView instanceof LauncherAppWidgetHostView) {
@@ -181,14 +181,16 @@
final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
RectF backgroundLocation = new RectF();
Rect crop = new Rect();
- mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
+ // We can assume there is only one remote target here because staged split never animates
+ // into the app icon, only into the homescreen
+ mRemoteTargetHandles[0].mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
Size windowSize = new Size(crop.width(), crop.height());
int fallbackBackgroundColor =
FloatingWidgetView.getDefaultBackgroundColor(mContext, runningTaskTarget);
FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity,
hostView, backgroundLocation, windowSize,
- mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent,
- fallbackBackgroundColor);
+ mRemoteTargetHandles[0].mTaskViewSimulator.getCurrentCornerRadius(),
+ isTargetTranslucent, fallbackBackgroundColor);
return new FloatingViewHomeAnimationFactory(floatingWidgetView) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 239233b..e948221 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.graphics.Rect;
import android.util.ArraySet;
@@ -30,6 +31,7 @@
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import java.util.Arrays;
import java.util.Set;
/**
@@ -93,8 +95,16 @@
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
+ // Convert appTargets to type RemoteAnimationTarget for all apps except Home app
+ RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appTargets)
+ .filter(remoteAnimationTarget ->
+ remoteAnimationTarget.activityType != ACTIVITY_TYPE_HOME)
+ .map(RemoteAnimationTargetCompat::unwrap)
+ .toArray(RemoteAnimationTarget[]::new);
+
RemoteAnimationTarget[] nonAppTargets =
- mSystemUiProxy.onGoingToRecentsLegacy(mCancelled);
+ mSystemUiProxy.onGoingToRecentsLegacy(mCancelled, nonHomeApps);
+
RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
homeContentInsets, minimizedHomeBounds);
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index c032889..b20d488 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -77,8 +77,12 @@
* Gets the navigation bar remote animation target if exists.
*/
public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+ return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
+ }
+
+ public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
for (RemoteAnimationTargetCompat target : nonApps) {
- if (target.windowType == TYPE_NAVIGATION_BAR) {
+ if (target.windowType == type) {
return target;
}
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 4495455..ec51599 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -15,9 +15,13 @@
*/
package com.android.quickstep;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_SELECT;
import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
import android.content.Context;
@@ -36,8 +40,11 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.AppCloseConfig;
+import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RectFSpringAnim2;
import com.android.quickstep.util.TaskViewSimulator;
@@ -46,7 +53,11 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
-public abstract class SwipeUpAnimationLogic {
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+public abstract class SwipeUpAnimationLogic implements
+ RecentsAnimationCallbacks.RecentsAnimationListener{
protected static final Rect TEMP_RECT = new Rect();
@@ -55,9 +66,9 @@
protected final Context mContext;
protected final RecentsAnimationDeviceState mDeviceState;
protected final GestureState mGestureState;
- protected final TaskViewSimulator mTaskViewSimulator;
- protected final TransformParams mTransformParams;
+ protected final RemoteTargetHandle[] mRemoteTargetHandles;
+ protected SplitConfigurationOptions.StagedSplitBounds mStagedSplitBounds;
// Shift in the range of [0, 1].
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
@@ -70,37 +81,56 @@
// How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1;
- protected AnimatorControllerWithResistance mWindowTransitionController;
+ protected boolean mIsSwipeForStagedSplit;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, TransformParams transformParams) {
mContext = context;
mDeviceState = deviceState;
mGestureState = gestureState;
- mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
- mTransformParams = transformParams;
- mTaskViewSimulator.getOrientationState().update(
+ mIsSwipeForStagedSplit = ENABLE_SPLIT_SELECT.get() &&
+ LauncherSplitScreenListener.INSTANCE.getNoCreate().getSplitTaskIds().length > 1;
+
+ TaskViewSimulator primaryTVS = new TaskViewSimulator(context,
+ gestureState.getActivityInterface());
+ primaryTVS.getOrientationState().update(
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
mDeviceState.getRotationTouchHelper().getDisplayRotation());
+ mRemoteTargetHandles = new RemoteTargetHandle[mIsSwipeForStagedSplit ? 2 : 1];
+ mRemoteTargetHandles[0] = new RemoteTargetHandle(primaryTVS, transformParams);
+
+ if (mIsSwipeForStagedSplit) {
+ TaskViewSimulator secondaryTVS = new TaskViewSimulator(context,
+ gestureState.getActivityInterface());
+ secondaryTVS.getOrientationState().update(
+ mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
+ mDeviceState.getRotationTouchHelper().getDisplayRotation());
+ mRemoteTargetHandles[1] = new RemoteTargetHandle(secondaryTVS, new TransformParams());
+ }
}
protected void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
-
- mTaskViewSimulator.setDp(dp);
mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
- dp, mContext, TEMP_RECT,
- mTaskViewSimulator.getOrientationState().getOrientationHandler());
+ dp, mContext, TEMP_RECT, mRemoteTargetHandles[0].mTaskViewSimulator
+ .getOrientationState().getOrientationHandler());
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
- PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
- mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR);
- AnimatorPlaybackController normalController = pa.createPlaybackController();
- mWindowTransitionController = AnimatorControllerWithResistance.createForRecents(
- normalController, mContext, mTaskViewSimulator.getOrientationState(),
- mDp, mTaskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
- mTaskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE);
+ for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
+ PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2);
+ TaskViewSimulator taskViewSimulator = remoteHandle.mTaskViewSimulator;
+ taskViewSimulator.setDp(dp);
+ taskViewSimulator.addAppToOverviewAnim(pendingAnimation, LINEAR);
+ AnimatorPlaybackController playbackController =
+ pendingAnimation.createPlaybackController();
+
+ remoteHandle.mPlaybackController = AnimatorControllerWithResistance.createForRecents(
+ playbackController, mContext, taskViewSimulator.getOrientationState(),
+ mDp, taskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
+ taskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE
+ );
+ }
}
@UiThread
@@ -125,7 +155,9 @@
public abstract void updateFinalShift();
protected PagedOrientationHandler getOrientationHandler() {
- return mTaskViewSimulator.getOrientationState().getOrientationHandler();
+ // OrientationHandler should be independent of remote target, can directly take one
+ return mRemoteTargetHandles[0].mTaskViewSimulator
+ .getOrientationState().getOrientationHandler();
}
protected abstract class HomeAnimationFactory {
@@ -207,31 +239,102 @@
* @param startProgress The progress of {@link #mCurrentShift} to start thw window from.
* @return {@link RectF} represents the bounds as starting point in window space.
*/
- protected RectF updateProgressForStartRect(Matrix outMatrix, float startProgress) {
+ protected RectF[] updateProgressForStartRect(Matrix outMatrix, float startProgress) {
mCurrentShift.updateValue(startProgress);
- mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
- RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+ RectF[] startRects = new RectF[mRemoteTargetHandles.length];
+ for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
+ i < mRemoteTargetHandlesLength; i++) {
+ RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
+ TaskViewSimulator tvs = remoteHandle.mTaskViewSimulator;
+ tvs.apply(remoteHandle.mTransformParams.setProgress(startProgress));
- mTaskViewSimulator.applyWindowToHomeRotation(outMatrix);
-
- final RectF startRect = new RectF(cropRectF);
- mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
- return startRect;
+ startRects[i] = new RectF(tvs.getCurrentCropRect());
+ tvs.applyWindowToHomeRotation(outMatrix);
+ tvs.getCurrentMatrix().mapRect(startRects[i]);
+ }
+ return startRects;
}
+ /** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
+ protected void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
+ for (RemoteTargetHandle handle : mRemoteTargetHandles) {
+ consumer.accept(handle);
+ }
+ }
+
+ /** @return only the TaskViewSimulators from {@link #mRemoteTargetHandles} */
+ protected TaskViewSimulator[] getRemoteTaskViewSimulators() {
+ return Arrays.stream(mRemoteTargetHandles)
+ .map(remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator)
+ .toArray(TaskViewSimulator[]::new);
+ }
+
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
+ RemoteAnimationTargetCompat dividerTarget = targets.getNonAppTargetOfType(
+ TYPE_DOCK_DIVIDER);
+ RemoteAnimationTargetCompat primaryTaskTarget;
+ RemoteAnimationTargetCompat secondaryTaskTarget;
+
+ if (!mIsSwipeForStagedSplit) {
+ primaryTaskTarget = targets.findTask(mGestureState.getRunningTaskId());
+ mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
+
+ if (primaryTaskTarget != null) {
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget);
+ }
+ } else {
+ // We're in staged split
+ primaryTaskTarget = targets.apps[0];
+ secondaryTaskTarget = targets.apps[1];
+ mStagedSplitBounds = new SplitConfigurationOptions.StagedSplitBounds(
+ primaryTaskTarget.screenSpaceBounds,
+ secondaryTaskTarget.screenSpaceBounds, dividerTarget.screenSpaceBounds);
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
+ mStagedSplitBounds);
+ mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget,
+ mStagedSplitBounds);
+ mRemoteTargetHandles[0].mTransformParams.setTargetSet(
+ createRemoteAnimationTargetsForTarget(primaryTaskTarget));
+ mRemoteTargetHandles[1].mTransformParams.setTargetSet(
+ createRemoteAnimationTargetsForTarget(secondaryTaskTarget));
+ }
+ }
+
+ private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
+ RemoteAnimationTargetCompat target) {
+ return new RemoteAnimationTargets(new RemoteAnimationTargetCompat[]{target},
+ null, null, MODE_CLOSING);
+ }
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
* @param homeAnimationFactory The home animation factory.
*/
- protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
+ protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
+ // TODO(b/195473584) compute separate end targets for different staged split
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
-
+ RectFSpringAnim[] out = new RectFSpringAnim[mRemoteTargetHandles.length];
Matrix homeToWindowPositionMap = new Matrix();
- final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
- RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+ RectF[] startRects = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
+ for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
+ i < mRemoteTargetHandlesLength; i++) {
+ RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
+ out[i] = getWindowAnimationToHomeInternal(homeAnimationFactory,
+ targetRect, remoteHandle.mTransformParams, remoteHandle.mTaskViewSimulator,
+ startRects[i], homeToWindowPositionMap);
+ }
+ return out;
+ }
+ private RectFSpringAnim getWindowAnimationToHomeInternal(
+ HomeAnimationFactory homeAnimationFactory, RectF targetRect,
+ TransformParams transformParams, TaskViewSimulator taskViewSimulator,
+ RectF startRect, Matrix homeToWindowPositionMap) {
+ RectF cropRectF = new RectF(taskViewSimulator.getCurrentCropRect());
// Move the startRect to Launcher space as floatingIconView runs in Launcher
Matrix windowToHomePositionMap = new Matrix();
homeToWindowPositionMap.invert(windowToHomePositionMap);
@@ -240,7 +343,7 @@
RectFSpringAnim anim;
if (PROTOTYPE_APP_CLOSE.get()) {
anim = new RectFSpringAnim2(startRect, targetRect, mContext,
- mTaskViewSimulator.getCurrentCornerRadius(),
+ taskViewSimulator.getCurrentCornerRadius(),
homeAnimationFactory.getEndRadius(cropRectF));
} else {
anim = new RectFSpringAnim(startRect, targetRect, mContext);
@@ -248,9 +351,10 @@
homeAnimationFactory.setAnimation(anim);
SpringAnimationRunner runner = new SpringAnimationRunner(
- homeAnimationFactory, cropRectF, homeToWindowPositionMap);
- anim.addOnUpdateListener(runner);
+ homeAnimationFactory, cropRectF, homeToWindowPositionMap,
+ transformParams, taskViewSimulator);
anim.addAnimatorListener(runner);
+ anim.addOnUpdateListener(runner);
return anim;
}
@@ -262,6 +366,7 @@
final RectF mWindowCurrentRect = new RectF();
final Matrix mHomeToWindowPositionMap;
+ private final TransformParams mLocalTransformParams;
final HomeAnimationFactory mAnimationFactory;
final AnimatorPlaybackController mHomeAnim;
@@ -271,17 +376,19 @@
final float mEndRadius;
SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
- Matrix homeToWindowPositionMap) {
+ Matrix homeToWindowPositionMap, TransformParams transformParams,
+ TaskViewSimulator taskViewSimulator) {
mAnimationFactory = factory;
mHomeAnim = factory.createActivityAnimationToHome();
mCropRectF = cropRectF;
mHomeToWindowPositionMap = homeToWindowPositionMap;
+ mLocalTransformParams = transformParams;
cropRectF.roundOut(mCropRect);
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
- mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
+ mStartRadius = taskViewSimulator.getCurrentCornerRadius();
mEndRadius = factory.getEndRadius(cropRectF);
}
@@ -300,10 +407,11 @@
if (mAnimationFactory.keepWindowOpaque()) {
alpha = 1f;
}
- mTransformParams
+ mLocalTransformParams
.setTargetAlpha(alpha)
.setCornerRadius(cornerRadius);
- mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
+ mLocalTransformParams.applySurfaceParams(mLocalTransformParams
+ .createSurfaceParams(this));
mAnimationFactory.update(config, currentRect, progress,
mMatrix.mapRadius(cornerRadius));
}
@@ -332,6 +440,21 @@
}
}
+ /**
+ * Container to keep together all the associated objects whose properties need to be updated to
+ * animate a single remote app target
+ */
+ public static class RemoteTargetHandle {
+ public TaskViewSimulator mTaskViewSimulator;
+ public TransformParams mTransformParams;
+ public AnimatorControllerWithResistance mPlaybackController;
+ public RemoteTargetHandle(TaskViewSimulator taskViewSimulator,
+ TransformParams transformParams) {
+ mTransformParams = transformParams;
+ mTaskViewSimulator = taskViewSimulator;
+ }
+ }
+
public interface RunningWindowAnim {
void end();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 11ca4b1..7d2d413 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -632,10 +633,11 @@
* @param cancel true if recents starting is being cancelled.
* @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
*/
- public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+ public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
+ RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
- return mSplitScreen.onGoingToRecentsLegacy(cancel);
+ return mSplitScreen.onGoingToRecentsLegacy(cancel, apps);
} catch (RemoteException e) {
Log.w(TAG, "Failed call onGoingToRecentsLegacy");
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index b5da097..5b9e214 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -154,9 +154,10 @@
boolean isRunningTask = v.isRunningTask();
TransformParams params = null;
TaskViewSimulator tsv = null;
+ // TODO(b/195675206) handle two TSVs here
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
- params = v.getRecentsView().getLiveTileParams();
- tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
+ params = v.getRecentsView().getRemoteTargetHandles()[0].mTransformParams;
+ tsv = v.getRecentsView().getRemoteTargetHandles()[0].mTaskViewSimulator;
}
createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets, nonAppTargets,
depthController, out, params, tsv);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4979206..20eff34 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -99,6 +99,7 @@
import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
+import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.SplitScreenBounds;
@@ -364,6 +365,7 @@
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
ProtoTracer.INSTANCE.get(this).add(this);
+ LauncherSplitScreenListener.INSTANCE.get(this).init();
sConnected = true;
}
@@ -520,6 +522,7 @@
getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+ LauncherSplitScreenListener.INSTANCE.get(this).destroy();
mTaskbarManager.destroy();
sConnected = false;
super.onDestroy();
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 64a428f..765480c 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -79,8 +79,10 @@
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
* from a foreground task.
*/
- public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
- mHomeTaskInfo = homeTaskInfo;
+ public void onGestureAnimationStartOnHome(RunningTaskInfo[] homeTaskInfo) {
+ // TODO(b/195607777) General fallback love, but this might be correct
+ // Home task should be defined as the front-most task info I think?
+ mHomeTaskInfo = homeTaskInfo[0];
onGestureAnimationStart(homeTaskInfo);
}
@@ -92,8 +94,8 @@
@Override
public void onPrepareGestureEndAnimation(
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
- TaskViewSimulator taskViewSimulator) {
- super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulator);
+ TaskViewSimulator[] taskViewSimulators) {
+ super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulators);
if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskViewByTaskId(mHomeTaskInfo.taskId);
if (tv != null) {
@@ -133,7 +135,13 @@
}
@Override
- protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
+ protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
+ if (runningTaskInfos.length > 1) {
+ // can't be in split screen w/ home task
+ return super.shouldAddStubTaskView(runningTaskInfos);
+ }
+
+ RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
if (mHomeTaskInfo != null && runningTaskInfo != null &&
mHomeTaskInfo.taskId == runningTaskInfo.taskId
&& getTaskViewCount() == 0) {
@@ -141,7 +149,7 @@
// show the empty recents message instead of showing a stub task and later removing it.
return false;
}
- return super.shouldAddStubTaskView(runningTaskInfo);
+ return super.shouldAddStubTaskView(runningTaskInfos);
}
@Override
@@ -149,6 +157,7 @@
// When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
// track the index of the next task appropriately, as if we are switching on any other app.
+ // TODO(b/195607777) Confirm home task info is front-most task and not mixed in with others
int runningTaskId = getTaskIdsForRunningTaskView()[0];
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId && !tasks.isEmpty()) {
// Check if the task list has running task
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index b2183d6..04b147c 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -46,6 +46,7 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewComponentObserver;
@@ -263,14 +264,16 @@
void initDp(DeviceProfile dp) {
initTransitionEndpoints(dp);
- mTaskViewSimulator.setPreviewBounds(
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreviewBounds(
new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets());
}
@Override
public void updateFinalShift() {
- mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
- mTaskViewSimulator.apply(mTransformParams);
+ mRemoteTargetHandles[0].mPlaybackController
+ .setProgress(mCurrentShift.value, mDragLengthFactor);
+ mRemoteTargetHandles[0].mTaskViewSimulator.apply(
+ mRemoteTargetHandles[0].mTransformParams);
}
AnimatedFloat getCurrentShift() {
@@ -326,7 +329,8 @@
mFakeIconView.setVisibility(View.INVISIBLE);
}
};
- RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
+ RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift,
+ homeAnimFactory)[0];
windowAnim.start(mContext, velocityPxPerMs);
return windowAnim;
}
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
index baca76c..7c83833 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -158,8 +158,7 @@
Rect startRect = new Rect();
PagedOrientationHandler orientationHandler = params.recentsOrientedState
.getOrientationHandler();
- LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect,
- orientationHandler);
+ LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect);
long distanceToCover = startRect.bottom;
PendingAnimation resistAnim = params.resistAnim != null
? params.resistAnim
diff --git a/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
new file mode 100644
index 0000000..da665d4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
@@ -0,0 +1,104 @@
+package com.android.quickstep.util;
+
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
+import android.content.Context;
+import android.os.IBinder;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import com.android.launcher3.util.SplitConfigurationOptions.StageType;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitTaskPosition;
+import com.android.quickstep.SystemUiProxy;
+import com.android.wm.shell.splitscreen.ISplitScreenListener;
+
+/**
+ * Listeners for system wide split screen position and stage changes.
+ * Use {@link #getSplitTaskIds()} to determine which tasks, if any, are in staged split.
+ */
+public class LauncherSplitScreenListener extends ISplitScreenListener.Stub {
+
+ public static final MainThreadInitializedObject<LauncherSplitScreenListener> INSTANCE =
+ new MainThreadInitializedObject<>(LauncherSplitScreenListener::new);
+
+ private final StagedSplitTaskPosition mMainStagePosition = new StagedSplitTaskPosition();
+ private final StagedSplitTaskPosition mSideStagePosition = new StagedSplitTaskPosition();
+
+ public LauncherSplitScreenListener(Context context) {
+ mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
+ mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
+ }
+
+ /** Also call {@link #destroy()} when done. */
+ public void init() {
+ SystemUiProxy.INSTANCE.getNoCreate().registerSplitScreenListener(this);
+ }
+
+ public void destroy() {
+ SystemUiProxy.INSTANCE.getNoCreate().unregisterSplitScreenListener(this);
+ }
+
+ /**
+ * @return index 0 will be task in left/top position, index 1 in right/bottom position.
+ * Will return empty array if device is not in staged split
+ */
+ public int[] getSplitTaskIds() {
+ if (mMainStagePosition.taskId == -1 || mSideStagePosition.taskId == -1) {
+ return new int[]{};
+ }
+ int[] out = new int[2];
+ if (mMainStagePosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ out[0] = mMainStagePosition.taskId;
+ out[1] = mSideStagePosition.taskId;
+ } else {
+ out[1] = mMainStagePosition.taskId;
+ out[0] = mSideStagePosition.taskId;
+ }
+ return out;
+ }
+
+ @Override
+ public void onStagePositionChanged(@StageType int stage, @StagePosition int position) {
+ if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
+ mMainStagePosition.stagePosition = position;
+ } else {
+ mSideStagePosition.stagePosition = position;
+ }
+ }
+
+ @Override
+ public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
+ // If task is not visible but we are tracking it, stop tracking it
+ if (!visible) {
+ if (mMainStagePosition.taskId == taskId) {
+ resetTaskId(mMainStagePosition);
+ } else if (mSideStagePosition.taskId == taskId) {
+ resetTaskId(mSideStagePosition);
+ } // else it's an un-tracked child
+ return;
+ }
+
+ // If stage has moved to undefined, stop tracking the task
+ if (stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
+ resetTaskId(taskId == mMainStagePosition.taskId ?
+ mMainStagePosition : mSideStagePosition);
+ return;
+ }
+
+ if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
+ mMainStagePosition.taskId = taskId;
+ } else {
+ mSideStagePosition.taskId = taskId;
+ }
+ }
+
+ private void resetTaskId(StagedSplitTaskPosition taskPosition) {
+ taskPosition.taskId = -1;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index ea1ece8..d0fb9e5 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -15,10 +15,15 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.Utilities.comp;
+
+import android.annotation.Nullable;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
+import com.android.launcher3.util.HorizontalInsettableView;
import com.android.unfold.UnfoldTransitionProgressProvider;
import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
@@ -27,10 +32,17 @@
*/
public class LauncherUnfoldAnimationController {
+ // Percentage of the width of the quick search bar that will be reduced
+ // from the both sides of the bar when progress is 0
+ private static final float MAX_WIDTH_INSET_FRACTION = 0.15f;
+
private final Launcher mLauncher;
private final UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
private final UnfoldMoveFromCenterWorkspaceAnimator mMoveFromCenterWorkspaceAnimation;
+ @Nullable
+ private HorizontalInsettableView mQsbInsettable;
+
private final AnimationListener mAnimationListener = new AnimationListener();
private boolean mIsTransitionRunning = false;
@@ -51,6 +63,11 @@
* Called when launcher is resumed
*/
public void onResume() {
+ Hotseat hotseat = mLauncher.getHotseat();
+ if (hotseat != null && hotseat.getQsb() instanceof HorizontalInsettableView) {
+ mQsbInsettable = (HorizontalInsettableView) hotseat.getQsb();
+ }
+
final ViewTreeObserver obs = mLauncher.getWorkspace().getViewTreeObserver();
obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -68,12 +85,13 @@
* Called when launcher activity is paused
*/
public void onPause() {
- mIsReadyToPlayAnimation = false;
-
if (mIsTransitionRunning) {
mIsTransitionRunning = false;
- mMoveFromCenterWorkspaceAnimation.onTransitionFinished();
+ mAnimationListener.onTransitionFinished();
}
+
+ mIsReadyToPlayAnimation = false;
+ mQsbInsettable = null;
}
/**
@@ -109,6 +127,10 @@
public void onTransitionFinished() {
if (mIsReadyToPlayAnimation) {
mMoveFromCenterWorkspaceAnimation.onTransitionFinished();
+
+ if (mQsbInsettable != null) {
+ mQsbInsettable.setHorizontalInsets(0);
+ }
}
mIsTransitionRunning = false;
@@ -117,6 +139,11 @@
@Override
public void onTransitionProgress(float progress) {
mMoveFromCenterWorkspaceAnimation.onTransitionProgress(progress);
+
+ if (mQsbInsettable != null) {
+ float insetPercentage = comp(progress) * MAX_WIDTH_INSET_FRACTION;
+ mQsbInsettable.setHorizontalInsets(insetPercentage);
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 8834dc2..302526d 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -42,8 +42,7 @@
PagedOrientationHandler orientationHandler) {
// Track the bottom of the window.
Rect taskSize = new Rect();
- LauncherActivityInterface.INSTANCE.calculateTaskSize(
- context, dp, taskSize, orientationHandler);
+ LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize);
return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 7eee415..7b1c62e 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
+import static com.android.launcher3.util.SplitConfigurationOptions.*;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
@@ -29,6 +30,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.util.Log;
import androidx.annotation.NonNull;
@@ -49,8 +51,13 @@
*/
public class TaskViewSimulator implements TransformParams.BuilderProxy {
+ private final String TAG = "TaskViewSimulator";
+ private final boolean DEBUG = false;
+
private final Rect mTmpCropRect = new Rect();
private final RectF mTempRectF = new RectF();
+ // Additional offset for split tasks
+ private final Point mSplitOffset = new Point();
private final float[] mTempPoint = new float[2];
private final Context mContext;
@@ -63,6 +70,8 @@
private final Rect mTaskRect = new Rect();
private final PointF mPivot = new PointF();
private DeviceProfile mDp;
+ @StagePosition
+ private int mStagePosition = STAGE_POSITION_UNDEFINED;
private final Matrix mMatrix = new Matrix();
private final Matrix mMatrixTmp = new Matrix();
@@ -89,6 +98,7 @@
// Cached calculations
private boolean mLayoutValid = false;
private int mOrientationStateId;
+ private StagedSplitBounds mStagedSplitBounds;
public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
mContext = context;
@@ -128,9 +138,19 @@
if (mDp == null) {
return 1;
}
- mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect,
- mOrientationState.getOrientationHandler());
- return mOrientationState.getFullScreenScaleAndPivot(mTaskRect, mDp, mPivot);
+ Rect fullTaskSize = new Rect();
+ mSizeStrategy.calculateTaskSize(mContext, mDp, fullTaskSize);
+
+ if (mStagedSplitBounds != null) {
+ // The task rect changes according to the staged split task sizes, but recents
+ // fullscreen scale and pivot remains the same since the task fits into the existing
+ // sized task space bounds
+ mSizeStrategy.calculateStagedSplitTaskSize(mContext, mDp, mTaskRect, mStagedSplitBounds,
+ mStagePosition);
+ } else {
+ mTaskRect.set(fullTaskSize);
+ }
+ return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
}
/**
@@ -143,6 +163,24 @@
}
/**
+ * Sets the targets which the simulator will control specifically for targets to animate when
+ * in split screen
+ *
+ * @param splitInfo set to {@code null} when not in staged split mode
+ */
+ public void setPreview(RemoteAnimationTargetCompat runningTarget, StagedSplitBounds splitInfo) {
+ setPreview(runningTarget);
+ mStagedSplitBounds = splitInfo;
+ if (mStagedSplitBounds == null) {
+ mStagePosition = STAGE_POSITION_UNDEFINED;
+ return;
+ }
+ mStagePosition = mThumbnailPosition.equals(splitInfo.mLeftTopBounds) ?
+ STAGE_POSITION_TOP_OR_LEFT :
+ STAGE_POSITION_BOTTOM_OR_RIGHT;
+ }
+
+ /**
* Sets the targets which the simulator will control
*/
public void setPreviewBounds(Rect bounds, Rect insets) {
@@ -239,6 +277,15 @@
getFullScreenScale();
mThumbnailData.rotation = mOrientationState.getDisplayRotation();
+ // TODO(b/195145340) handle non 50-50 split scenarios
+ if (mStagedSplitBounds != null) {
+ if (mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+ // The preview set is for the bottom/right, inset by top/left task
+ mSplitOffset.y = mStagedSplitBounds.mLeftTopBounds.height() +
+ mStagedSplitBounds.mDividerBounds.height() / 2;
+ }
+ }
+
// mIsRecentsRtl is the inverse of TaskView RTL.
boolean isRtlEnabled = !mIsRecentsRtl;
mPositionHelper.updateThumbnailMatrix(
@@ -246,6 +293,9 @@
mTaskRect.width(), mTaskRect.height(),
mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
+ if (DEBUG) {
+ Log.d(TAG, " taskRect: " + mTaskRect + " splitOffset: " + mSplitOffset);
+ }
}
float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
@@ -280,6 +330,9 @@
recentsViewPrimaryTranslation.value);
applyWindowToHomeRotation(mMatrix);
+ // Move lower/right split window into correct position
+ mMatrix.postTranslate(0, mSplitOffset.y);
+
// Crop rect is the inverse of thumbnail matrix
mTempRectF.set(-insets.left, -insets.top,
taskWidth + insets.right, taskHeight + insets.bottom);
@@ -287,6 +340,25 @@
mTempRectF.roundOut(mTmpCropRect);
params.applySurfaceParams(params.createSurfaceParams(this));
+
+ if (!DEBUG) {
+ return;
+ }
+ Log.d(TAG, "progress: " + fullScreenProgress
+ + " scale: " + scale
+ + " recentsViewScale: " + recentsViewScale.value
+ + " crop: " + mTmpCropRect
+ + " radius: " + getCurrentCornerRadius()
+ + " translate: " + mSplitOffset
+ + " taskW: " + taskWidth + " H: " + taskHeight
+ + " taskRect: " + mTaskRect
+ + " taskPrimaryT: " + taskPrimaryTranslation.value
+ + " recentsPrimaryT: " + recentsViewPrimaryTranslation.value
+ + " recentsSecondaryT: " + recentsViewSecondaryTranslation.value
+ + " taskSecondaryT: " + taskSecondaryTranslation.value
+ + " recentsScroll: " + recentsViewScroll.value
+ + " pivot: " + mPivot
+ );
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
new file mode 100644
index 0000000..cd20f4b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -0,0 +1,137 @@
+package com.android.quickstep.views;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
+ *
+ * That's right. If you call within the next 5 minutes we'll go ahead and double your order and
+ * send you !! TWO !! Tasks along with their TaskThumbnailViews complimentary. On. The. House.
+ * And not only that, we'll even clean up your thumbnail request if you don't like it.
+ * All the benefits of one TaskView, except DOUBLED!
+ *
+ * (Icon loading sold separately, fees may apply. Shipping & Handling for Overlays not included).
+ */
+public class GroupedTaskView extends TaskView {
+
+ private Task mSecondaryTask;
+ private TaskThumbnailView mSnapshotView2;
+ private CancellableTask mThumbnailLoadRequest2;
+
+ public GroupedTaskView(Context context) {
+ super(context);
+ }
+
+ public GroupedTaskView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public GroupedTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSnapshotView2 = findViewById(R.id.bottomright_snapshot);
+ }
+
+ public void bind(Task primary, Task secondary, RecentsOrientedState orientedState) {
+ super.bind(primary, orientedState);
+ mSecondaryTask = secondary;
+ mTaskIdContainer[1] = secondary.key.id;
+ mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2);
+ mSnapshotView2.bind(secondary);
+ adjustThumbnailBoundsForSplit();
+ }
+
+ @Override
+ public void onTaskListVisibilityChanged(boolean visible, int changes) {
+ super.onTaskListVisibilityChanged(visible, changes);
+ if (visible) {
+ RecentsModel model = RecentsModel.INSTANCE.get(getContext());
+ TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
+
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ mThumbnailLoadRequest2 = thumbnailCache.updateThumbnailInBackground(mSecondaryTask,
+ thumbnailData -> mSnapshotView2.setThumbnail(
+ mSecondaryTask, thumbnailData
+ ));
+ }
+
+ if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+ // TODO What's the Icon for this going to look like? :o
+ }
+ } else {
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ mSnapshotView2.setThumbnail(null, null);
+ // Reset the task thumbnail reference as well (it will be fetched from the cache or
+ // reloaded next time we need it)
+ mSecondaryTask.thumbnail = null;
+ }
+ if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+ // TODO
+ }
+ }
+ }
+
+ @Override
+ protected void cancelPendingLoadTasks() {
+ super.cancelPendingLoadTasks();
+ if (mThumbnailLoadRequest2 != null) {
+ mThumbnailLoadRequest2.cancel();
+ mThumbnailLoadRequest2 = null;
+ }
+ }
+
+ @Override
+ public void onRecycle() {
+ super.onRecycle();
+ mSnapshotView2.setThumbnail(mSecondaryTask, null);
+ }
+
+ @Override
+ public void setOverlayEnabled(boolean overlayEnabled) {
+ super.setOverlayEnabled(overlayEnabled);
+ mSnapshotView2.setOverlayEnabled(overlayEnabled);
+ }
+
+ private void adjustThumbnailBoundsForSplit() {
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ ViewGroup.LayoutParams primaryLp = mSnapshotView.getLayoutParams();
+ primaryLp.width = mSecondaryTask == null ?
+ MATCH_PARENT :
+ getWidth();
+ int spaceAboveSnapshot = deviceProfile.overviewTaskThumbnailTopMarginPx;
+ // TODO get divider height
+ int dividerBar = 20;
+ primaryLp.height = mSecondaryTask == null ?
+ MATCH_PARENT :
+ (getHeight() - spaceAboveSnapshot - dividerBar) / 2;
+ mSnapshotView.setLayoutParams(primaryLp);
+
+ if (mSecondaryTask == null) {
+ mSnapshotView2.setVisibility(GONE);
+ return;
+ }
+
+ mSnapshotView2.setVisibility(VISIBLE);
+ ViewGroup.LayoutParams secondaryLp = mSnapshotView2.getLayoutParams();
+ secondaryLp.width = getWidth();
+ secondaryLp.height = primaryLp.height;
+ mSnapshotView2.setLayoutParams(secondaryLp);
+ mSnapshotView2.setTranslationY(primaryLp.height + spaceAboveSnapshot + dividerBar);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 86c3bf6..ef52b41 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -19,6 +19,7 @@
import static android.view.Surface.ROTATION_0;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
@@ -51,6 +52,7 @@
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -143,11 +145,13 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.SwipeUpAnimationLogic.RemoteTargetHandle;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.ViewUtils;
+import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitScreenBounds;
@@ -317,7 +321,13 @@
view.setScaleY(scale);
view.mLastComputedTaskStartPushOutDistance = null;
view.mLastComputedTaskEndPushOutDistance = null;
- view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
+ view.runActionOnRemoteHandles(new Consumer<RemoteTargetHandle>() {
+ @Override
+ public void accept(RemoteTargetHandle remoteTargetHandle) {
+ remoteTargetHandle.mTaskViewSimulator.recentsViewScale.value =
+ scale;
+ }
+ });
view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
view.updatePageOffsets();
}
@@ -365,8 +375,7 @@
// mTaskGridVerticalDiff and mTopBottomRowHeightDiff summed together provides the top
// position for bottom row of grid tasks.
- protected final TransformParams mLiveTileParams = new TransformParams();
- protected final TaskViewSimulator mLiveTileTaskViewSimulator;
+ protected RemoteTargetHandle[] mRemoteTargetHandles;
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
@@ -402,9 +411,10 @@
private final InvariantDeviceProfile mIdp;
/**
- * Getting views should be done via {@link #getTaskViewFromPool()}
+ * Getting views should be done via {@link #getTaskViewFromPool(boolean)}
*/
private final ViewPool<TaskView> mTaskViewPool;
+ private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
private final TaskOverlayFactory mTaskOverlayFactory;
@@ -504,13 +514,13 @@
// Only valid until the launcher state changes to NORMAL
/**
* ID for the current running TaskView view, unique amongst TaskView instances. ID's are set
- * through {@link #getTaskViewFromPool()} and incremented by {@link #mTaskViewIdCount}
+ * through {@link #getTaskViewFromPool(boolean)} and incremented by {@link #mTaskViewIdCount}
*/
protected int mRunningTaskViewId = -1;
private int mTaskViewIdCount;
private final int[] INVALID_TASK_IDS = new int[]{-1, -1};
protected boolean mRunningTaskTileHidden;
- private Task mTmpRunningTask;
+ private Task[] mTmpRunningTasks;
protected int mFocusedTaskViewId = -1;
private boolean mTaskIconScaledDown = false;
@@ -626,6 +636,9 @@
mClearAllButton.setOnClickListener(this::dismissAllTasks);
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
+ // There's only one pair of grouped tasks we can envision at the moment
+ mGroupedTaskViewPool = new ViewPool<>(context, this,
+ R.layout.task_grouped, 2 /* max size */, 1 /* initial size */);
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@@ -656,10 +669,6 @@
// Initialize quickstep specific cache params here, as this is constructed only once
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
- mLiveTileTaskViewSimulator = new TaskViewSimulator(getContext(), getSizeStrategy());
- mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
- mLiveTileTaskViewSimulator.setOrientationState(mOrientationState);
-
mTintingColor = getForegroundScrimDimColor(context);
}
@@ -707,7 +716,7 @@
super.dispatchDraw(canvas);
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
- && mLiveTileParams.getTargetSet() != null) {
+ && mRemoteTargetHandles != null) {
redrawLiveTile();
}
}
@@ -749,9 +758,13 @@
if (mHandleTaskStackChanges) {
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
- Task task = taskView.getTask();
- taskView.getThumbnail().setThumbnail(task, thumbnailData);
- return task;
+ for (TaskView.TaskIdAttributeContainer container :
+ taskView.getTaskIdAttributeContainers()) {
+ if (container == null || taskId != container.getTask().key.id) {
+ continue;
+ }
+ container.getThumbnailView().setThumbnail(container.getTask(), thumbnailData);
+ }
}
}
return null;
@@ -812,7 +825,8 @@
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = new SurfaceTransactionApplier(this);
- mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ .setSyncTransactionApplier(mSyncTransactionApplier));
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
@@ -830,7 +844,8 @@
mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = null;
- mLiveTileParams.setSyncTransactionApplier(null);
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ .setSyncTransactionApplier(null));
executeSideTaskLaunchCallback();
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
@@ -851,9 +866,15 @@
if (child instanceof TaskView && child != mSplitHiddenTaskView
&& child != mMovingTaskView) {
TaskView taskView = (TaskView) child;
- mHasVisibleTaskData.delete(taskView.getTaskIds()[0]);
+ for (int i : taskView.getTaskIds()) {
+ mHasVisibleTaskData.delete(i);
+ }
+ if (child instanceof GroupedTaskView) {
+ mGroupedTaskViewPool.recycle((GroupedTaskView)taskView);
+ } else {
+ mTaskViewPool.recycle(taskView);
+ }
taskView.setTaskViewId(-1);
- mTaskViewPool.recycle(taskView);
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
}
updateTaskStartIndex(child);
@@ -906,10 +927,15 @@
}
}
+ /**
+ * TODO(b/195675206) Check both taskIDs from runningTaskViewId
+ * and launch if either of them is {@param taskId}
+ */
public void launchSideTaskInLiveTileModeForRestartedApp(int taskId) {
int runningTaskViewId = getTaskViewIdFromTaskId(taskId);
if (mRunningTaskViewId != -1 && mRunningTaskViewId == runningTaskViewId) {
- RemoteAnimationTargets targets = getLiveTileParams().getTargetSet();
+ TransformParams params = mRemoteTargetHandles[0].mTransformParams;
+ RemoteAnimationTargets targets = params.getTargetSet();
if (targets != null && targets.findTask(taskId) != null) {
launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
targets.nonApps);
@@ -1015,10 +1041,21 @@
if (!enabled) {
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
- mTmpRunningTask = null;
+ mTmpRunningTasks = null;
if (mSplitSelectStateController.isSplitSelectActive()) {
cancelSplitSelect(false);
}
+ // Remove grouped tasks and recycle once we exit overview
+ int taskCount = getTaskViewCount();
+ for (int i = 0; i < taskCount; i++) {
+ View v = getTaskViewAt(i);
+ if (!(v instanceof GroupedTaskView)) {
+ return;
+ }
+ GroupedTaskView gtv = (GroupedTaskView) v;
+ gtv.onTaskListVisibilityChanged(false);
+ removeView(gtv);
+ }
}
updateLocusId();
}
@@ -1220,18 +1257,32 @@
TaskView ignoreResetTaskView =
mIgnoreResetTaskId == -1 ? null : getTaskViewByTaskId(mIgnoreResetTaskId);
- final int requiredTaskCount = tasks.size();
- if (getTaskViewCount() != requiredTaskCount) {
+ int[] splitTaskIds =
+ LauncherSplitScreenListener.INSTANCE.getNoCreate().getSplitTaskIds();
+ int requiredGroupTaskViews = splitTaskIds.length / 2;
+
+ // Subtract half the number of split tasks and not total number because we've already
+ // added a GroupedTaskView when swipe up gesture happens.
+ // This will need to change if we start showing GroupedTaskViews during swipe up from home
+ int requiredTaskViewCount = tasks.size() - requiredGroupTaskViews;
+
+ if (getTaskViewCount() != requiredTaskViewCount) {
if (indexOfChild(mClearAllButton) != -1) {
removeView(mClearAllButton);
}
- for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
- addView(getTaskViewFromPool());
+
+ for (int i = getTaskViewCount(); i < requiredTaskViewCount; i++) {
+ addView(getTaskViewFromPool(false));
}
- while (getTaskViewCount() > requiredTaskCount) {
+ while (getTaskViewCount() > requiredTaskViewCount) {
removeView(getChildAt(getChildCount() - 1));
}
- if (requiredTaskCount > 0) {
+ while (requiredGroupTaskViews > 0) {
+ // Add to front of list
+ addView(getTaskViewFromPool(true), 0);
+ requiredGroupTaskViews--;
+ }
+ if (requiredTaskViewCount > 0) {
addView(mClearAllButton);
}
}
@@ -1245,12 +1296,28 @@
+ " runningTaskViewId: " + mRunningTaskViewId
+ " forTaskView: " + getTaskViewFromTaskViewId(mRunningTaskViewId));
- // Rebind and reset all task views
- for (int i = requiredTaskCount - 1; i >= 0; i--) {
- final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
- final Task task = tasks.get(i);
+ for (int taskViewIndex = requiredTaskViewCount - 1, taskDataIndex = tasks.size() - 1;
+ taskViewIndex >= 0;
+ taskViewIndex--, taskDataIndex--) {
+ final int pageIndex = requiredTaskViewCount - taskViewIndex - 1 + mTaskViewStartIndex;
+ final Task task = tasks.get(taskDataIndex);
final TaskView taskView = (TaskView) getChildAt(pageIndex);
- taskView.bind(task, mOrientationState);
+ if (taskView instanceof GroupedTaskView) {
+ Task leftTop;
+ Task rightBottom;
+ if (task.key.id == splitTaskIds[0]) {
+ leftTop = task;
+ taskDataIndex--;
+ rightBottom = tasks.get(taskDataIndex);
+ } else {
+ rightBottom = task;
+ taskDataIndex--;
+ leftTop = tasks.get(taskDataIndex);
+ }
+ ((GroupedTaskView) taskView).bind(leftTop, rightBottom, mOrientationState);
+ } else {
+ taskView.bind(task, mOrientationState);
+ }
}
// Keep same previous focused task
@@ -1271,8 +1338,8 @@
newRunningTaskView = getTaskViewByTaskId(runningTaskId);
if (newRunningTaskView == null) {
StringBuilder sb = new StringBuilder();
- for (int i = requiredTaskCount - 1; i >= 0; i--) {
- final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
+ for (int i = requiredTaskViewCount - 1; i >= 0; i--) {
+ final int pageIndex = requiredTaskViewCount - i - 1 + mTaskViewStartIndex;
final TaskView taskView = (TaskView) getChildAt(pageIndex);
int taskViewId = taskView.getTaskViewId();
sb.append(" taskViewId: " + taskViewId
@@ -1356,12 +1423,12 @@
// Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
// to reset the params after it settles in Overview from swipe up so that we don't
// render with obsolete param values.
- mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = 0;
- mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = 0;
- mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
- mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
-
- mLiveTileParams.setTargetAlpha(1);
+ runActionOnRemoteHandles(remoteTargetHandle -> {
+ remoteTargetHandle.mTaskViewSimulator.taskPrimaryTranslation.value = 0;
+ remoteTargetHandle.mTaskViewSimulator.taskSecondaryTranslation.value = 0;
+ remoteTargetHandle.mTaskViewSimulator.fullScreenProgress.value = 0;
+ remoteTargetHandle.mTaskViewSimulator.recentsViewScale.value = 1;
+ });
// Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
// null.
@@ -1415,7 +1482,8 @@
setPageSpacing(dp.overviewPageSpacing);
// Propagate DeviceProfile change event.
- mLiveTileTaskViewSimulator.setDp(dp);
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator.setDp(dp));
mActionsView.setDp(dp);
mOrientationState.setDeviceProfile(dp);
@@ -1526,8 +1594,7 @@
}
public void getTaskSize(Rect outRect) {
- mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect,
- mOrientationHandler);
+ mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
mLastComputedTaskSize.set(outRect);
}
@@ -1535,8 +1602,7 @@
* Returns the size of task selected to enter modal state.
*/
public Point getSelectedTaskSize() {
- mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect,
- mOrientationHandler);
+ mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect);
return new Point(mTempRect.width(), mTempRect.height());
}
@@ -1669,8 +1735,17 @@
visible = lower <= index && index <= upper;
}
if (visible) {
- if (task == mTmpRunningTask) {
- // Skip loading if this is the task that we are animating into
+ boolean skipLoadingTask = false;
+ if (mTmpRunningTasks != null) {
+ for (Task t : mTmpRunningTasks) {
+ if (task == t) {
+ // Skip loading if this is the task that we are animating into
+ skipLoadingTask = true;
+ break;
+ }
+ }
+ }
+ if (skipLoadingTask) {
continue;
}
if (!mHasVisibleTaskData.get(task.key.id)) {
@@ -1745,7 +1820,8 @@
}
}
setEnableDrawingLiveTile(false);
- mLiveTileParams.setTargetSet(null);
+ runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
+ .setTargetSet(null));
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -1812,8 +1888,10 @@
* Handle the edge case where Recents could increment task count very high over long
* period of device usage. Probably will never happen, but meh.
*/
- private TaskView getTaskViewFromPool() {
- TaskView taskView = mTaskViewPool.getView();
+ private <T extends TaskView> T getTaskViewFromPool(boolean isGrouped) {
+ T taskView = isGrouped ?
+ (T) mGroupedTaskViewPool.getView() :
+ (T) mTaskViewPool.getView();
taskView.setTaskViewId(mTaskViewIdCount);
if (mTaskViewIdCount == Integer.MAX_VALUE) {
mTaskViewIdCount = 0;
@@ -1849,7 +1927,7 @@
/**
* Called when a gesture from an app is starting.
*/
- public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
+ public void onGestureAnimationStart(RunningTaskInfo[] runningTaskInfo) {
mGestureActive = true;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
@@ -1915,7 +1993,7 @@
*/
public void onPrepareGestureEndAnimation(
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
- TaskViewSimulator taskViewSimulator) {
+ TaskViewSimulator[] taskViewSimulators) {
mCurrentGestureEndTarget = endTarget;
if (endTarget == GestureState.GestureEndTarget.RECENTS) {
setEnableFreeScroll(true);
@@ -1932,13 +2010,16 @@
runningTaskView.getGridTranslationX(),
runningTaskView.getGridTranslationY());
}
- if (animatorSet == null) {
- setGridProgress(1);
- taskViewSimulator.taskPrimaryTranslation.value = runningTaskPrimaryGridTranslation;
- } else {
- animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
- animatorSet.play(taskViewSimulator.taskPrimaryTranslation.animateToValue(
- runningTaskPrimaryGridTranslation));
+ for (TaskViewSimulator tvs : taskViewSimulators) {
+ if (animatorSet == null) {
+ setGridProgress(1);
+ tvs.taskPrimaryTranslation.value =
+ runningTaskPrimaryGridTranslation;
+ } else {
+ animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+ animatorSet.play(tvs.taskPrimaryTranslation.animateToValue(
+ runningTaskPrimaryGridTranslation));
+ }
}
}
}
@@ -1967,7 +2048,21 @@
/**
* Returns true if we should add a stub taskView for the running task id
*/
- protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
+ protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
+ if (runningTaskInfos.length > 1) {
+ // * Always create new view for GroupedTaskView
+ // * Remove existing associated taskViews for tasks currently in split
+ for (RunningTaskInfo rti : runningTaskInfos) {
+ TaskView taskView = getTaskViewByTaskId(rti.taskId);
+ if (taskView == null) {
+ continue;
+ }
+ taskView.onTaskListVisibilityChanged(false);
+ removeView(taskView);
+ }
+ return true;
+ }
+ RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
return runningTaskInfo != null && getTaskViewByTaskId(runningTaskInfo.taskId) == null;
}
@@ -1977,29 +2072,44 @@
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task.
*/
- public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
+ public void showCurrentTask(RunningTaskInfo[] runningTaskInfo) {
int runningTaskViewId = -1;
+ boolean needGroupTaskView = runningTaskInfo.length > 1;
+ RunningTaskInfo taskInfo = runningTaskInfo[0];
if (shouldAddStubTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
- final TaskView taskView = getTaskViewFromPool();
+ final TaskView taskView;
+ if (needGroupTaskView) {
+ taskView = getTaskViewFromPool(true);
+ RunningTaskInfo secondaryTaskInfo = runningTaskInfo[1];
+ mTmpRunningTasks = new Task[]{
+ Task.from(new TaskKey(taskInfo), taskInfo, false),
+ Task.from(new TaskKey(secondaryTaskInfo), secondaryTaskInfo, false)
+ };
+ addView(taskView, mTaskViewStartIndex);
+ ((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
+ mOrientationState);
+ } else {
+ taskView = getTaskViewFromPool(false);
+ addView(taskView, mTaskViewStartIndex);
+ // The temporary running task is only used for the duration between the start of the
+ // gesture and the task list is loaded and applied
+ mTmpRunningTasks = new Task[]{Task.from(new TaskKey(taskInfo), taskInfo, false)};
+ taskView.bind(mTmpRunningTasks[0], mOrientationState);
+ }
runningTaskViewId = taskView.getTaskViewId();
- addView(taskView, mTaskViewStartIndex);
if (wasEmpty) {
addView(mClearAllButton);
}
- // The temporary running task is only used for the duration between the start of the
- // gesture and the task list is loaded and applied
- mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false);
- taskView.bind(mTmpRunningTask, mOrientationState);
// Measure and layout immediately so that the scroll values is updated instantly
// as the user might be quick-switching
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
- } else if (getTaskViewByTaskId(runningTaskInfo.taskId) != null) {
- runningTaskViewId = getTaskViewByTaskId(runningTaskInfo.taskId).getTaskViewId();
+ } else if (!needGroupTaskView && getTaskViewByTaskId(taskInfo.taskId) != null) {
+ runningTaskViewId = getTaskViewByTaskId(taskInfo.taskId).getTaskViewId();
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -2417,8 +2527,11 @@
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
// alpha is set to 0 so that it can be recycled in the view pool properly
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask()) {
- anim.setFloat(mLiveTileParams, TransformParams.TARGET_ALPHA, 0,
- clampToProgress(ACCEL, 0, 0.5f));
+ runActionOnRemoteHandles(remoteTargetHandle -> {
+ TransformParams params = remoteTargetHandle.mTransformParams;
+ anim.setFloat(params, TransformParams.TARGET_ALPHA, 0,
+ clampToProgress(ACCEL, 0, 0.5f));
+ });
}
anim.setFloat(taskView, VIEW_ALPHA, 0, clampToProgress(ACCEL, 0, 0.5f));
FloatProperty<TaskView> secondaryViewTranslate =
@@ -2437,10 +2550,12 @@
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
- mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
- mOrientationHandler.getSecondaryValue(
- taskView.getTranslationX(),
- taskView.getTranslationY());
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ .taskSecondaryTranslation.value = mOrientationHandler
+ .getSecondaryValue(taskView.getTranslationX(),
+ taskView.getTranslationY()
+ ));
redrawLiveTile();
});
}
@@ -2491,7 +2606,6 @@
boolean showAsGrid = showAsGrid();
int taskCount = getTaskViewCount();
int dismissedIndex = indexOfChild(dismissedTaskView);
- int dismissedTaskId = dismissedTaskView.getTaskIds()[0];
int dismissedTaskViewId = dismissedTaskView.getTaskViewId();
// Grid specific properties.
@@ -2589,9 +2703,14 @@
&& child instanceof TaskView
&& ((TaskView) child).isRunningTask()) {
anim.addOnFrameCallback(() -> {
- mLiveTileTaskViewSimulator.taskPrimaryTranslation.value =
- mOrientationHandler.getPrimaryValue(child.getTranslationX(),
- child.getTranslationY());
+ runActionOnRemoteHandles(
+ remoteTargetHandle ->
+ remoteTargetHandle.mTaskViewSimulator
+ .taskPrimaryTranslation.value =
+ mOrientationHandler.getPrimaryValue(
+ child.getTranslationX(),
+ child.getTranslationY()
+ ));
redrawLiveTile();
});
}
@@ -2673,9 +2792,9 @@
if (ENABLE_QUICKSTEP_LIVE_TILE.get()
&& dismissedTaskView.isRunningTask()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
- () -> removeTaskInternal(dismissedTaskId));
+ () -> removeTaskInternal(dismissedTaskViewId));
} else {
- removeTaskInternal(dismissedTaskId);
+ removeTaskInternal(dismissedTaskViewId);
}
mActivity.getStatsLogManager().logger()
.withItemInfo(dismissedTaskView.getItemInfo())
@@ -2806,9 +2925,17 @@
return lastVisibleIndex;
}
- private void removeTaskInternal(int dismissedTaskId) {
+ private void removeTaskInternal(int dismissedTaskViewId) {
+ int[] taskIds = getTaskIdsForTaskViewId(dismissedTaskViewId);
+ int primaryTaskId = taskIds[0];
+ int secondaryTaskId = taskIds[1];
UI_HELPER_EXECUTOR.getHandler().postDelayed(
- () -> ActivityManagerWrapper.getInstance().removeTask(dismissedTaskId),
+ () -> {
+ ActivityManagerWrapper.getInstance().removeTask(primaryTaskId);
+ if (secondaryTaskId != -1) {
+ ActivityManagerWrapper.getInstance().removeTask(secondaryTaskId);
+ }
+ },
REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
}
@@ -3141,7 +3268,9 @@
mLastComputedTaskStartPushOutDistance = null;
mLastComputedTaskEndPushOutDistance = null;
updatePageOffsets();
- mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ .setScroll(getScrollOffset()));
setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
@@ -3205,7 +3334,9 @@
translationProperty.set(child, totalTranslation);
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& i == getRunningTaskIndex()) {
- mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = totalTranslation;
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ .taskPrimaryTranslation.value = totalTranslation);
redrawLiveTile();
}
}
@@ -3308,7 +3439,9 @@
TaskView task = getTaskViewAt(i);
task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
}
- mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ .recentsViewSecondaryTranslation.value = translation);
}
protected void setTaskViewsPrimarySplitTranslation(float translation) {
@@ -3510,8 +3643,6 @@
resetTaskVisuals();
mSplitHiddenTaskView.setVisibility(VISIBLE);
mSplitHiddenTaskView = null;
- mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
- mSecondSplitHiddenTaskView = null;
mSplitHiddenTaskViewIndex = -1;
if (mFirstFloatingTaskView != null) {
mActivity.getRootView().removeView(mFirstFloatingTaskView);
@@ -3520,6 +3651,8 @@
if (mSecondFloatingTaskView != null) {
mActivity.getRootView().removeView(mSecondFloatingTaskView);
mSecondFloatingTaskView = null;
+ mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
+ mSecondSplitHiddenTaskView = null;
}
}
@@ -3615,10 +3748,12 @@
int runningTaskIndex = recentsView.getRunningTaskIndex();
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
&& runningTaskIndex != taskIndex) {
- anim.play(ObjectAnimator.ofFloat(
- recentsView.getLiveTileTaskViewSimulator().taskPrimaryTranslation,
- AnimatedFloat.VALUE,
- primaryTranslation));
+ for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
+ anim.play(ObjectAnimator.ofFloat(
+ remoteHandle.mTaskViewSimulator.taskPrimaryTranslation,
+ AnimatedFloat.VALUE,
+ primaryTranslation));
+ }
}
int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
@@ -3698,7 +3833,9 @@
mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator);
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
+ .addOverviewToAppAnim(mPendingAnimation, interpolator));
mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
}
mPendingAnimation.addEndListener(isSuccess -> {
@@ -3789,31 +3926,87 @@
}
public void redrawLiveTile() {
- if (mLiveTileParams.getTargetSet() != null) {
- mLiveTileTaskViewSimulator.apply(mLiveTileParams);
- }
+ runActionOnRemoteHandles(remoteTargetHandle -> {
+ TransformParams params = remoteTargetHandle.mTransformParams;
+ if (params.getTargetSet() != null) {
+ remoteTargetHandle.mTaskViewSimulator.apply(params);
+ }
+ });
}
- public TaskViewSimulator getLiveTileTaskViewSimulator() {
- return mLiveTileTaskViewSimulator;
- }
-
- public TransformParams getLiveTileParams() {
- return mLiveTileParams;
+ public RemoteTargetHandle[] getRemoteTargetHandles() {
+ return mRemoteTargetHandles;
}
// TODO: To be removed in a follow up CL
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets recentsAnimationTargets) {
mRecentsAnimationController = recentsAnimationController;
- if (recentsAnimationTargets != null && recentsAnimationTargets.apps.length > 0) {
- if (mSyncTransactionApplier != null) {
- recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
- }
- mLiveTileTaskViewSimulator.setPreview(
- recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]);
- mLiveTileParams.setTargetSet(recentsAnimationTargets);
+ if (recentsAnimationTargets == null || recentsAnimationTargets.apps.length == 0) {
+ return;
}
+
+ if (mSyncTransactionApplier != null) {
+ recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
+ }
+
+ // TODO Consolidate this shared code with SwipeUpAnimationLogic (or mabe just reuse
+ // what that class has and pass it into here
+ mRemoteTargetHandles = new RemoteTargetHandle[recentsAnimationTargets.apps.length];
+ TaskViewSimulator primaryTvs = createTaskViewSimulator();
+ mRemoteTargetHandles[0] = new RemoteTargetHandle(primaryTvs, new TransformParams());
+ if (recentsAnimationTargets.apps.length == 1) {
+ mRemoteTargetHandles[0].mTaskViewSimulator
+ .setPreview(recentsAnimationTargets.apps[0], null);
+ mRemoteTargetHandles[0].mTransformParams.setTargetSet(recentsAnimationTargets);
+ } else {
+ TaskViewSimulator secondaryTvs = createTaskViewSimulator();
+ secondaryTvs.setOrientationState(mOrientationState);
+ secondaryTvs.recentsViewScale.value = 1;
+
+ mRemoteTargetHandles[1] = new RemoteTargetHandle(secondaryTvs, new TransformParams());
+ RemoteAnimationTargetCompat dividerTarget =
+ recentsAnimationTargets.getNonAppTargetOfType(TYPE_DOCK_DIVIDER);
+ RemoteAnimationTargetCompat primaryTaskTarget = recentsAnimationTargets.apps[0];
+ RemoteAnimationTargetCompat secondaryTaskTarget = recentsAnimationTargets.apps[1];
+ SplitConfigurationOptions.StagedSplitBounds
+ info = new SplitConfigurationOptions.StagedSplitBounds(
+ primaryTaskTarget.screenSpaceBounds,
+ secondaryTaskTarget.screenSpaceBounds, dividerTarget.screenSpaceBounds);
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget, info);
+ mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget, info);
+ RemoteAnimationTargets rats = new RemoteAnimationTargets(
+ new RemoteAnimationTargetCompat[]{primaryTaskTarget},
+ recentsAnimationTargets.wallpapers, recentsAnimationTargets.nonApps,
+ MODE_CLOSING
+ );
+ RemoteAnimationTargets splitRats = new RemoteAnimationTargets(
+ new RemoteAnimationTargetCompat[]{secondaryTaskTarget},
+ recentsAnimationTargets.wallpapers, recentsAnimationTargets.nonApps,
+ MODE_CLOSING
+ );
+ mRemoteTargetHandles[0].mTransformParams.setTargetSet(rats);
+ mRemoteTargetHandles[1].mTransformParams.setTargetSet(splitRats);
+ }
+ }
+
+ /** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
+ private void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
+ if (mRemoteTargetHandles == null) {
+ return;
+ }
+
+ for (RemoteTargetHandle handle : mRemoteTargetHandles) {
+ consumer.accept(handle);
+ }
+ }
+
+ private TaskViewSimulator createTaskViewSimulator() {
+ TaskViewSimulator tvs = new TaskViewSimulator(getContext(), getSizeStrategy());
+ tvs.setOrientationState(mOrientationState);
+ tvs.setDp(mActivity.getDeviceProfile());
+ tvs.recentsViewScale.value = 1;
+ return tvs;
}
public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
@@ -4119,14 +4312,42 @@
}
return;
}
- int runningTaskId = getTaskIdsForRunningTaskView()[0];
- switchToScreenshot(mRunningTaskViewId == -1 ? null
- : mRecentsAnimationController.screenshotTask(runningTaskId), onFinishRunnable);
+
+ switchToScreenshotInternal(onFinishRunnable);
+ }
+
+ private void switchToScreenshotInternal(Runnable onFinishRunnable) {
+ TaskView taskView = getRunningTaskView();
+ if (taskView == null) {
+ onFinishRunnable.run();
+ return;
+ }
+
+ taskView.setShowScreenshot(true);
+ for (TaskView.TaskIdAttributeContainer container :
+ taskView.getTaskIdAttributeContainers()) {
+ if (container == null) {
+ continue;
+ }
+
+ ThumbnailData td =
+ mRecentsAnimationController.screenshotTask(container.getTask().key.id);
+ TaskThumbnailView thumbnailView = container.getThumbnailView();
+ if (td != null) {
+ thumbnailView.setThumbnail(container.getTask(), td);
+ } else {
+ thumbnailView.refresh();
+ }
+ }
+ ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
}
/**
* Switch the current running task view to static snapshot mode, using the
* provided thumbnail data as the snapshot.
+ * TODO(b/195609063) Consolidate this method w/ the one above, except this thumbnail data comes
+ * from gesture state, which is a larger change of it having to keep track of multiple tasks.
+ * OR. Maybe it doesn't need to pass in a thumbnail and we can use the exact same flow as above
*/
public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
@@ -4279,7 +4500,8 @@
}
private void dispatchScrollChanged() {
- mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.mTaskViewSimulator.setScroll(getScrollOffset()));
for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
mScrollListeners.get(i).onScrollChanged();
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 1c17f2a..174e1b3 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -91,6 +91,7 @@
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.SwipeUpAnimationLogic.RemoteTargetHandle;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
@@ -100,16 +101,20 @@
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.TaskCornerRadius;
+import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.annotation.Retention;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
+import java.util.stream.Stream;
/**
* A task in the Recents view.
@@ -333,8 +338,8 @@
private final TaskOutlineProvider mOutlineProvider;
- private Task mTask;
- private TaskThumbnailView mSnapshotView;
+ protected Task mTask;
+ protected TaskThumbnailView mSnapshotView;
private IconView mIconView;
private final DigitalWellBeingToast mDigitalWellBeingToast;
private float mFullscreenProgress;
@@ -342,7 +347,7 @@
private float mNonGridScale = 1;
private float mDismissScale = 1;
private final FullscreenDrawParams mCurrentFullscreenParams;
- private final StatefulActivity mActivity;
+ protected final StatefulActivity mActivity;
// Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
private float mDismissTranslationX;
@@ -371,7 +376,12 @@
private float mStableAlpha = 1;
private int mTaskViewId = -1;
- private final int[] mTaskIdContainer = new int[]{-1, -1};
+ /**
+ * Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
+ */
+ protected final int[] mTaskIdContainer = new int[]{-1, -1};
+ protected final TaskIdAttributeContainer[] mTaskIdAttributeContainer =
+ new TaskIdAttributeContainer[2];
private boolean mShowScreenshot;
@@ -522,10 +532,15 @@
cancelPendingLoadTasks();
mTask = task;
mTaskIdContainer[0] = mTask.key.id;
+ mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView);
mSnapshotView.bind(task);
setOrientationState(orientedState);
}
+ public TaskIdAttributeContainer[] getTaskIdAttributeContainers() {
+ return mTaskIdAttributeContainer;
+ }
+
public Task getTask() {
return mTask;
}
@@ -567,7 +582,29 @@
mIsClickableAsLiveTile = false;
RecentsView recentsView = getRecentsView();
- final RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+ RemoteAnimationTargets targets;
+ RemoteTargetHandle[] remoteTargetHandles =
+ recentsView.mRemoteTargetHandles;
+ if (remoteTargetHandles.length == 1) {
+ targets = remoteTargetHandles[0].mTransformParams.getTargetSet();
+ } else {
+ TransformParams topLeftParams = remoteTargetHandles[0].mTransformParams;
+ TransformParams rightBottomParams = remoteTargetHandles[1].mTransformParams;
+ RemoteAnimationTargetCompat[] apps = Stream.concat(
+ Arrays.stream(topLeftParams.getTargetSet().apps),
+ Arrays.stream(rightBottomParams.getTargetSet().apps))
+ .toArray(RemoteAnimationTargetCompat[]::new);
+ RemoteAnimationTargetCompat[] wallpapers = Stream.concat(
+ Arrays.stream(topLeftParams.getTargetSet().wallpapers),
+ Arrays.stream(rightBottomParams.getTargetSet().wallpapers))
+ .toArray(RemoteAnimationTargetCompat[]::new);
+ RemoteAnimationTargetCompat[] nonApps = Stream.concat(
+ Arrays.stream(topLeftParams.getTargetSet().nonApps),
+ Arrays.stream(rightBottomParams.getTargetSet().nonApps))
+ .toArray(RemoteAnimationTargetCompat[]::new);
+ targets = new RemoteAnimationTargets(apps, wallpapers, nonApps,
+ topLeftParams.getTargetSet().targetMode);
+ }
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and
// here, try to launch the task as a non live tile task.
@@ -727,11 +764,11 @@
}
}
- private boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
+ protected boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
return (dataChange & flag) == flag;
}
- private void cancelPendingLoadTasks() {
+ protected void cancelPendingLoadTasks() {
if (mThumbnailLoadRequest != null) {
mThumbnailLoadRequest.cancel();
mThumbnailLoadRequest = null;
@@ -1518,4 +1555,22 @@
}
}
+
+ public class TaskIdAttributeContainer {
+ private final TaskThumbnailView thumbnailView;
+ private final Task task;
+
+ public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView) {
+ this.task = task;
+ this.thumbnailView = thumbnailView;
+ }
+
+ public TaskThumbnailView getThumbnailView() {
+ return thumbnailView;
+ }
+
+ public Task getTask() {
+ return task;
+ }
+ }
}
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index c4ee9ae..25a1739 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -101,8 +101,8 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"மாற்றிய பெயரைச் சேமிக்க, தட்டவும்"</string>
<string name="folder_closed" msgid="4100806530910930934">"கோப்புறை மூடப்பட்டது"</string>
<string name="folder_renamed" msgid="1794088362165669656">"கோப்புறை <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> கோப்புகள்"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> அல்லது அதற்கு அதிகமான கோப்புகள்"</string>
+ <string name="folder_name_format_exact" msgid="8626242716117004803">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ஃபைல்கள்"</string>
+ <string name="folder_name_format_overflow" msgid="4270108890534995199">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> அல்லது அதற்கு அதிகமான ஃபைல்கள்"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"வால்பேப்பர் & ஸ்டைல்"</string>
<string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 6893888..22aa785 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -58,7 +58,7 @@
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"విడ్జెట్ సెట్టింగ్లను మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="widget_education_close_button" msgid="8676165703104836580">"అర్థమైంది"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"విడ్జెట్ సెట్టింగ్లను మార్చండి"</string>
- <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"అప్లికేషన్లను శోధించండి"</string>
+ <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"అప్లికేషన్లను వెతకండి"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"అప్లికేషన్లను లోడ్ చేస్తోంది…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అప్లికేషన్లేవీ కనుగొనబడలేదు"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"మరిన్ని యాప్ల కోసం వెతుకు"</string>
@@ -68,9 +68,9 @@
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"షార్ట్కట్ను తరలించడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కండి & హోల్డ్ చేయండి."</string>
<string name="out_of_space" msgid="6692471482459245734">"ఈ మొదటి స్క్రీన్లో స్థలం లేదు"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"యాప్ల జాబితా"</string>
- <string name="all_apps_button_personal_label" msgid="1315764287305224468">"వ్యక్తిగత యాప్ల జాబితా"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"కార్యాలయ యాప్ల జాబితా"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"యాప్ల లిస్ట్"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"వ్యక్తిగత యాప్ల లిస్ట్"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"కార్యాలయ యాప్ల లిస్ట్"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"తీసివేయి"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్స్టాల్ చేయి"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"యాప్ సమాచారం"</string>
@@ -78,11 +78,11 @@
<string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ను సూచించవద్దు"</string>
<string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్కట్లను ఇన్స్టాల్ చేయడం"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడం"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"హోమ్లో సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_write_settings" msgid="3574213698004620587">"హోమ్ సెట్టింగ్లు మరియు సత్వరమార్గాలను వ్రాయడం"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"హోమ్లో సెట్టింగ్లు మరియు సత్వరమార్గాలను మార్చడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా షార్ట్కట్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్లు మరియు షార్ట్కట్లను చదవడం"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"హోమ్లో సెట్టింగ్లు మరియు షార్ట్కట్లను చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"హోమ్ సెట్టింగ్లు మరియు షార్ట్కట్లను వ్రాయడం"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"హోమ్లో సెట్టింగ్లు మరియు షార్ట్కట్లను మార్చడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="msg_no_phone_permission" msgid="9208659281529857371">"ఫోన్ కాల్స్ను చేసేందుకు <xliff:g id="APP_NAME">%1$s</xliff:g>కి అనుమతి లేదు"</string>
<string name="gadget_error_text" msgid="740356548025791839">"విడ్జెట్ను లోడ్ చేయడం సాధ్యం కాలేదు"</string>
<string name="gadget_setup_text" msgid="1745356155479272374">"సెటప్ను పూర్తి చేయడానికి ట్యాప్ చేయండి"</string>
@@ -126,8 +126,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g>ను ఇన్స్టాల్ చేయడం, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> డౌన్లోడ్ అవుతోంది, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ఇన్స్టాల్ కావడానికి వేచి ఉంది"</string>
- <string name="widgets_list" msgid="796804551140113767">"విడ్జెట్ల జాబితా"</string>
- <string name="widgets_list_closed" msgid="6141506579418771922">"విడ్జెట్ల జాబితా మూసివేయబడింది"</string>
+ <string name="widgets_list" msgid="796804551140113767">"విడ్జెట్ల లిస్ట్"</string>
+ <string name="widgets_list_closed" msgid="6141506579418771922">"విడ్జెట్ల లిస్ట్ మూసివేయబడింది"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"హోమ్ స్క్రీన్కు జోడించండి"</string>
<string name="action_move_here" msgid="2170188780612570250">"అంశాన్ని ఇక్కడికి తరలించు"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"అంశం హోమ్స్క్రీన్కి జోడించబడింది"</string>
@@ -141,7 +141,7 @@
<string name="add_to_folder" msgid="9040534766770853243">"ఈ ఫోల్డర్కి జోడించండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> గల ఫోల్డర్కు జోడించు"</string>
<string name="added_to_folder" msgid="4793259502305558003">"అంశం ఫోల్డర్కు జోడించబడింది"</string>
- <string name="create_folder_with" msgid="4050141361160214248">"ఈ పేరుతో ఫోల్డర్ను సృష్టించండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"ఈ పేరుతో ఫోల్డర్ను క్రియేట్ చేయండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ఫోల్డర్ సృష్టించబడింది"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"హోమ్స్క్రీన్కు తరలించు"</string>
<string name="action_resize" msgid="1802976324781771067">"పరిమాణం మార్చు"</string>
@@ -150,7 +150,7 @@
<string name="action_decrease_width" msgid="1374549771083094654">"వెడల్పును తగ్గించు"</string>
<string name="action_decrease_height" msgid="282377193880900022">"ఎత్తును తగ్గించు"</string>
<string name="widget_resized" msgid="9130327887929620">"విడ్జెట్ పరిమాణం వెడల్పు <xliff:g id="NUMBER_0">%1$s</xliff:g>కి, ఎత్తు <xliff:g id="NUMBER_1">%2$s</xliff:g>కి మార్చబడింది"</string>
- <string name="action_deep_shortcut" msgid="2864038805849372848">"సత్వరమార్గాలు"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"షార్ట్కట్స్"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"షార్ట్కట్లు మరియు నోటిఫికేషన్లు"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"తీసివేయి"</string>
<string name="accessibility_close" msgid="2277148124685870734">"మూసివేస్తుంది"</string>
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index 162367b..b4ac8f6 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -95,13 +95,4 @@
<favorite launcher:uri="market://details?id=com.android.launcher" />
</resolve>
- <!-- Placeholder before we add page pairing in b/196376162 -->
- <resolve
- launcher:screen="1"
- launcher:x="0"
- launcher:y="-4" >
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
- <favorite launcher:uri="#Intent;type=images/*;end" />
- </resolve>
-
</favorites>
diff --git a/res/xml/default_workspace_splitdisplay_5x5.xml b/res/xml/default_workspace_splitdisplay_5x5.xml
new file mode 100644
index 0000000..162367b
--- /dev/null
+++ b/res/xml/default_workspace_splitdisplay_5x5.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Dialer, Messaging, [Maps/Music], Browser, Camera -->
+ <resolve
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
+ <favorite launcher:uri="tel:123" />
+ <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+ <favorite launcher:uri="sms:" />
+ <favorite launcher:uri="smsto:" />
+ <favorite launcher:uri="mms:" />
+ <favorite launcher:uri="mmsto:" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;end" />
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MUSIC;end" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+ <favorite launcher:uri="http://www.example.com/" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+ <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+ </resolve>
+
+ <!-- Bottom row -->
+ <resolve
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="-1" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+ <favorite launcher:uri="mailto:" />
+
+ </resolve>
+
+ <resolve
+ launcher:screen="0"
+ launcher:x="1"
+ launcher:y="-1" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+ <favorite launcher:uri="#Intent;type=images/*;end" />
+
+ </resolve>
+
+ <resolve
+ launcher:screen="0"
+ launcher:x="4"
+ launcher:y="-1" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
+ <favorite launcher:uri="market://details?id=com.android.launcher" />
+ </resolve>
+
+ <!-- Placeholder before we add page pairing in b/196376162 -->
+ <resolve
+ launcher:screen="1"
+ launcher:x="0"
+ launcher:y="-4" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+ <favorite launcher:uri="#Intent;type=images/*;end" />
+ </resolve>
+
+</favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 256999c..d8ee6f2 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -105,7 +105,8 @@
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:dbFile="launcher.db"
- launcher:defaultLayoutId="@xml/default_workspace_5x5" >
+ launcher:defaultLayoutId="@xml/default_workspace_5x5"
+ launcher:defaultSplitDisplayLayoutId="@xml/default_workspace_splitdisplay_5x5" >
<display-option
launcher:name="Large Phone"
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 4979b40..e080537 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -64,7 +64,8 @@
TYPE_OPTIONS_POPUP,
TYPE_ICON_SURFACE,
TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP,
- TYPE_WIDGETS_EDUCATION_DIALOG
+ TYPE_WIDGETS_EDUCATION_DIALOG,
+ TYPE_TASKBAR_EDUCATION_DIALOG
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
@@ -87,18 +88,20 @@
public static final int TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP = 1 << 14;
public static final int TYPE_WIDGETS_EDUCATION_DIALOG = 1 << 15;
+ public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
| TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
- | TYPE_WIDGETS_EDUCATION_DIALOG;
+ | TYPE_WIDGETS_EDUCATION_DIALOG | TYPE_TASKBAR_EDUCATION_DIALOG;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
- | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG;
+ | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG
+ | TYPE_TASKBAR_EDUCATION_DIALOG;
// Usually we show the back button when a floating view is open. Instead, hide for these types.
public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index d20738e..3010be1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -220,6 +220,7 @@
// DragController
public int flingToDeleteThresholdVelocity;
+ /** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
boolean useTwoPanels) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 6966abf..7b6a5bf 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -72,6 +72,7 @@
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -383,7 +384,13 @@
loaderResults.bindWidgets();
return true;
} else {
- startLoaderForResults(loaderResults);
+ stopLoader();
+ mLoaderTask = new LoaderTask(
+ mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
+
+ // Always post the loader task, instead of running directly
+ // (even on same thread) so that we exit any nested synchronized blocks
+ MODEL_EXECUTOR.post(mLoaderTask);
}
}
}
@@ -406,25 +413,17 @@
}
}
- public void startLoaderForResults(LoaderResults results) {
+ /**
+ * Loads the model if not loaded
+ * @param callback called with the data model upon successful load or null on model thread.
+ */
+ public void loadAsync(Consumer<BgDataModel> callback) {
synchronized (mLock) {
- stopLoader();
- mLoaderTask = new LoaderTask(
- mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, results);
-
- // Always post the loader task, instead of running directly (even on same thread) so
- // that we exit any nested synchronized blocks
- MODEL_EXECUTOR.post(mLoaderTask);
- }
- }
-
- public void startLoaderForResultsIfNotLoaded(LoaderResults results) {
- synchronized (mLock) {
- if (!isModelLoaded()) {
- Log.d(TAG, "Workspace not loaded, loading now");
- startLoaderForResults(results);
+ if (!mModelLoaded && !mIsLoaderTaskRunning) {
+ startLoader();
}
}
+ MODEL_EXECUTOR.post(() -> callback.accept(isModelLoaded() ? mBgDataModel : null));
}
@Override
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index da701a8..38a957d 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -219,6 +219,9 @@
public static final BooleanFlag ENABLE_TASKBAR = getDebugFlag(
"ENABLE_TASKBAR", true, "Allows a system Taskbar to be shown on larger devices.");
+ public static final BooleanFlag ENABLE_TASKBAR_EDU = getDebugFlag("ENABLE_TASKBAR_EDU", false,
+ "Enables showing taskbar education the first time an app is opened.");
+
public static final BooleanFlag ENABLE_OVERVIEW_GRID = getDebugFlag(
"ENABLE_OVERVIEW_GRID", true, "Uses grid overview layout. "
+ "Only applicable on large screen devices.");
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index f7dd6b2..af006d6 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -49,7 +49,6 @@
import com.android.launcher3.model.GridSizeMigrationTaskV2;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDelegate;
-import com.android.launcher3.model.ModelPreload;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
@@ -185,18 +184,13 @@
}
}.run();
} else {
- new ModelPreload() {
-
- @Override
- public void onComplete(boolean isSuccess) {
- if (isSuccess) {
- MAIN_EXECUTOR.execute(() ->
- renderView(inflationContext, getBgDataModel(), null));
- } else {
- Log.e(TAG, "Model loading failed");
- }
+ LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> {
+ if (dataModel != null) {
+ MAIN_EXECUTOR.execute(() -> renderView(inflationContext, dataModel, null));
+ } else {
+ Log.e(TAG, "Model loading failed");
}
- }.start(inflationContext);
+ });
}
}
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
deleted file mode 100644
index 756b7da..0000000
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2018 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.model;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.Context;
-import android.util.Log;
-
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.ModelUpdateTask;
-import com.android.launcher3.model.BgDataModel.Callbacks;
-
-import java.util.concurrent.Executor;
-
-/**
- * Utility class to preload LauncherModel
- */
-public class ModelPreload implements ModelUpdateTask {
-
- private static final String TAG = "ModelPreload";
-
- private LauncherAppState mApp;
- private LauncherModel mModel;
- private BgDataModel mBgDataModel;
- private AllAppsList mAllAppsList;
-
- @Override
- public final void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
- AllAppsList allAppsList, Executor uiExecutor) {
- mApp = app;
- mModel = model;
- mBgDataModel = dataModel;
- mAllAppsList = allAppsList;
- }
-
- @Override
- public final void run() {
- mModel.startLoaderForResultsIfNotLoaded(
- new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
- MODEL_EXECUTOR.post(() -> {
- Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
- onComplete(mModel.isModelLoaded());
- });
- }
-
- public BgDataModel getBgDataModel() {
- return mBgDataModel;
- }
-
- /**
- * Called when the task is complete
- */
- @WorkerThread
- public void onComplete(boolean isSuccess) { }
-
- public void start(Context context) {
- LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(this);
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/HorizontalInsettableView.java b/src/com/android/launcher3/util/HorizontalInsettableView.java
new file mode 100644
index 0000000..7979bc0
--- /dev/null
+++ b/src/com/android/launcher3/util/HorizontalInsettableView.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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;
+
+/**
+ * Allows the implementing view to add insets to the left and right.
+ */
+public interface HorizontalInsettableView {
+
+ /**
+ * Sets left and right insets for the view so it looks like the width of the view is
+ * reduced when inset is increased.
+ *
+ * The inset is calculated based on the width of the view: e.g. when the width of
+ * the view is 100px then if we apply 0.15f horizontal inset percentage the rendered width
+ * of the view will be 70px with 15px of padding on the left and right sides.
+ *
+ * @param insetPercentage width percentage to inset the content from the left and from the right
+ */
+ void setHorizontalInsets(float insetPercentage);
+
+}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 40bc9c3..cf1467a 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -38,6 +38,7 @@
public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
public static final String SEARCH_EDU_SEEN = "launcher.search_edu_seen";
public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count";
+ public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen";
/**
* Events that either have happened or have not (booleans).
@@ -45,7 +46,8 @@
@StringDef(value = {
HOME_BOUNCE_SEEN,
HOTSEAT_LONGPRESS_TIP_SEEN,
- SEARCH_EDU_SEEN
+ SEARCH_EDU_SEEN,
+ TASKBAR_EDU_SEEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventBoolKey {
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 573c8bd..1f1db9d 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -18,6 +18,8 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.graphics.Rect;
+
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
@@ -82,4 +84,25 @@
mStageType = stageType;
}
}
+
+ public static class StagedSplitBounds {
+ public final Rect mLeftTopBounds;
+ public final Rect mRightBottomBounds;
+ public final Rect mDividerBounds;
+
+
+ public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, Rect dividerBounds) {
+ mLeftTopBounds = leftTopBounds;
+ mRightBottomBounds = rightBottomBounds;
+ mDividerBounds = dividerBounds;
+ }
+ }
+
+ public static class StagedSplitTaskPosition {
+ public int taskId = -1;
+ @StagePosition
+ public int stagePosition = STAGE_POSITION_UNDEFINED;
+ @StageType
+ public int stageType = STAGE_TYPE_UNDEFINED;
+ }
}