Merge "Explicitly set mAllowGoingDown = false when only up is allowed" into sc-dev
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 842abc3..7ab09c5 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -103,16 +103,40 @@
android:clearTaskOnLaunch="true"
android:exported="false"/>
+ <!--
+ Activity for gesture nav onboarding.
+ It's protected by android.permission.REBOOT to ensure that only system apps can start it
+ (setup wizard already has this permission)
+ -->
<activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
- android:autoRemoveFromRecents="true"
- android:excludeFromRecents="true"
- android:screenOrientation="portrait"
- android:exported="true">
+ android:autoRemoveFromRecents="true"
+ android:excludeFromRecents="true"
+ android:screenOrientation="portrait"
+ android:permission="android.permission.REBOOT"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
+
+ <!--
+ Activity following gesture nav onboarding.
+ It's protected by android.permission.REBOOT to ensure that only system apps can start it
+ (setup wizard already has this permission)
+ -->
+ <activity android:name="com.android.quickstep.interaction.AllSetActivity"
+ android:autoRemoveFromRecents="true"
+ android:excludeFromRecents="true"
+ android:screenOrientation="portrait"
+ android:permission="android.permission.REBOOT"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.quickstep.action.GESTURE_ONBOARDING_ALL_SET"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
<activity
android:name=".hybridhotseat.HotseatEduActivity"
android:theme="@android:style/Theme.NoDisplay"
diff --git a/quickstep/res/drawable/ic_all_set.xml b/quickstep/res/drawable/ic_all_set.xml
new file mode 100644
index 0000000..a6852aa
--- /dev/null
+++ b/quickstep/res/drawable/ic_all_set.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="42dp"
+ android:height="40dp"
+ android:viewportWidth="42"
+ android:viewportHeight="40">
+ <path
+ android:pathData="M38,14H25.38L27.28,4.86L27.34,4.22C27.34,3.4 27,2.64 26.46,2.1L24.34,0C24.34,0 10.16,13.7 10,14H0V40H32C33.66,40 35.08,39 35.68,37.56L41.72,23.46C41.9,23 42,22.52 42,22V18C42,15.8 40.2,14 38,14ZM10,36H4V18H10V36ZM38,22L32,36H14V16L22.68,7.32L20,18H38V22Z"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_ime_switcher.xml b/quickstep/res/drawable/ic_ime_switcher.xml
new file mode 100644
index 0000000..a86d390
--- /dev/null
+++ b/quickstep/res/drawable/ic_ime_switcher.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,7h2v2h-2V7zM15,7h2v2h-2V7zM3,7h2v2H3V7zM7,7h2v2H7V7zM11,7h2v2h-2V7zM19,11h2v2h-2V11zM15,11h2v2h-2V11zM3,11h2v2H3V11zM7,11h2v2H7V11zM11,11h2v2h-2V11zM7,15h10v2H7V15z"
+ android:fillColor="@android:color/white" />
+</vector>
diff --git a/quickstep/res/drawable/ic_sysbar_back.xml b/quickstep/res/drawable/ic_sysbar_back.xml
new file mode 100644
index 0000000..1eea677
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_back.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:autoMirrored="true"
+ android:viewportWidth="28"
+ android:viewportHeight="28">
+
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6.49,14.86c-0.66-0.39-0.66-1.34,0-1.73l6.02-3.53l5.89-3.46C19.11,5.73,20,6.26,20,7.1V14v6.9 c0,0.84-0.89,1.37-1.6,0.95l-5.89-3.46L6.49,14.86z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_home.xml b/quickstep/res/drawable/ic_sysbar_home.xml
new file mode 100644
index 0000000..b4b397b
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_home.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28"
+ android:viewportHeight="28">
+
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M 14 7 C 17.8659932488 7 21 10.1340067512 21 14 C 21 17.8659932488 17.8659932488 21 14 21 C 10.1340067512 21 7 17.8659932488 7 14 C 7 10.1340067512 10.1340067512 7 14 7 Z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_recent.xml b/quickstep/res/drawable/ic_sysbar_recent.xml
new file mode 100644
index 0000000..f8c4778
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_recent.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28"
+ android:viewportHeight="28">
+
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19.9,21.5H8.1c-0.88,0-1.6-0.72-1.6-1.6V8.1c0-0.88,0.72-1.6,1.6-1.6h11.8c0.88,0,1.6,0.72,1.6,1.6v11.8 C21.5,20.78,20.78,21.5,19.9,21.5z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
new file mode 100644
index 0000000..d6160de
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/taskbar_icon_selection_ripple">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="8dp" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
new file mode 100644
index 0000000..a6a17e5
--- /dev/null
+++ b/quickstep/res/layout/activity_allset.xml
@@ -0,0 +1,80 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/allset_page_margin_horizontal"
+ android:paddingEnd="@dimen/allset_page_margin_horizontal"
+ android:layoutDirection="locale"
+ android:textDirection="locale"
+ android:background="?android:attr/colorBackground">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_gravity="start"
+ android:gravity="start"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/allset_title_icon_margin_top"
+ android:src="@drawable/ic_all_set"/>
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/allset_title_margin_top"
+ android:gravity="start"
+ android:text="@string/allset_title"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/allset_subtitle_margin_top"
+ android:gravity="start"
+ android:text="@string/allset_description"/>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/navigation_settings"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:textSize="14sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/hint"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="72dp"/>
+
+ <TextView
+ android:id="@id/hint"
+ style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+ android:textSize="14sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/allset_hint_margin_bottom"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:text="@string/allset_hint"/>
+</RelativeLayout>
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index d7bcd9e..f303f31 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -15,12 +15,6 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <com.android.quickstep.views.SplitPlaceholderView
- android:id="@+id/split_placeholder"
- android:layout_width="match_parent"
- android:layout_height="@dimen/split_placeholder_size"
- android:background="@android:color/darker_gray"
- android:visibility="gone" />
<com.android.quickstep.views.LauncherRecentsView
android:id="@+id/overview_panel"
@@ -31,6 +25,13 @@
android:clipToPadding="false"
android:visibility="invisible" />
+ <com.android.quickstep.views.SplitPlaceholderView
+ android:id="@+id/split_placeholder"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/split_placeholder_size"
+ android:background="@android:color/darker_gray"
+ android:visibility="gone" />
+
<include
android:id="@+id/overview_actions_view"
layout="@layout/overview_actions_container" />
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 732222a..240fe55 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -26,4 +26,10 @@
android:layout_height="wrap_content"
android:gravity="center"/>
+ <com.android.launcher3.taskbar.ImeBarView
+ android:id="@+id/ime_bar_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"/>
+
</com.android.launcher3.taskbar.TaskbarContainerView>
\ No newline at end of file
diff --git a/quickstep/res/values/attrs.xml b/quickstep/res/values/attrs.xml
new file mode 100644
index 0000000..336fb57
--- /dev/null
+++ b/quickstep/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<resources>
+ <declare-styleable name="AllSetLinkSpan">
+ <attr name="android:textSize"/>
+ <attr name="android:fontFamily"/>
+ </declare-styleable>
+</resources>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 3bc8ddc..167c7c3 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -27,4 +27,5 @@
<!-- Taskbar -->
<color name="taskbar_background">#101010</color>
+ <color name="taskbar_icon_selection_ripple">#E0E0E0</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 9c0a083..4f9b3eb 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -99,6 +99,13 @@
<dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
<dimen name="gesture_tutorial_button_margin_start_end">18dp</dimen>
+ <!-- All Set page -->
+ <dimen name="allset_page_margin_horizontal">40dp</dimen>
+ <dimen name="allset_title_margin_top">28dp</dimen>
+ <dimen name="allset_title_icon_margin_top">80dp</dimen>
+ <dimen name="allset_hint_margin_bottom">52dp</dimen>
+ <dimen name="allset_subtitle_margin_top">24dp</dimen>
+
<!-- All Apps Education tutorial -->
<dimen name="swipe_edu_padding">8dp</dimen>
<dimen name="swipe_edu_circle_size">64dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a0f1638..7ada496 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -177,6 +177,15 @@
<!-- Feedback subtext displaying the current step and the total number of steps for the tutorial. [CHAR LIMIT=30] -->
<string name="gesture_tutorial_step">Tutorial <xliff:g id="current">%1$d</xliff:g>/<xliff:g id="total">%2$d</xliff:g></string>
+ <!-- Title of "All Set" page [CHAR LIMIT=NONE] -->
+ <string name="allset_title">All set!</string>
+ <!-- Hint string at the bottom of "All Set" page [CHAR LIMIT=NONE] -->
+ <string name="allset_hint">Swipe up to go home</string>
+ <!-- Description of "All Set" page [CHAR LIMIT=NONE] -->
+ <string name="allset_description">You\u2019re ready to start using your phone</string>
+ <!-- String linking to navigation settings on "All Set" page [CHAR LIMIT=NONE] -->
+ <string name="allset_navigation_settings"><annotation id="link">Navigation settings for accessibility</annotation></string>
+
<!-- ******* Overview ******* -->
<!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
<string name="action_share">Share</string>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 7c7d20a..0a8ecb8 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -59,7 +59,7 @@
parent="TextAppearance.GestureTutorial">
<item name="android:gravity">start</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">google-sans</item>
+ <item name="android:fontFamily">google-sans-text</item>
<item name="android:letterSpacing">0.03</item>
<item name="android:textSize">18sp</item>
<item name="android:lineHeight">24sp</item>
@@ -99,6 +99,11 @@
<item name="android:textColor">@color/gesture_tutorial_primary_color</item>
</style>
+ <style name="TextAppearance.GestureTutorial.LinkText"
+ parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
+ <item name="android:textSize">14sp</item>
+ </style>
+
<!--
Can be applied to views to color things like ripples and list highlights the workspace text
color.
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index cd22196..2aac877 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@@ -30,8 +31,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.IBinder;
import android.view.View;
import androidx.annotation.Nullable;
@@ -61,6 +65,7 @@
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController;
@@ -82,6 +87,8 @@
private DepthController mDepthController = new DepthController(this);
private QuickstepTransitionManager mAppTransitionManager;
+ private ServiceConnection mTisBinderConnection;
+ protected TouchInteractionService.TISBinder mTisBinder;
/**
* Reusable command for applying the back button alpha on the background thread.
@@ -103,6 +110,24 @@
super.onCreate(savedInstanceState);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
addMultiWindowModeChangedListener(mDepthController);
+ setupTouchInteractionServiceBinder();
+ }
+
+ private void setupTouchInteractionServiceBinder() {
+ Intent intent = new Intent(this, TouchInteractionService.class);
+ mTisBinderConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder binder) {
+ mTisBinder = ((TouchInteractionService.TISBinder) binder);
+ mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ mTisBinder = null;
+ }
+ };
+ bindService(intent, mTisBinderConnection, 0);
}
@Override
@@ -113,6 +138,10 @@
if (mTaskbarController != null) {
mTaskbarController.cleanup();
mTaskbarController = null;
+ if (mTisBinder != null) {
+ mTisBinder.setTaskbarOverviewProxyDelegate(null);
+ unbindService(mTisBinderConnection);
+ }
}
super.onDestroy();
@@ -248,6 +277,9 @@
private void addTaskbarIfNecessary() {
if (mTaskbarController != null) {
mTaskbarController.cleanup();
+ if (mTisBinder != null) {
+ mTisBinder.setTaskbarOverviewProxyDelegate(null);
+ }
mTaskbarController = null;
}
if (mDeviceProfile.isTaskbarPresent) {
@@ -256,6 +288,9 @@
mTaskbarController = new TaskbarController(this,
taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome);
mTaskbarController.init();
+ if (mTisBinder != null) {
+ mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
+ }
}
}
@@ -343,7 +378,7 @@
@Override
public float[] getNormalOverviewScaleAndOffset() {
return SysUINavigationMode.getMode(this).hasGestures
- ? new float[] {1, 1} : new float[] {1.1f, 0};
+ ? new float[] {1, NO_OFFSET, 1} : new float[] {1.1f, NO_OFFSET, NO_OFFSET};
}
@Override
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index b0c13f9..a9c2a5e 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -18,7 +18,6 @@
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.formatElapsedTime;
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
@@ -252,11 +251,9 @@
}
@Override
- public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
- if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
- // Reinitialize everything
- Executors.MODEL_EXECUTOR.execute(this::recreatePredictors);
- }
+ public void onIdpChanged(InvariantDeviceProfile profile) {
+ // Reinitialize everything
+ Executors.MODEL_EXECUTOR.execute(this::recreatePredictors);
}
private void onAppTargetEvent(AppTargetEvent event, int client) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
new file mode 100644
index 0000000..0d4130d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 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 android.annotation.DrawableRes;
+import android.content.Context;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.launcher3.R;
+
+/**
+ * Creates Buttons for Taskbar for 3 button nav.
+ * Can add animations and state management for buttons in this class as things progress.
+ */
+public class ButtonProvider {
+
+ private int mMarginLeftRight;
+ private final Context mContext;
+
+ public ButtonProvider(Context context) {
+ mContext = context;
+ }
+
+ public void setMarginLeftRight(int margin) {
+ mMarginLeftRight = margin;
+ }
+
+ public View getBack() {
+ // Back button
+ return getButtonForDrawable(R.drawable.ic_sysbar_back);
+ }
+
+ public View getDown() {
+ // Ime down button
+ return getButtonForDrawable(R.drawable.ic_sysbar_back);
+ }
+
+ public View getHome() {
+ // Home button
+ return getButtonForDrawable(R.drawable.ic_sysbar_home);
+ }
+
+ public View getRecents() {
+ // Recents button
+ return getButtonForDrawable(R.drawable.ic_sysbar_recent);
+ }
+
+ public View getImeSwitcher() {
+ // IME Switcher Button
+ return getButtonForDrawable(R.drawable.ic_ime_switcher);
+ }
+
+ private View getButtonForDrawable(@DrawableRes int drawableId) {
+ ImageView buttonView = new ImageView(mContext);
+ buttonView.setImageResource(drawableId);
+ buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
+ buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
+ return buttonView;
+ }
+
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
new file mode 100644
index 0000000..bb3669b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 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.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import com.android.launcher3.views.ActivityContext;
+
+public class ImeBarView extends RelativeLayout {
+
+ private ButtonProvider mButtonProvider;
+ private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
+ private View mImeView;
+
+ public ImeBarView(Context context) {
+ this(context, null);
+ }
+
+ public ImeBarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ImeBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void construct(ButtonProvider buttonProvider) {
+ mButtonProvider = buttonProvider;
+ }
+
+ public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) {
+ mControllerCallbacks = taskbarCallbacks;
+ ActivityContext context = getActivityContext();
+ RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
+ context.getDeviceProfile().iconSizePx,
+ context.getDeviceProfile().iconSizePx
+ );
+ RelativeLayout.LayoutParams downParams = new RelativeLayout.LayoutParams(imeParams);
+
+ imeParams.addRule(ALIGN_PARENT_END);
+ imeParams.setMarginEnd(context.getDeviceProfile().iconSizePx);
+ downParams.setMarginStart(context.getDeviceProfile().iconSizePx);
+ downParams.addRule(ALIGN_PARENT_START);
+
+ // Down Arrow
+ View downView = mButtonProvider.getDown();
+ downView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+ BUTTON_BACK));
+ downView.setLayoutParams(downParams);
+ downView.setRotation(-90);
+ addView(downView);
+
+ // IME switcher button
+ mImeView = mButtonProvider.getImeSwitcher();
+ mImeView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+ BUTTON_IME_SWITCH));
+ mImeView.setLayoutParams(imeParams);
+ addView(mImeView);
+ }
+
+ public void cleanup() {
+ removeAllViews();
+ }
+
+ public void setImeSwitcherVisibility(boolean show) {
+ mImeView.setVisibility(show ? VISIBLE : GONE);
+ }
+
+ private <T extends Context & ActivityContext> T getActivityContext() {
+ return ActivityContext.lookupContext(getContext());
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
index 46e4506..29f6935 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
@@ -44,7 +44,7 @@
private final AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
this::updateVisibilityAlpha);
private final AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
- this::updateVisibilityAlpha);
+ this::updateVisibilityAlphaForIme);
// Scale.
private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat(
@@ -110,16 +110,22 @@
// We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
// assumption being that Taskbar should always be visible regardless of the current
// LauncherState if Launcher is paused.
+ float alphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
mTaskbarVisibilityAlphaForLauncherState.value);
- float alphaDueToOther = mTaskbarVisibilityAlphaForIme.value;
- float taskbarAlpha = alphaDueToLauncher * alphaDueToOther;
+ float taskbarAlpha = alphaDueToLauncher * alphaDueToIme;
mTaskbarCallbacks.updateTaskbarVisibilityAlpha(taskbarAlpha);
// Make the nav bar invisible if taskbar is visible.
setNavBarButtonAlpha(1f - taskbarAlpha);
}
+ private void updateVisibilityAlphaForIme() {
+ updateVisibilityAlpha();
+ float taskbarAlphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
+ mTaskbarCallbacks.updateImeBarVisibilityAlpha(1f - taskbarAlphaDueToIme);
+ }
+
private void updateScale() {
// We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
// assumption being that Taskbar should always be at scale 1f regardless of the current
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index c93de00..6084e10 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.taskbar;
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -28,6 +31,7 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@@ -48,9 +52,12 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.TouchInteractionService.TaskbarOverviewProxyDelegate;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -58,13 +65,15 @@
/**
* Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
*/
-public class TaskbarController {
+public class TaskbarController implements TaskbarOverviewProxyDelegate {
private static final String WINDOW_TITLE = "Taskbar";
private final TaskbarContainerView mTaskbarContainerView;
private final TaskbarView mTaskbarViewInApp;
private final TaskbarView mTaskbarViewOnHome;
+ private final ImeBarView mImeBarView;
+
private final BaseQuickstepLauncher mLauncher;
private final WindowManager mWindowManager;
// Layout width and height of the Taskbar in the default state.
@@ -73,9 +82,13 @@
private final TaskbarAnimationController mTaskbarAnimationController;
private final TaskbarHotseatController mHotseatController;
private final TaskbarDragController mDragController;
+ private final TaskbarNavButtonController mNavButtonController;
// Initialized in init().
private WindowManager.LayoutParams mWindowLayoutParams;
+ private SysUINavigationMode.Mode mNavMode = SysUINavigationMode.Mode.NO_BUTTON;
+ private final SysUINavigationMode.NavigationModeChangeListener mNavigationModeChangeListener =
+ this::onNavModeChanged;
private @Nullable Animator mAnimator;
private boolean mIsAnimatingToLauncher;
@@ -85,10 +98,14 @@
mLauncher = launcher;
mTaskbarContainerView = taskbarContainerView;
mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
+ ButtonProvider buttonProvider = new ButtonProvider(launcher);
mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view);
- mTaskbarViewInApp.construct(createTaskbarViewCallbacks());
+ mTaskbarViewInApp.construct(createTaskbarViewCallbacks(), buttonProvider);
mTaskbarViewOnHome = taskbarViewOnHome;
- mTaskbarViewOnHome.construct(createTaskbarViewCallbacks());
+ mTaskbarViewOnHome.construct(createTaskbarViewCallbacks(), buttonProvider);
+ mImeBarView = mTaskbarContainerView.findViewById(R.id.ime_bar_view);
+ mImeBarView.construct(buttonProvider);
+ mNavButtonController = new TaskbarNavButtonController(launcher);
mWindowManager = mLauncher.getWindowManager();
mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
@@ -108,11 +125,21 @@
@Override
public void updateTaskbarVisibilityAlpha(float alpha) {
- mTaskbarContainerView.setAlpha(alpha);
+ mTaskbarViewInApp.setAlpha(alpha);
mTaskbarViewOnHome.setAlpha(alpha);
}
@Override
+ public void updateImeBarVisibilityAlpha(float alpha) {
+ if (mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
+ // TODO Remove sysui IME bar for gesture nav as well
+ return;
+ }
+ mImeBarView.setAlpha(alpha);
+ mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
+ }
+
+ @Override
public void updateTaskbarScale(float scale) {
mTaskbarViewInApp.setScaleX(scale);
mTaskbarViewInApp.setScaleY(scale);
@@ -136,16 +163,21 @@
return new TaskbarContainerViewCallbacks() {
@Override
public void onViewRemoved() {
- if (mTaskbarContainerView.getChildCount() == 1) {
- // Only TaskbarView remains.
- setTaskbarWindowFullscreen(false);
+ // Ensure no other children present (like Folders, etc)
+ for (int i = 0; i < mTaskbarContainerView.getChildCount(); i++) {
+ View v = mTaskbarContainerView.getChildAt(i);
+ if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))){
+ return;
+ }
}
+ setTaskbarWindowFullscreen(false);
}
@Override
public boolean isTaskbarTouchable() {
return mTaskbarContainerView.getAlpha() > AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD
- && mTaskbarViewInApp.getVisibility() == View.VISIBLE
+ && (mTaskbarViewInApp.getVisibility() == VISIBLE
+ || mImeBarView.getVisibility() == VISIBLE)
&& !mIsAnimatingToLauncher;
}
};
@@ -198,7 +230,7 @@
// space so that the others line up with the home screen hotseat.
boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome
|| mLauncher.hasBeenResumed() || mIsAnimatingToLauncher;
- return isOnHomeScreen ? View.INVISIBLE : View.GONE;
+ return isOnHomeScreen ? INVISIBLE : GONE;
}
@Override
@@ -212,6 +244,11 @@
alignRealHotseatWithTaskbar();
}
}
+
+ @Override
+ public void onNavigationButtonClick(@TaskbarButton int buttonType) {
+ mNavButtonController.onButtonClick(buttonType);
+ }
};
}
@@ -228,9 +265,12 @@
* Initializes the Taskbar, including adding it to the screen.
*/
public void init() {
- mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons());
- mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons());
+ mNavMode = SysUINavigationMode.INSTANCE.get(mLauncher)
+ .addModeChangeListener(mNavigationModeChangeListener);
+ mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(), mNavMode);
+ mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(), mNavMode);
mTaskbarContainerView.init(mTaskbarViewInApp);
+ mImeBarView.init(createTaskbarViewCallbacks());
addToWindowManager();
mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
mTaskbarAnimationController.init();
@@ -272,12 +312,15 @@
mTaskbarViewInApp.cleanup();
mTaskbarViewOnHome.cleanup();
mTaskbarContainerView.cleanup();
+ mImeBarView.cleanup();
removeFromWindowManager();
mTaskbarStateHandler.setTaskbarCallbacks(null);
mTaskbarAnimationController.cleanup();
mHotseatController.cleanup();
setWhichTaskbarViewIsVisible(null);
+ SysUINavigationMode.INSTANCE.get(mLauncher)
+ .removeModeChangeListener(mNavigationModeChangeListener);
}
private void removeFromWindowManager() {
@@ -315,6 +358,12 @@
mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
}
+ private void onNavModeChanged(SysUINavigationMode.Mode newMode) {
+ mNavMode = newMode;
+ cleanup();
+ init();
+ }
+
/**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
@@ -387,6 +436,28 @@
*/
public void setIsImeVisible(boolean isImeVisible) {
mTaskbarAnimationController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
+ blockTaskbarTouchesForIme(isImeVisible);
+ }
+
+ /**
+ * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
+ * instantiating at all, which is what's responsible for sending sysui state flags over.
+ *
+ * @param vis IME visibility flag
+ * @param backDisposition Used to determine back button behavior for software keyboard
+ * See BACK_DISPOSITION_* constants in {@link InputMethodService}
+ */
+ public void updateImeStatus(int displayId, int vis, int backDisposition,
+ boolean showImeSwitcher) {
+ if (displayId != mTaskbarContainerView.getContext().getDisplayId() ||
+ mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
+ return;
+ }
+
+ boolean imeVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+ mTaskbarAnimationController.animateToVisibilityForIme(imeVisible ? 0 : 1);
+ mImeBarView.setImeSwitcherVisibility(showImeSwitcher);
+ blockTaskbarTouchesForIme(imeVisible);
}
/**
@@ -436,12 +507,17 @@
private void setWhichTaskbarViewIsVisible(@Nullable TaskbarView visibleTaskbar) {
mTaskbarViewInApp.setVisibility(visibleTaskbar == mTaskbarViewInApp
- ? View.VISIBLE : View.INVISIBLE);
+ ? VISIBLE : INVISIBLE);
mTaskbarViewOnHome.setVisibility(visibleTaskbar == mTaskbarViewOnHome
- ? View.VISIBLE : View.INVISIBLE);
+ ? VISIBLE : INVISIBLE);
mLauncher.getHotseat().setIconsAlpha(visibleTaskbar != mTaskbarViewInApp ? 1f : 0f);
}
+ private void blockTaskbarTouchesForIme(boolean block) {
+ mTaskbarViewOnHome.setTouchesEnabled(!block);
+ mTaskbarViewInApp.setTouchesEnabled(!block);
+ }
+
/**
* Returns the ratio of the taskbar icon size on home vs in an app.
*/
@@ -485,6 +561,7 @@
protected interface TaskbarAnimationControllerCallbacks {
void updateTaskbarBackgroundAlpha(float alpha);
void updateTaskbarVisibilityAlpha(float alpha);
+ void updateImeBarVisibilityAlpha(float alpha);
void updateTaskbarScale(float scale);
void updateTaskbarTranslationY(float translationY);
}
@@ -507,6 +584,7 @@
/** Returns how much to scale non-icon elements such as spacing and dividers. */
float getNonIconScale(TaskbarView taskbarView);
void onItemPositionsChanged(TaskbarView taskbarView);
+ void onNavigationButtonClick(@TaskbarButton int buttonType);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
new file mode 100644
index 0000000..54e1610
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 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 android.content.Context;
+import android.content.Intent;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.annotation.IntDef;
+
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TouchInteractionService;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controller for 3 button mode in the taskbar.
+ * Handles all the functionality of the various buttons, making/routing the right calls into
+ * launcher or sysui/system.
+ *
+ * TODO: Create callbacks to hook into UI layer since state will change for more context buttons/
+ * assistant invocation.
+ */
+public class TaskbarNavButtonController {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BUTTON_BACK,
+ BUTTON_HOME,
+ BUTTON_RECENTS,
+ BUTTON_IME_SWITCH
+ })
+
+ public @interface TaskbarButton {}
+
+ static final int BUTTON_BACK = 1;
+ static final int BUTTON_HOME = BUTTON_BACK << 1;
+ static final int BUTTON_RECENTS = BUTTON_HOME << 1;
+ static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
+
+
+ private final Context mContext;
+
+ public TaskbarNavButtonController(Context context) {
+ mContext = context;
+ }
+
+ public void onButtonClick(@TaskbarButton int buttonType) {
+ switch (buttonType) {
+ case BUTTON_BACK:
+ executeBack();
+ break;
+ case BUTTON_HOME:
+ navigateHome();
+ break;
+ case BUTTON_RECENTS:
+ navigateToOverview();;
+ break;
+ case BUTTON_IME_SWITCH:
+ showIMESwitcher();
+ break;
+ }
+ }
+
+ private void navigateHome() {
+ mContext.startActivity(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ private void navigateToOverview() {
+ TouchInteractionService.getInstance().getOverviewCommandHelper()
+ .addCommand(OverviewCommandHelper.TYPE_SHOW);
+ }
+
+ private void executeBack() {
+ SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
+ }
+
+ private void showIMESwitcher() {
+ mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
+ true /* showAuxiliarySubtypes */, mContext.getDisplayId());
+ }
+
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 60a7add..9e8013e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,6 +15,10 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
@@ -24,8 +28,10 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.SystemProperties;
import android.util.AttributeSet;
import android.view.DragEvent;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -45,12 +51,17 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.SysUINavigationMode;
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
+
+ private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
+ SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
+
private final int mIconTouchSize;
private final boolean mIsRtl;
private final int mTouchSlop;
@@ -68,15 +79,22 @@
private LayoutTransition mLayoutTransition;
private int mHotseatStartIndex;
private int mHotseatEndIndex;
+ private LinearLayout mButtonRegion;
// Delegate touches to the closest view if within mIconTouchSize.
private boolean mDelegateTargeted;
private View mDelegateView;
+ // Prevents dispatching touches to children if true
+ private boolean mTouchEnabled = true;
private boolean mIsDraggingItem;
// Only non-null when the corresponding Folder is open.
private @Nullable FolderIcon mLeaveBehindFolderIcon;
+ private int mNavButtonStartIndex;
+ /** Provider of buttons added to taskbar in 3 button nav */
+ private ButtonProvider mButtonProvider;
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -100,15 +118,28 @@
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
- protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
+ protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks,
+ ButtonProvider buttonProvider) {
mControllerCallbacks = taskbarViewCallbacks;
mNonIconScale = mControllerCallbacks.getNonIconScale(this);
mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
+ mButtonProvider = buttonProvider;
+ mButtonProvider.setMarginLeftRight(mItemMarginLeftRight);
}
- protected void init(int numHotseatIcons) {
- mHotseatStartIndex = 0;
+ protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) {
+ // TODO: check if buttons on left
+ if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) {
+ // 3 button
+ mNavButtonStartIndex = 0;
+ createNavButtons();
+ } else {
+ mNavButtonStartIndex = -1;
+ removeNavButtons();
+ }
+
+ mHotseatStartIndex = mNavButtonStartIndex + 1;
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
updateHotseatItems(new ItemInfo[numHotseatIcons]);
@@ -185,11 +216,11 @@
if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
|| needsReinflate) {
removeView(hotseatView);
- ActivityContext activityContext = ActivityContext.lookupContext(getContext());
+ ActivityContext activityContext = getActivityContext();
if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
- ActivityContext.lookupContext(getContext()), this, folderInfo);
+ getActivityContext(), this, folderInfo);
folderIcon.setTextVisible(false);
hotseatView = folderIcon;
} else {
@@ -244,11 +275,23 @@
}
@Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (!mTouchEnabled) {
+ return true;
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = delegateTouchIfNecessary(event);
return super.onTouchEvent(event) || handled;
}
+ public void setTouchesEnabled(boolean touchEnabled) {
+ this.mTouchEnabled = touchEnabled;
+ }
+
/**
* User touched the Taskbar background. Determine whether the touch is close enough to a view
* that we should forward the touches to it.
@@ -335,12 +378,57 @@
return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
}
+ private void removeNavButtons() {
+ if (mButtonRegion != null) {
+ mButtonRegion.removeAllViews();
+ removeView(mButtonRegion);
+ } // else We've never been in 3 button. Woah Scoob!
+ }
+
+ /**
+ * Add back/home/recents buttons into a single ViewGroup that will be inserted at
+ * {@param navButtonStartIndex}
+ */
+ private void createNavButtons() {
+ ActivityContext context = getActivityContext();
+ if (mButtonRegion == null) {
+ mButtonRegion = new LinearLayout(getContext());
+ } else {
+ mButtonRegion.removeAllViews();
+ }
+ mButtonRegion.setVisibility(VISIBLE);
+
+ LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
+ context.getDeviceProfile().iconSizePx,
+ context.getDeviceProfile().iconSizePx
+ );
+ buttonParams.gravity = Gravity.CENTER;
+
+ View backButton = mButtonProvider.getBack();
+ backButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+ BUTTON_BACK));
+ mButtonRegion.addView(backButton, buttonParams);
+
+ // Home button
+ View homeButton = mButtonProvider.getHome();
+ homeButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+ BUTTON_HOME));
+ mButtonRegion.addView(homeButton, buttonParams);
+
+ View recentsButton = mButtonProvider.getRecents();
+ recentsButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+ BUTTON_RECENTS));
+ mButtonRegion.addView(recentsButton, buttonParams);
+
+ addView(mButtonRegion, mNavButtonStartIndex);
+ }
+
@Override
public boolean onDragEvent(DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mIsDraggingItem = true;
- AbstractFloatingView.closeAllOpenViews(ActivityContext.lookupContext(getContext()));
+ AbstractFloatingView.closeAllOpenViews(getActivityContext());
return true;
case DragEvent.ACTION_DRAG_ENDED:
mIsDraggingItem = false;
@@ -407,12 +495,15 @@
}
private View inflate(@LayoutRes int layoutResId) {
- return ActivityContext.lookupContext(getContext()).getLayoutInflater()
- .inflate(layoutResId, this, false);
+ return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false);
}
@Override
public void setInsets(Rect insets) {
// Ignore, we just implement Insettable to draw behind system insets.
}
+
+ private <T extends Context & ActivityContext> T getActivityContext() {
+ return ActivityContext.lookupContext(getContext());
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 01616d4..e508690 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -24,10 +24,12 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.util.FloatProperty;
@@ -62,7 +64,8 @@
public void setState(@NonNull LauncherState state) {
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
- ADJACENT_PAGE_OFFSET.set(mRecentsView, scaleAndOffset[1]);
+ ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
+ ADJACENT_PAGE_VERTICAL_OFFSET.set(mRecentsView, scaleAndOffset[2]);
TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
@@ -92,15 +95,17 @@
float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher);
setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
- setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+ setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
+ setter.setFloat(mRecentsView, ADJACENT_PAGE_VERTICAL_OFFSET, scaleAndOffset[2],
+ config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
PagedOrientationHandler orientationHandler =
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset(
- TASK_PRIMARY_TRANSLATION, TASK_SECONDARY_TRANSLATION, mLauncher.getDeviceProfile());
+ TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+ mLauncher.getDeviceProfile());
setter.setFloat(mRecentsView, taskViewsFloat,
- toState.getOverviewSecondaryTranslation(mLauncher),
- config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
+ toState.getOverviewSecondaryTranslation(mLauncher), LINEAR);
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index a81bdd5..d822c8c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -84,11 +84,6 @@
}
@Override
- public float[] getOverviewScaleAndOffset(Launcher launcher) {
- return new float[] {0.9f, 1};
- }
-
- @Override
public LauncherState getHistoryForState(LauncherState previousState) {
return previousState == OVERVIEW ? OVERVIEW : NORMAL;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 77c2611..06ffae4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -96,6 +96,6 @@
BaseDraggingActivity activity) {
return new float[] {
((RecentsView) activity.getOverviewPanel()).getMaxScaleForFullScreen(),
- NO_OFFSET};
+ NO_OFFSET, NO_OFFSET};
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 6f084a1..1fc288f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -78,6 +78,6 @@
float scale = Math.min((float) modalTaskSize.height() / taskSize.y,
(float) modalTaskSize.width() / taskSize.x);
- return new float[] {scale, NO_OFFSET};
+ return new float[] {scale, NO_OFFSET, NO_OFFSET};
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 135c478..c9cfad3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
import android.graphics.Rect;
@@ -60,9 +59,8 @@
@Override
public int getTransitionDuration(Context context) {
- // In no-button mode, overview comes in all the way from the left, so give it more time.
- boolean isNoButtonMode = SysUINavigationMode.INSTANCE.get(context).getMode() == NO_BUTTON;
- return isNoButtonMode ? 380 : 250;
+ // In gesture modes, overview comes in all the way from the bottom, so give it more time.
+ return SysUINavigationMode.INSTANCE.get(context).getMode().hasGestures ? 380 : 250;
}
@Override
@@ -80,7 +78,7 @@
@Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
- return new float[] {NO_SCALE, NO_OFFSET};
+ return new float[] {NO_SCALE, NO_OFFSET, NO_OFFSET};
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 3ac7866..adc6b18 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -23,34 +23,34 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import android.animation.ValueAnimator;
-import android.view.View;
import com.android.launcher3.CellLayout;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.SysUINavigationMode;
@@ -65,6 +65,12 @@
// Scale recents takes before animating in
private static final float RECENTS_PREPARE_SCALE = 1.33f;
+ // Scale workspace takes before animating in
+ private static final float WORKSPACE_PREPARE_SCALE_GESTURES = 0.97f;
+ private static final float WORKSPACE_PREPARE_SCALE_BUTTONS = 0.92f;
+ // When the overview to home transition reaches this percentage, immediately hide overview and
+ // start animating away the scrim and animating in workspace.
+ private static final float OVERVIEW_TO_HOME_HARD_HAND_OFF = 0.4f;
// Due to use of physics, duration may differ between devices so we need to calculate and
// cache the value.
@@ -79,21 +85,31 @@
StateAnimationConfig config) {
RecentsView overview = mActivity.getOverviewPanel();
if (toState == NORMAL && fromState == OVERVIEW) {
- config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
- config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
- config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
- config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
-
- if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
- // Scrolling in tasks, so make visible straight away
- if (overview.getTaskViewCount() > 0) {
- config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
- } else {
- config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
- }
+ final float workspacePrepareScale;
+ if (SysUINavigationMode.getMode(mActivity).hasGestures
+ && overview.getTaskViewCount() > 0) {
+ workspacePrepareScale = WORKSPACE_PREPARE_SCALE_GESTURES;
+ // Overview is going offscreen, so keep it at its current scale and opacity.
+ config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
+ config.setInterpolator(ANIM_OVERVIEW_FADE, clampToProgress(
+ FINAL_FRAME, 0f, OVERVIEW_TO_HOME_HARD_HAND_OFF));
+ config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(
+ DEACCEL, 0f, OVERVIEW_TO_HOME_HARD_HAND_OFF));
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, AGGRESSIVE_EASE_IN_OUT);
+ config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(
+ DEACCEL, OVERVIEW_TO_HOME_HARD_HAND_OFF, 1f));
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, clampToProgress(
+ DEACCEL, OVERVIEW_TO_HOME_HARD_HAND_OFF, 1f));
+ config.setInterpolator(ANIM_WORKSPACE_FADE, clampToProgress(
+ INSTANT, OVERVIEW_TO_HOME_HARD_HAND_OFF, 1f));
} else {
+ workspacePrepareScale = WORKSPACE_PREPARE_SCALE_BUTTONS;
+ config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+ config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, LINEAR);
+ config.setInterpolator(ANIM_SCRIM_FADE, LINEAR);
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
+ config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
}
Workspace workspace = mActivity.getWorkspace();
@@ -106,25 +122,18 @@
&& currentChild.getShortcutsAndWidgets().getAlpha() > 0;
}
if (!isWorkspaceVisible) {
- workspace.setScaleX(0.92f);
- workspace.setScaleY(0.92f);
+ workspace.setScaleX(workspacePrepareScale);
+ workspace.setScaleY(workspacePrepareScale);
}
Hotseat hotseat = mActivity.getHotseat();
boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
if (!isHotseatVisible) {
- hotseat.setScaleX(0.92f);
- hotseat.setScaleY(0.92f);
- AllAppsContainerView qsbContainer = mActivity.getAppsView();
- View qsb = qsbContainer.getSearchView();
- boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
- if (!qsbVisible) {
- qsbContainer.setScaleX(0.92f);
- qsbContainer.setScaleY(0.92f);
- }
+ hotseat.setScaleX(workspacePrepareScale);
+ hotseat.setScaleY(workspacePrepareScale);
}
} else if ((fromState == NORMAL || fromState == HINT_STATE
|| fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) {
- if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
+ if (SysUINavigationMode.getMode(mActivity).hasGestures) {
config.setInterpolator(ANIM_WORKSPACE_SCALE,
fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
@@ -148,6 +157,10 @@
config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_SCRIM_FADE, t -> {
+ // Animate at the same rate until reaching progress 1, and skip the overshoot.
+ return Math.min(1, OVERSHOOT_1_2.getInterpolation(t));
+ });
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
} else if (fromState == HINT_STATE && toState == NORMAL) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 12de4a6..0f64abc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -36,11 +36,14 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -207,7 +210,7 @@
/** Create state animation to control non-overview components. */
private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) {
config.duration = (long) (Math.max(mXRange, mYRange) * 2);
- config.animFlags |= SKIP_OVERVIEW;
+ config.animFlags |= SKIP_OVERVIEW | SKIP_SCRIM;
mNonOverviewAnim = mLauncher.getStateManager()
.createAnimationToNewWorkspace(toState, config);
mNonOverviewAnim.getTarget().addListener(mClearStateOnCancelListener);
@@ -219,7 +222,8 @@
// Set RecentView's initial properties.
RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
- ADJACENT_PAGE_OFFSET.set(mRecentsView, 1f);
+ ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
+ ADJACENT_PAGE_VERTICAL_OFFSET.set(mRecentsView, 0f);
mRecentsView.setContentAlpha(1);
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().setValue(
@@ -229,10 +233,14 @@
// As we drag right, animate the following properties:
// - RecentsView translationX
// - OverviewScrim
+ // - RecentsView fade (if it's empty)
PendingAnimation xAnim = new PendingAnimation((long) (mXRange * 2));
- xAnim.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], LINEAR);
+ xAnim.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1], LINEAR);
xAnim.setViewBackgroundColor(mLauncher.getScrimView(),
toState.getWorkspaceScrimColor(mLauncher), LINEAR);
+ if (mRecentsView.getTaskViewCount() == 0) {
+ xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
+ }
mXOverviewAnim = xAnim.createPlaybackController();
mXOverviewAnim.dispatchOnStart();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 3953e42..5891d5f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -30,6 +30,9 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -105,6 +108,14 @@
StateAnimationConfig config = new StateAnimationConfig();
setupInterpolators(config);
config.duration = (long) (getShiftRange() * 2);
+
+ // Set RecentView's initial properties for coming in from the side.
+ RECENTS_SCALE_PROPERTY.set(mOverviewPanel,
+ QUICK_SWITCH.getOverviewScaleAndOffset(mLauncher)[0] * 0.85f);
+ ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mOverviewPanel, 1f);
+ ADJACENT_PAGE_VERTICAL_OFFSET.set(mOverviewPanel, 0f);
+ mOverviewPanel.setContentAlpha(1);
+
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, config);
mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index ecaac94..fe10908 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -47,7 +47,6 @@
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_END;
import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_START;
-import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
@@ -55,7 +54,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
@@ -340,6 +338,10 @@
}
protected boolean onActivityInit(Boolean alreadyOnHome) {
+ if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+ return false;
+ }
+
T createdActivity = mActivityInterface.getCreatedActivity();
if (createdActivity != null) {
initTransitionEndpoints(createdActivity.getDeviceProfile());
@@ -464,8 +466,6 @@
mDeviceState.getRotationTouchHelper()
.onEndTargetCalculated(mGestureState.getEndTarget(),
mActivityInterface);
-
- mRecentsView.onGestureEndTargetCalculated(mGestureState.getEndTarget());
});
notifyGestureStartedAsync();
@@ -571,6 +571,8 @@
}
});
reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
+ mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
+ reapplyWindowTransformAnim::cancel);
} else {
applyWindowTransform();
}
@@ -1133,6 +1135,10 @@
}
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
mLauncherTransitionController = null;
+
+ if (mRecentsView != null) {
+ mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget());
+ }
} else {
AnimatorSet animatorSet = new AnimatorSet();
ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
@@ -1171,9 +1177,9 @@
}
});
animatorSet.play(windowAnim);
- S state = mActivityInterface.stateFromGestureEndTarget(mGestureState.getEndTarget());
- if (mRecentsView != null && state.displayOverviewTasksAsGrid(mDp)) {
- animatorSet.play(ObjectAnimator.ofFloat(mRecentsView, RECENTS_GRID_PROGRESS, 1));
+ if (mRecentsView != null) {
+ mRecentsView.onPrepareGestureEndAnimation(
+ animatorSet, mGestureState.getEndTarget());
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
animatorSet.start();
@@ -1376,12 +1382,6 @@
mActivityInitListener.unregister();
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
mTaskSnapshot = null;
- mHandler.post(() -> {
- // Defer clearing the activity since invalidation can happen over multiple callbacks
- // ie. invalidateHandlerWithLauncher()
- mActivity = null;
- mRecentsView = null;
- });
}
private void invalidateHandlerWithLauncher() {
@@ -1392,6 +1392,12 @@
mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
resetLauncherListeners();
+
+ mHandler.post(() -> {
+ // Defer clearing the activity since invalidation can happen over multiple callbacks.
+ mActivity = null;
+ mRecentsView = null;
+ });
}
private void endLauncherTransitionController() {
@@ -1701,7 +1707,7 @@
// 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) {
+ if (mRecentsViewScrollLinked && mRecentsView != null) {
mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
}
mTaskViewSimulator.apply(mTransformParams);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 7aa81d4..b60b1be 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -23,8 +23,8 @@
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.SysUINavigationMode.getMode;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
-import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_Y_ANIM;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -469,17 +469,17 @@
float fromTranslation = attached ? 1 : 0;
float toTranslation = attached ? 0 : 1;
mActivity.getStateManager()
- .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
+ .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_Y_ANIM);
if (!recentsView.isShown() && animate) {
- ADJACENT_PAGE_OFFSET.set(recentsView, fromTranslation);
+ ADJACENT_PAGE_VERTICAL_OFFSET.set(recentsView, fromTranslation);
} else {
- fromTranslation = ADJACENT_PAGE_OFFSET.get(recentsView);
+ fromTranslation = ADJACENT_PAGE_VERTICAL_OFFSET.get(recentsView);
}
if (!animate) {
- ADJACENT_PAGE_OFFSET.set(recentsView, toTranslation);
+ ADJACENT_PAGE_VERTICAL_OFFSET.set(recentsView, toTranslation);
} else {
mActivity.getStateManager().createStateElementAnimation(
- INDEX_RECENTS_TRANSLATE_X_ANIM,
+ INDEX_RECENTS_TRANSLATE_Y_ANIM,
fromTranslation, toTranslation).start();
}
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 9fa65d9..3c81d1b 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -163,7 +163,9 @@
@Override
public boolean isInLiveTileMode() {
- return false;
+ RecentsActivity activity = getCreatedActivity();
+ return activity != null && activity.getStateManager().getState() == DEFAULT &&
+ activity.isStarted();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 9398277..46cd8a2 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -260,6 +260,7 @@
@Override
public void setAnimation(RectFSpringAnim anim) {
anim.addAnimatorListener(floatingWidgetView);
+ floatingWidgetView.setOnTargetChangeListener(anim::onTargetPositionChanged);
floatingWidgetView.setFastFinishRunnable(anim::end);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3b92779..0e9e3ad 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -22,10 +22,13 @@
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
import static com.android.launcher3.Utilities.createHomeIntent;
+import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -35,6 +38,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.view.SurfaceControl.Transaction;
import android.view.View;
import androidx.annotation.Nullable;
@@ -47,6 +51,7 @@
import com.android.launcher3.R;
import com.android.launcher3.WrappedAnimationRunnerImpl;
import com.android.launcher3.WrappedLauncherAnimationRunner;
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -69,6 +74,7 @@
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.views.OverviewActionsView;
+import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.SplitPlaceholderView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -90,6 +96,8 @@
private Handler mUiHandler = new Handler(Looper.getMainLooper());
+ private static final long HOME_APPEAR_DURATION = 250;
+
private RecentsDragLayer mDragLayer;
private ScrimView mScrimView;
private FallbackRecentsView mFallbackRecentsView;
@@ -112,6 +120,7 @@
mScrimView = findViewById(R.id.scrim_view);
mFallbackRecentsView = findViewById(R.id.overview_panel);
mActionsView = findViewById(R.id.overview_actions_view);
+ SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
splitPlaceholderView.init(
@@ -291,6 +300,16 @@
super.onConfigurationChanged(newConfig);
}
+ @Override
+ public void onStateSetEnd(RecentsState state) {
+ super.onStateSetEnd(state);
+
+ if (state == RecentsState.DEFAULT) {
+ AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(),
+ OVERVIEW_STATE_ORDINAL);
+ }
+ }
+
/**
* Initialize/update the device profile.
*/
@@ -329,7 +348,42 @@
}
public void startHome() {
- startActivity(createHomeIntent());
+ if (LIVE_TILE.get()) {
+ RecentsView recentsView = getOverviewPanel();
+ recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
+ this::startHomeInternal));
+ } else {
+ startHomeInternal();
+ }
+ }
+
+ private void startHomeInternal() {
+ WrappedLauncherAnimationRunner runner = new WrappedLauncherAnimationRunner(
+ getMainThreadHandler(), this::onCreateAnimationToHome, true);
+ RemoteAnimationAdapterCompat adapterCompat =
+ new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0);
+ startActivity(createHomeIntent(),
+ ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
+ }
+
+ private void onCreateAnimationToHome(
+ int transit, RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+ AnimatorPlaybackController controller = getStateManager()
+ .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
+ controller.dispatchOnStart();
+
+ RemoteAnimationTargets targets = new RemoteAnimationTargets(
+ appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
+ for (RemoteAnimationTargetCompat app : targets.apps) {
+ new Transaction().setAlpha(app.leash.getSurfaceControl(), 1).apply();
+ }
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(controller.getAnimationPlayer());
+ anim.setDuration(HOME_APPEAR_DURATION);
+ result.setAnimation(anim, this,
+ () -> getStateManager().goToState(RecentsState.HOME, false));
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index c786167..2eb9dd8 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -31,6 +31,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.icons.IconProvider.IconChangeListener;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.model.Task;
@@ -49,7 +50,7 @@
* Singleton class to load and manage recents model.
*/
@TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel extends TaskStackChangeListener {
+public class RecentsModel extends TaskStackChangeListener implements IconChangeListener {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -69,12 +70,13 @@
mContext = context;
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
- mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR);
+
+ IconProvider iconProvider = new IconProvider(context);
+ mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
- IconProvider.registerIconChangeListener(context,
- this::onPackageIconChanged, MAIN_EXECUTOR.getHandler());
+ iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
}
public TaskIconCache getIconCache() {
@@ -183,17 +185,23 @@
if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
// Clear everything once we reach a low-mem situation
mThumbnailCache.clear();
- mIconCache.clear();
+ mIconCache.clearCache();
}
}
- private void onPackageIconChanged(String pkg, UserHandle user) {
- mIconCache.invalidateCacheEntries(pkg, user);
+ @Override
+ public void onAppIconChanged(String packageName, UserHandle user) {
+ mIconCache.invalidateCacheEntries(packageName, user);
for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
- mThumbnailChangeListeners.get(i).onTaskIconChanged(pkg, user);
+ mThumbnailChangeListeners.get(i).onTaskIconChanged(packageName, user);
}
}
+ @Override
+ public void onSystemIconStateChanged(String iconState) {
+ mIconCache.clearCache();
+ }
+
/**
* Adds a listener for visuals changes
*/
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index efec037..74f4bea 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -34,6 +34,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Observer for the resource config that specifies the navigation bar mode.
@@ -75,7 +76,8 @@
private int mNavBarGesturalHeight;
private int mNavBarLargerGesturalHeight;
- private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
+ private final List<NavigationModeChangeListener> mChangeListeners =
+ new CopyOnWriteArrayList<>();
private final List<OneHandedModeChangeListener> mOneHandedOverlayChangeListeners =
new ArrayList<>();
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index ba1c413..fa61fff 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
+import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import android.app.ActivityManager.TaskDescription;
import android.content.Context;
@@ -35,9 +36,12 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconProvider;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
@@ -52,7 +56,7 @@
/**
* Manages the caching of task icons and related data.
*/
-public class TaskIconCache {
+public class TaskIconCache implements DisplayInfoChangeListener {
private final Executor mBgExecutor;
private final AccessibilityManager mAccessibilityManager;
@@ -62,15 +66,27 @@
private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
private final IconProvider mIconProvider;
- public TaskIconCache(Context context, Executor bgExecutor) {
+ private BaseIconFactory mIconFactory;
+
+ public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
mContext = context;
mBgExecutor = bgExecutor;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ mIconProvider = iconProvider;
Resources res = context.getResources();
int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
+
mIconCache = new TaskKeyLruCache<>(cacheSize);
- mIconProvider = new IconProvider(context);
+
+ DisplayController.INSTANCE.get(mContext).addChangeListener(this);
+ }
+
+ @Override
+ public void onDisplayInfoChanged(Context context, Info info, int flags) {
+ if ((flags & CHANGE_DENSITY) != 0) {
+ clearCache();
+ }
}
/**
@@ -104,8 +120,11 @@
return request;
}
- public void clear() {
- mIconCache.evictAll();
+ /**
+ * Clears the icon cache
+ */
+ public void clearCache() {
+ mBgExecutor.execute(this::resetFactory);
}
void onTaskRemoved(TaskKey taskKey) {
@@ -193,8 +212,8 @@
synchronized (mDefaultIcons) {
BitmapInfo info = mDefaultIcons.get(userId);
if (info == null) {
- try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
- info = la.makeDefaultIcon(UserHandle.of(userId));
+ try (BaseIconFactory bif = getIconFactory()) {
+ info = bif.makeDefaultIcon(UserHandle.of(userId));
}
mDefaultIcons.put(userId, info);
}
@@ -205,16 +224,32 @@
@WorkerThread
private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
int primaryColor, boolean isInstantApp) {
- try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
- la.disableColorExtraction();
- la.setWrapperBackgroundColor(primaryColor);
+ try (BaseIconFactory bif = getIconFactory()) {
+ bif.disableColorExtraction();
+ bif.setWrapperBackgroundColor(primaryColor);
// User version code O, so that the icon is always wrapped in an adaptive icon container
- return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+ return bif.createBadgedIconBitmap(drawable, UserHandle.of(userId),
Build.VERSION_CODES.O, isInstantApp);
}
}
+ @WorkerThread
+ private BaseIconFactory getIconFactory() {
+ if (mIconFactory == null) {
+ mIconFactory = new BaseIconFactory(mContext,
+ DisplayController.INSTANCE.get(mContext).getInfo().densityDpi,
+ mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size));
+ }
+ return mIconFactory;
+ }
+
+ @WorkerThread
+ private void resetFactory() {
+ mIconFactory = null;
+ mIconCache.evictAll();
+ }
+
private static class TaskCacheEntry {
public Drawable icon;
public String contentDescription = "";
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4fc9770..5fe0fc7 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -52,6 +52,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
@@ -145,7 +146,22 @@
@Nullable
private OverscrollPlugin mOverscrollPlugin;
- private final IBinder mMyBinder = new IOverviewProxy.Stub() {
+ /**
+ * Extension of OverviewProxy aidl interface without needing to modify the actual interface.
+ * This is for methods that need only need local access and not intended to make IPC calls.
+ */
+ public abstract static class TISBinder extends IOverviewProxy.Stub {
+ public abstract void setTaskbarOverviewProxyDelegate(
+ @Nullable TaskbarOverviewProxyDelegate i);
+ }
+
+
+ private final TISBinder mMyBinder = new TISBinder() {
+
+ public void setTaskbarOverviewProxyDelegate(
+ @Nullable TaskbarOverviewProxyDelegate delegate) {
+ mTaskbarOverviewProxyDelegate = delegate;
+ }
@BinderThread
public void onInitialize(Bundle bundle) {
@@ -252,20 +268,49 @@
MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
+ @Override
public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
WindowBounds wb = new WindowBounds(bounds, insets);
MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
}
+
+ @Override
+ public void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
+ int backDisposition, boolean showImeSwitcher) throws RemoteException {
+ if (mTaskbarOverviewProxyDelegate == null) {
+ return;
+ }
+ MAIN_EXECUTOR.execute(() -> {
+ if (mTaskbarOverviewProxyDelegate == null) {
+ return;
+ }
+ mTaskbarOverviewProxyDelegate
+ .updateImeStatus(displayId, vis, backDisposition, showImeSwitcher);
+ });
+ }
};
+ public interface TaskbarOverviewProxyDelegate {
+ void updateImeStatus(int displayId, int vis, int backDisposition,
+ boolean showImeSwitcher);
+ }
+
private static boolean sConnected = false;
+ private static TouchInteractionService sInstance;
private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper;
+ @Nullable
+ private TaskbarOverviewProxyDelegate mTaskbarOverviewProxyDelegate;
public static boolean isConnected() {
return sConnected;
}
+ @Nullable
+ public static TouchInteractionService getInstance() {
+ return sInstance;
+ }
+
public static boolean isInitialized() {
return sIsInitialized;
}
@@ -293,6 +338,10 @@
private DisplayManager mDisplayManager;
+ public TouchInteractionService() {
+ sInstance = this;
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -389,6 +438,10 @@
onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
}
+ public OverviewCommandHelper getOverviewCommandHelper() {
+ return mOverviewCommandHelper;
+ }
+
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
@@ -496,6 +549,13 @@
Point sz = new Point();
display.getRealSize(sz);
if (rotation != Surface.ROTATION_0) {
+ if ((rotation % 2) != 0) {
+ // via display-manager, the display size is unrotated, so "rotate" its size
+ // to match the rotation we are transforming the event into.
+ final int tmpX = sz.x;
+ sz.x = sz.y;
+ sz.y = tmpX;
+ }
event.transform(InputChannelCompat.createRotationMatrix(rotation, sz.x, sz.y));
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index e4d148c..4d776ba 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -22,7 +22,8 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -86,8 +87,10 @@
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
- setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+ setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
+ setter.setFloat(mRecentsView, ADJACENT_PAGE_VERTICAL_OFFSET, scaleAndOffset[2],
+ config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 4d4b6e1..1bb8e96 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -20,6 +20,7 @@
import static com.android.quickstep.fallback.RecentsState.HOME;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
+import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -27,6 +28,9 @@
import android.util.AttributeSet;
import android.util.Log;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.testing.TestProtocol;
@@ -67,7 +71,6 @@
@Override
public void startHome() {
mActivity.startHome();
- mActivity.getStateManager().goToState(RecentsState.HOME);
}
/**
@@ -86,14 +89,17 @@
* to the center.
*/
@Override
- public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
- super.onGestureEndTargetCalculated(endTarget);
- if (mHomeTaskInfo != null && endTarget == RECENTS) {
+ public void onPrepareGestureEndAnimation(
+ @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+ super.onPrepareGestureEndAnimation(animatorSet, endTarget);
+ if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskView(mHomeTaskInfo.taskId);
if (tv != null) {
PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
pa.addEndListener(e -> setCurrentTask(-1));
- runDismissAnimation(pa);
+ AnimatorPlaybackController controller = pa.createPlaybackController();
+ controller.dispatchOnStart();
+ animatorSet.play(controller.getAnimationPlayer());
}
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index b3d6cfa..532f219 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -39,15 +39,18 @@
private static final int FLAG_OVERVIEW_ACTIONS = BaseState.getFlag(3);
private static final int FLAG_SHOW_AS_GRID = BaseState.getFlag(4);
private static final int FLAG_SCRIM = BaseState.getFlag(5);
+ private static final int FLAG_LIVE_TILE = BaseState.getFlag(6);
public static final RecentsState DEFAULT = new RecentsState(0,
- FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID | FLAG_SCRIM);
+ FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID | FLAG_SCRIM
+ | FLAG_LIVE_TILE);
public static final RecentsState MODAL_TASK = new ModalState(1,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
- | FLAG_SHOW_AS_GRID | FLAG_SCRIM);
+ | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE);
public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
public static final RecentsState HOME = new RecentsState(3, 0);
+ public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
public final int ordinal;
private final int mFlags;
@@ -108,6 +111,13 @@
}
/**
+ * For this state, whether live tile should be shown.
+ */
+ public boolean hasLiveTile() {
+ return hasFlag(FLAG_LIVE_TILE);
+ }
+
+ /**
* For this state, what color scrim should be drawn behind overview.
*/
public int getScrimColor(RecentsActivity activity) {
@@ -116,7 +126,7 @@
}
public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
- return new float[] { NO_SCALE, NO_OFFSET };
+ return new float[] { NO_SCALE, NO_OFFSET, NO_OFFSET };
}
/**
@@ -152,4 +162,15 @@
return getOverviewScaleAndOffsetForBackgroundState(activity);
}
}
+
+ private static class LauncherState extends RecentsState {
+ LauncherState(int id, int flags) {
+ super(id, flags);
+ }
+
+ @Override
+ public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+ return new float[] { NO_SCALE, NO_OFFSET, 1 };
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
index d7458d2..273d1f6 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.fallback;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.quickstep.RecentsActivity;
@@ -26,7 +28,8 @@
@Override
protected boolean isRecentsInteractive() {
- return mActivity.hasWindowFocus();
+ return mActivity.hasWindowFocus() || (LIVE_TILE.get()
+ && mActivity.getStateManager().getState().hasLiveTile());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
new file mode 100644
index 0000000..76f43c9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -0,0 +1,133 @@
+/*
+ * 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.quickstep.interaction;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+import java.net.URISyntaxException;
+
+/**
+ * A page shows after SUW flow to hint users to swipe up from the bottom of the screen to go home
+ * for the gestural system navigation.
+ */
+public class AllSetActivity extends Activity {
+
+ private static final String LOG_TAG = "AllSetActivity";
+ private static final String URI_SYSTEM_NAVIGATION_SETTING =
+ "#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S.:settings:fragment_args_key=gesture_system_navigation_input_summary;S.:settings:show_fragment=com.android.settings.gestures.SystemNavigationGestureSettings;end";
+ private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "accent_color_dark_mode";
+ private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "accent_color_light_mode";
+
+ private int mAccentColor;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_allset);
+ setTitle(R.string.allset_title);
+
+ final int mode =
+ getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ mAccentColor = getIntent().getIntExtra(
+ mode == Configuration.UI_MODE_NIGHT_YES
+ ? EXTRA_ACCENT_COLOR_DARK_MODE : EXTRA_ACCENT_COLOR_LIGHT_MODE,
+ /* defValue= */ Color.BLACK);
+
+ ((ImageView) findViewById(R.id.icon)).getDrawable().mutate().setTint(mAccentColor);
+
+ TextView navigationSettings = findViewById(R.id.navigation_settings);
+ navigationSettings.setMovementMethod(LinkMovementMethod.getInstance());
+ AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
+ AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION,
+ new AllSetLinkSpan(
+ /* context= */ this,
+ view -> {
+ try {
+ startActivityForResult(
+ Intent.parseUri(URI_SYSTEM_NAVIGATION_SETTING, 0), 0);
+ } catch (URISyntaxException e) {
+ Log.e(LOG_TAG, "Failed to parse system nav settings intent", e);
+ }
+ finish();
+ }));
+ navigationSettings.setText(
+ AnnotationSpan.linkify(getText(R.string.allset_navigation_settings), linkInfo));
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus) {
+ hideSystemUI();
+ }
+ }
+
+ private void hideSystemUI() {
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN);
+ getWindow().setNavigationBarColor(Color.TRANSPARENT);
+ }
+
+ private final class AllSetLinkSpan extends AnnotationSpan {
+
+ private final String mFontFamily;
+ private final int mTextSize;
+
+ AllSetLinkSpan(Context context, View.OnClickListener listener) {
+ super(listener);
+ TypedArray typedArray =
+ context.obtainStyledAttributes(R.style.TextAppearance_GestureTutorial_LinkText,
+ R.styleable.AllSetLinkSpan);
+ mFontFamily = typedArray.getString(R.styleable.AllSetLinkSpan_android_fontFamily);
+ mTextSize =
+ typedArray.getDimensionPixelSize(
+ R.styleable.AllSetLinkSpan_android_textSize, /* defValue= */ -1);
+ typedArray.recycle();
+ }
+
+ @Override
+ public void updateDrawState(TextPaint ds) {
+ super.updateDrawState(ds);
+ ds.setColor(mAccentColor);
+ ds.setTypeface(Typeface.create(mFontFamily, Typeface.NORMAL));
+ ds.setUnderlineText(false);
+ if (mTextSize != -1) {
+ ds.setTextSize(mTextSize);
+ }
+ }
+ }
+
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/AnnotationSpan.java b/quickstep/src/com/android/quickstep/interaction/AnnotationSpan.java
new file mode 100644
index 0000000..fea5078
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AnnotationSpan.java
@@ -0,0 +1,143 @@
+/*
+ * 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.quickstep.interaction;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.text.Annotation;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.URLSpan;
+import android.util.Log;
+import android.view.View;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * This class is used to add {@link View.OnClickListener} for the text been wrapped by
+ * annotation.
+ *
+ * Copied from packages/apps/Settings/src/com/android/settings/utils/AnnotationSpan.java.
+ */
+public class AnnotationSpan extends URLSpan {
+
+ private final View.OnClickListener mClickListener;
+
+ AnnotationSpan(View.OnClickListener lsn) {
+ super((String) null);
+ mClickListener = lsn;
+ }
+
+ @Override
+ public void onClick(View widget) {
+ if (mClickListener != null) {
+ mClickListener.onClick(widget);
+ }
+ }
+
+ public static CharSequence linkify(CharSequence rawText, LinkInfo... linkInfos) {
+ SpannableString msg = new SpannableString(rawText);
+ Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class);
+ SpannableStringBuilder builder = new SpannableStringBuilder(msg);
+ for (Annotation annotation : spans) {
+ final String key = annotation.getValue();
+ int start = msg.getSpanStart(annotation);
+ int end = msg.getSpanEnd(annotation);
+ AnnotationSpan link = null;
+ for (LinkInfo linkInfo : linkInfos) {
+ if (linkInfo.mAnnotation.equals(key)) {
+ link = linkInfo.mCustomizedSpan != null ? linkInfo.mCustomizedSpan
+ : new AnnotationSpan(linkInfo.mClickListener);
+ break;
+ }
+ }
+ if (link != null) {
+ builder.setSpan(link, start, end, msg.getSpanFlags(link));
+ }
+ }
+ return builder;
+ }
+
+ /**
+ * get the text part without having text for link part
+ */
+ public static CharSequence textWithoutLink(CharSequence encodedText) {
+ SpannableString msg = new SpannableString(encodedText);
+ Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class);
+ if (spans == null) {
+ return encodedText;
+ }
+ Arrays.sort(spans, Comparator.comparingInt(span -> -msg.getSpanStart(span)));
+ StringBuilder msgWithoutLink = new StringBuilder(msg.toString());
+ for (Annotation span : spans) {
+ msgWithoutLink.delete(msg.getSpanStart(span), msg.getSpanEnd(span));
+ }
+ return msgWithoutLink.toString();
+ }
+
+ /** Data class to store the annotation and the click action. */
+ public static class LinkInfo {
+ public static final String DEFAULT_ANNOTATION = "link";
+ private static final String TAG = "AnnotationSpan.LinkInfo";
+ private final String mAnnotation;
+ private final Boolean mActionable;
+ private final View.OnClickListener mClickListener;
+ private final AnnotationSpan mCustomizedSpan;
+
+ public LinkInfo(String annotation, View.OnClickListener listener) {
+ mAnnotation = annotation;
+ mClickListener = listener;
+ mActionable = true; // assume actionable
+ mCustomizedSpan = null;
+ }
+
+ public LinkInfo(String annotation, AnnotationSpan customizedSpan) {
+ mAnnotation = annotation;
+ mClickListener = null;
+ mActionable = customizedSpan != null;
+ mCustomizedSpan = customizedSpan;
+ }
+
+ public LinkInfo(Context context, String annotation, Intent intent) {
+ mAnnotation = annotation;
+ mCustomizedSpan = null;
+ if (intent != null) {
+ mActionable = context.getPackageManager().resolveActivity(intent, 0) != null;
+ } else {
+ mActionable = false;
+ }
+ if (mActionable) {
+ mClickListener =
+ view -> {
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity was not found for intent, " + intent);
+ }
+ };
+ } else {
+ mClickListener = null;
+ }
+ }
+
+ public boolean isActionable() {
+ return mActionable;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
index c527be3..60c7add 100644
--- a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
+++ b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
@@ -20,8 +20,6 @@
import android.content.Context;
-import com.android.quickstep.OverviewComponentObserver;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SysUINavigationMode;
import java.util.function.Predicate;
@@ -37,7 +35,6 @@
private final Supplier<Boolean> mBasePredicate;
private final Predicate<SysUINavigationMode.Mode> mModePredicate;
private boolean mSupported;
- private OverviewComponentObserver mObserver;
private NavigationModeFeatureFlag(Supplier<Boolean> basePredicate,
Predicate<SysUINavigationMode.Mode> modePredicate) {
@@ -46,17 +43,12 @@
}
public boolean get() {
- return mBasePredicate.get() && mSupported && mObserver != null
- && mObserver.isHomeAndOverviewSame();
+ return mBasePredicate.get() && mSupported;
}
public void initialize(Context context) {
onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context).getMode());
SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
-
- // Temporary solution to disable live tile for the fallback launcher
- RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(context);
- mObserver = new OverviewComponentObserver(context, rads);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
index 42be9bb..10b7662 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -17,44 +17,26 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.util.Log;
-import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.views.RecentsView;
/**
* Runs an animation from overview to home. Currently, this animation is just a wrapper around the
- * normal state transition, in order to keep RecentsView at the same scale and translationY that
- * it started out at as it translates offscreen. It also scrolls RecentsView to page 0 and may play
- * a {@link StaggeredWorkspaceAnim} if we're starting from an upward fling.
+ * normal state transition and may play a {@link StaggeredWorkspaceAnim} if we're starting from an
+ * upward fling.
*/
public class OverviewToHomeAnim {
private static final String TAG = "OverviewToHomeAnim";
- // Constants to specify how to scroll RecentsView to the default page if it's not already there.
- private static final int DEFAULT_PAGE = 0;
- private static final int PER_PAGE_SCROLL_DURATION = 150;
- private static final int MAX_PAGE_SCROLL_DURATION = 750;
-
private final Launcher mLauncher;
private final Runnable mOnReachedHome;
@@ -95,24 +77,8 @@
mIsHomeStaggeredAnimFinished = true;
}
- RecentsView recentsView = mLauncher.getOverviewPanel();
- int numPagesToScroll = recentsView.getNextPage() - DEFAULT_PAGE;
- int scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
- numPagesToScroll * PER_PAGE_SCROLL_DURATION);
- int duration = Math.max(scrollDuration, startState.getTransitionDuration(mLauncher));
-
- StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig();
- config.duration = duration;
- boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler()
- .isLayoutNaturalToLauncher();
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher
- ? clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f) : FINAL_FRAME);
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
- config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
- config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, LINEAR);
- if (!isLayoutNaturalToLauncher) {
- config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL);
- }
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.duration = startState.getTransitionDuration(mLauncher);
AnimatorSet stateAnim = stateManager.createAtomicAnimation(
startState, NORMAL, config);
stateAnim.addListener(new AnimationSuccessListener() {
@@ -125,7 +91,6 @@
anim.play(stateAnim);
stateManager.setCurrentAnimation(anim, NORMAL);
anim.start();
- recentsView.snapToPage(DEFAULT_PAGE, duration);
}
private void maybeOverviewToHomeAnimComplete() {
@@ -133,17 +98,4 @@
mOnReachedHome.run();
}
}
-
- /**
- * Wrapper around StateAnimationConfig that doesn't allow interpolators to be set if they are
- * already set. This ensures they aren't overridden before being used.
- */
- private static class UseFirstInterpolatorStateAnimConfig extends StateAnimationConfig {
- @Override
- public void setInterpolator(int animId, Interpolator interpolator) {
- if (mInterpolators[animId] == null || interpolator == null) {
- super.setInterpolator(animId, interpolator);
- }
- }
- }
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index ba70bf7..c1ca060 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -15,11 +15,13 @@
*/
package com.android.quickstep.util;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
import android.animation.Animator;
import android.animation.ObjectAnimator;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -29,7 +31,7 @@
extends AtomicAnimationFactory<STATE_TYPE> {
public static final int INDEX_RECENTS_FADE_ANIM = AtomicAnimationFactory.NEXT_INDEX + 0;
- public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
+ public static final int INDEX_RECENTS_TRANSLATE_Y_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
private static final int MY_ANIM_COUNT = 2;
@@ -46,14 +48,14 @@
case INDEX_RECENTS_FADE_ANIM:
return ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
- case INDEX_RECENTS_TRANSLATE_X_ANIM: {
+ case INDEX_RECENTS_TRANSLATE_Y_ANIM: {
RecentsView rv = mActivity.getOverviewPanel();
return new SpringAnimationBuilder(mActivity)
- .setMinimumVisibleChange(1f / rv.getPageOffsetScale())
+ .setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE)
.setDampingRatio(0.8f)
.setStiffness(250)
.setValues(values)
- .build(rv, ADJACENT_PAGE_OFFSET);
+ .build(rv, ADJACENT_PAGE_VERTICAL_OFFSET);
}
default:
return super.createStateElementAnimation(index, values);
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 01d51f8..3d33e57 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -25,6 +25,7 @@
import android.animation.AnimatorSet;
import android.app.ActivityOptions;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -64,6 +65,7 @@
private final SystemUiProxy mSystemUiProxy;
private TaskView mInitialTaskView;
private SplitPositionOption mInitialPosition;
+ private Rect mInitialBounds;
private final Handler mHandler;
public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
@@ -74,9 +76,11 @@
/**
* To be called after first task selected
*/
- public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption) {
+ public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+ Rect initialBounds) {
mInitialTaskView = taskView;
mInitialPosition = positionOption;
+ mInitialBounds = initialBounds;
}
/**
@@ -220,9 +224,14 @@
public void resetState() {
mInitialTaskView = null;
mInitialPosition = null;
+ mInitialBounds = null;
}
public boolean isSplitSelectActive() {
return mInitialTaskView != null;
}
+
+ public Rect getInitialBounds() {
+ return mInitialBounds;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index e63f8bb..f578ad1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -36,6 +36,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
@@ -95,7 +96,9 @@
mContext = context;
mSizeStrategy = sizeStrategy;
- mOrientationState = new RecentsOrientedState(context, sizeStrategy, i -> { });
+ // TODO(b/187074722): Don't create this per-TaskViewSimulator
+ mOrientationState = TraceHelper.allowIpcs("",
+ () -> new RecentsOrientedState(context, sizeStrategy, i -> { }));
mOrientationState.setGestureActive(true);
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mOrientationStateId = mOrientationState.getStateId();
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
index f74aa55..9ea2369 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -100,8 +100,8 @@
/** Restores the drawables to the source view. */
void finish() {
if (isUninitialized()) return;
- mSourceView.setForeground(mOriginalForeground);
- mSourceView.setBackground(mOriginalBackground);
+ if (mOriginalForeground != null) mSourceView.setForeground(mOriginalForeground);
+ if (mOriginalBackground != null) mSourceView.setBackground(mOriginalBackground);
}
void recycle() {
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 8499902..ed54f10 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -27,6 +27,7 @@
import android.view.GhostView;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.FrameLayout;
import com.android.launcher3.Launcher;
@@ -39,7 +40,8 @@
/** A view that mimics an App Widget through a launch animation. */
@TargetApi(Build.VERSION_CODES.S)
-public class FloatingWidgetView extends FrameLayout implements AnimatorListener {
+public class FloatingWidgetView extends FrameLayout implements AnimatorListener,
+ OnGlobalLayoutListener {
private static final Matrix sTmpMatrix = new Matrix();
private final Launcher mLauncher;
@@ -54,6 +56,7 @@
private Runnable mEndRunnable;
private Runnable mFastFinishRunnable;
+ private Runnable mOnTargetChangeRunnable;
public FloatingWidgetView(Context context) {
this(context, null);
@@ -93,6 +96,32 @@
public void onAnimationRepeat(Animator animator) {
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnGlobalLayoutListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onGlobalLayout() {
+ if (isUninitialized()) return;
+ positionViews();
+ if (mOnTargetChangeRunnable != null) {
+ mOnTargetChangeRunnable.run();
+ }
+ }
+
+ /** Sets a runnable that is called on global layout change. */
+ public void setOnTargetChangeListener(Runnable onTargetChangeListener) {
+ mOnTargetChangeRunnable = onTargetChangeListener;
+ }
+
/** Sets a runnable that is called after a call to {@link #fastFinish()}. */
public void setFastFinishRunnable(Runnable runnable) {
mFastFinishRunnable = runnable;
@@ -205,6 +234,7 @@
private void recycle() {
mEndRunnable = null;
mFastFinishRunnable = null;
+ mOnTargetChangeRunnable = null;
mBackgroundPosition = null;
mListenerView.setListener(null);
mAppWidgetView = null;
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index f5a8ff8..65956d5 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import android.annotation.TargetApi;
import android.content.Context;
@@ -39,7 +38,6 @@
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
@@ -90,15 +88,7 @@
@Override
public void startHome() {
- Runnable onReachedHome = () -> mActivity.getStateManager().goToState(NORMAL, false);
- OverviewToHomeAnim overviewToHomeAnim = new OverviewToHomeAnim(mActivity, onReachedHome);
- if (LIVE_TILE.get()) {
- switchToScreenshot(null,
- () -> finishRecentsAnimation(true /* toRecents */,
- () -> overviewToHomeAnim.animateWithVelocity(0)));
- } else {
- overviewToHomeAnim.animateWithVelocity(0);
- }
+ mActivity.getStateManager().goToState(NORMAL);
}
@Override
@@ -245,8 +235,8 @@
if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
// We want to keep the tasks translations in this temporary state
// after resetting the rest above
- setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation);
- setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation);
+ setTaskViewsPrimarySplitTranslation(mTaskViewsPrimarySplitTranslation);
+ setTaskViewsSecondarySplitTranslation(mTaskViewsSecondarySplitTranslation);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c9e7a73..a04b886 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -23,7 +23,6 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
@@ -171,8 +170,7 @@
public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
- InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
- SplitScreenBounds.OnChangeListener {
+ TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
public static final FloatProperty<RecentsView> CONTENT_ALPHA =
new FloatProperty<RecentsView>("contentAlpha") {
@@ -213,19 +211,35 @@
}
};
- public static final FloatProperty<RecentsView> ADJACENT_PAGE_OFFSET =
- new FloatProperty<RecentsView>("adjacentPageOffset") {
+ public static final FloatProperty<RecentsView> ADJACENT_PAGE_HORIZONTAL_OFFSET =
+ new FloatProperty<RecentsView>("adjacentPageHorizontalOffset") {
@Override
public void setValue(RecentsView recentsView, float v) {
- if (recentsView.mAdjacentPageOffset != v) {
- recentsView.mAdjacentPageOffset = v;
+ if (recentsView.mAdjacentPageHorizontalOffset != v) {
+ recentsView.mAdjacentPageHorizontalOffset = v;
recentsView.updatePageOffsets();
}
}
@Override
public Float get(RecentsView recentsView) {
- return recentsView.mAdjacentPageOffset;
+ return recentsView.mAdjacentPageHorizontalOffset;
+ }
+ };
+
+ public static final FloatProperty<RecentsView> ADJACENT_PAGE_VERTICAL_OFFSET =
+ new FloatProperty<RecentsView>("adjacentPageVerticalOffset") {
+ @Override
+ public void setValue(RecentsView recentsView, float v) {
+ if (recentsView.mAdjacentPageVerticalOffset != v) {
+ recentsView.mAdjacentPageVerticalOffset = v;
+ recentsView.updateVerticalPageOffsets();
+ }
+ }
+
+ @Override
+ public Float get(RecentsView recentsView) {
+ return recentsView.mAdjacentPageVerticalOffset;
}
};
@@ -240,6 +254,8 @@
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskViewsResistanceTranslation(v);
+ recentsView.mLastComputedTaskBottomPushOutDistance = null;
+ recentsView.updateVerticalPageOffsets();
}
@Override
@@ -254,16 +270,29 @@
* more specific, we'd want to create a similar FloatProperty just for a TaskView's
* offsetX/Y property
*/
- public static final FloatProperty<RecentsView> TASK_PRIMARY_TRANSLATION =
- new FloatProperty<RecentsView>("taskPrimaryTranslation") {
+ public static final FloatProperty<RecentsView> TASK_PRIMARY_SPLIT_TRANSLATION =
+ new FloatProperty<RecentsView>("taskPrimarySplitTranslation") {
@Override
public void setValue(RecentsView recentsView, float v) {
- recentsView.setTaskViewsPrimaryTranslation(v);
+ recentsView.setTaskViewsPrimarySplitTranslation(v);
}
@Override
public Float get(RecentsView recentsView) {
- return recentsView.mTaskViewsPrimaryTranslation;
+ return recentsView.mTaskViewsPrimarySplitTranslation;
+ }
+ };
+
+ public static final FloatProperty<RecentsView> TASK_SECONDARY_SPLIT_TRANSLATION =
+ new FloatProperty<RecentsView>("taskSecondarySplitTranslation") {
+ @Override
+ public void setValue(RecentsView recentsView, float v) {
+ recentsView.setTaskViewsSecondarySplitTranslation(v);
+ }
+
+ @Override
+ public Float get(RecentsView recentsView) {
+ return recentsView.mTaskViewsSecondarySplitTranslation;
}
};
@@ -276,10 +305,11 @@
view.setScaleY(scale);
view.mLastComputedTaskStartPushOutDistance = null;
view.mLastComputedTaskEndPushOutDistance = null;
+ view.mLastComputedTaskBottomPushOutDistance = null;
view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
- view.updatePageOffsets();
view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
- view.setTaskViewsPrimaryTranslation(view.mTaskViewsPrimaryTranslation);
+ view.updatePageOffsets();
+ view.updateVerticalPageOffsets();
}
@Override
@@ -318,6 +348,7 @@
// How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
protected Float mLastComputedTaskStartPushOutDistance = null;
protected Float mLastComputedTaskEndPushOutDistance = null;
+ protected Float mLastComputedTaskBottomPushOutDistance = null;
protected boolean mEnableDrawingLiveTile = false;
protected final Rect mTempRect = new Rect();
protected final RectF mTempRectF = new RectF();
@@ -361,9 +392,11 @@
private boolean mOverviewGridEnabled;
private boolean mOverviewFullscreenEnabled;
- private float mAdjacentPageOffset = 0;
+ private float mAdjacentPageHorizontalOffset = 0;
+ private float mAdjacentPageVerticalOffset = 0;
protected float mTaskViewsSecondaryTranslation = 0;
- protected float mTaskViewsPrimaryTranslation = 0;
+ protected float mTaskViewsPrimarySplitTranslation = 0;
+ protected float mTaskViewsSecondarySplitTranslation = 0;
// Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
private float mGridProgress = 0;
private final IntSet mTopRowIdSet = new IntSet();
@@ -609,28 +642,9 @@
// Draw overscroll
if (mAllowOverScroll && (!mEdgeGlowRight.isFinished() || !mEdgeGlowLeft.isFinished())) {
final int restoreCount = canvas.save();
- final int width = getWidth();
- final int height = getHeight();
- int primarySize = mOrientationHandler.getPrimaryValue(width, height);
- int secondarySize = mOrientationHandler.getSecondaryValue(width, height);
- float effectiveShift = 0;
- if (!mEdgeGlowLeft.isFinished()) {
- mEdgeGlowLeft.setSize(secondarySize, primarySize);
- if (((TranslateEdgeEffect) mEdgeGlowLeft).getTranslationShift(mTempFloat)) {
- effectiveShift = mTempFloat[0];
- postInvalidateOnAnimation();
- }
- }
- if (!mEdgeGlowRight.isFinished()) {
- mEdgeGlowRight.setSize(secondarySize, primarySize);
- if (((TranslateEdgeEffect) mEdgeGlowRight).getTranslationShift(mTempFloat)) {
- effectiveShift -= mTempFloat[0];
- postInvalidateOnAnimation();
- }
- }
-
- int scroll = OverScroll.dampedScroll(effectiveShift * primarySize, primarySize);
+ int primarySize = mOrientationHandler.getPrimaryValue(getWidth(), getHeight());
+ int scroll = OverScroll.dampedScroll(getUndampedOverScrollShift(), primarySize);
mOrientationHandler.set(canvas, CANVAS_TRANSLATE, scroll);
if (mOverScrollShift != scroll) {
@@ -652,6 +666,31 @@
}
}
+ private float getUndampedOverScrollShift() {
+ final int width = getWidth();
+ final int height = getHeight();
+ int primarySize = mOrientationHandler.getPrimaryValue(width, height);
+ int secondarySize = mOrientationHandler.getSecondaryValue(width, height);
+
+ float effectiveShift = 0;
+ if (!mEdgeGlowLeft.isFinished()) {
+ mEdgeGlowLeft.setSize(secondarySize, primarySize);
+ if (((TranslateEdgeEffect) mEdgeGlowLeft).getTranslationShift(mTempFloat)) {
+ effectiveShift = mTempFloat[0];
+ postInvalidateOnAnimation();
+ }
+ }
+ if (!mEdgeGlowRight.isFinished()) {
+ mEdgeGlowRight.setSize(secondarySize, primarySize);
+ if (((TranslateEdgeEffect) mEdgeGlowRight).getTranslationShift(mTempFloat)) {
+ effectiveShift -= mTempFloat[0];
+ postInvalidateOnAnimation();
+ }
+ }
+
+ return effectiveShift * primarySize;
+ }
+
/**
* Returns the view shift due to overscroll
*/
@@ -705,16 +744,6 @@
updateTaskStackListenerState();
}
- @Override
- public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
- if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) == 0) {
- return;
- }
- mModel.getIconCache().clear();
- unloadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
- loadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
- }
-
public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
mActionsView = actionsView;
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
@@ -739,7 +768,6 @@
mSyncTransactionApplier = new SurfaceTransactionApplier(this);
mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
- mIdp.addOnChangeListener(this);
mIPipAnimationListener.setActivity(mActivity);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
mIPipAnimationListener);
@@ -758,7 +786,6 @@
mSyncTransactionApplier = null;
mLiveTileParams.setSyncTransactionApplier(null);
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
- mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
mIPipAnimationListener.setActivity(null);
@@ -1175,6 +1202,7 @@
// Update the set of visible task's data
loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setTaskModalness(0);
+ updateVerticalPageOffsets();
}
public void setFullscreenProgress(float fullscreenProgress) {
@@ -1631,13 +1659,21 @@
/**
* Called when a gesture from an app has finished, and an end target has been determined.
*/
- public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+ public void onPrepareGestureEndAnimation(
+ @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+ if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
+ .displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
+ if (animatorSet == null) {
+ setGridProgress(1);
+ } else {
+ animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+ }
+ }
mCurrentGestureEndTarget = endTarget;
if (endTarget == GestureState.GestureEndTarget.NEW_TASK
|| endTarget == GestureState.GestureEndTarget.LAST_TASK) {
// When switching to tasks in quick switch, ensures the snapped page's scroll maintain
- // invariant between quick switch and overview grid, to ensure a smooth animation
- // transition.
+ // invariant between quick switch and overview, to ensure a smooth animation transition.
updateGridProperties();
}
}
@@ -1839,15 +1875,28 @@
final int boxLength = Math.max(mLastComputedGridTaskSize.width(),
mLastComputedGridTaskSize.height());
int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
- float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
- float taskGridVerticalDiff = mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+
+ /*
+ * taskGridVerticalDiff is used to position the top of a task in the top row of the grid
+ * heightOffset is the vertical space one grid task takes + space between top and
+ * bottom row
+ * Summed together they provide the top position for bottom row of grid tasks
+ */
+ final float taskGridVerticalDiff =
+ mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+ final float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
int topRowWidth = 0;
int bottomRowWidth = 0;
float topAccumulatedTranslationX = 0;
float bottomAccumulatedTranslationX = 0;
+
+ // Contains whether the child index is in top or bottom of grid (for non-focused task)
+ // Different from mTopRowIdSet, which contains the taskId of what task is in top row
IntSet topSet = new IntSet();
IntSet bottomSet = new IntSet();
+
+ // Horizontal grid translation for each task
float[] gridTranslations = new float[taskCount];
int focusedTaskIndex = Integer.MAX_VALUE;
@@ -1901,7 +1950,6 @@
boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId)
: topRowWidth <= bottomRowWidth;
if (isTopRow) {
- gridTranslations[i] += topAccumulatedTranslationX;
topRowWidth += taskWidthAndSpacing;
topSet.add(i);
mTopRowIdSet.add(taskId);
@@ -1917,11 +1965,10 @@
widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
}
- float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
- gridTranslations[i] += gridTranslationX;
- topAccumulatedTranslationX += gridTranslationX;
+ float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+ gridTranslations[i] += topAccumulatedTranslationX + currentTaskTranslationX;
+ topAccumulatedTranslationX += currentTaskTranslationX;
} else {
- gridTranslations[i] += bottomAccumulatedTranslationX;
bottomRowWidth += taskWidthAndSpacing;
bottomSet.add(i);
@@ -1937,9 +1984,9 @@
widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
}
- float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
- gridTranslations[i] += gridTranslationX;
- bottomAccumulatedTranslationX += gridTranslationX;
+ float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+ gridTranslations[i] += bottomAccumulatedTranslationX + currentTaskTranslationX;
+ bottomAccumulatedTranslationX += currentTaskTranslationX;
}
if (taskView == snappedTaskView) {
snappedTaskRowWidth = isTopRow ? topRowWidth : bottomRowWidth;
@@ -2130,18 +2177,43 @@
// 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
anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
- FloatProperty<TaskView> secondaryViewTranslate =
- taskView.getSecondaryDissmissTranslationProperty();
- int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
- int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
+ SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
ResourceProvider rp = DynamicResource.provider(mActivity);
SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
.setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
.setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
+ FloatProperty<TaskView> dismissingTaskViewTranslate =
+ taskView.getSecondaryDissmissTranslationProperty();;
+ // TODO(b/186800707) translate entire grid size distance
+ int translateDistance = mOrientationHandler.getSecondaryDimension(taskView);
+ int positiveNegativeFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
+ if (splitController.isSplitSelectActive()) {
+ // Have the task translate towards whatever side was just pinned
+ int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitController
+ .getActiveSplitPositionOption(), mActivity.getDeviceProfile());
+ switch (dir) {
+ case PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE:
+ dismissingTaskViewTranslate = taskView
+ .getSecondaryDissmissTranslationProperty();
+ positiveNegativeFactor = -1;
+ break;
- anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
- verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
+ case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE:
+ dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
+ positiveNegativeFactor = 1;
+ break;
+
+ case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_NEGATIVE:
+ dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
+ positiveNegativeFactor = -1;
+ break;
+ default:
+ throw new IllegalStateException("Invalid split task translation: " + dir);
+ }
+ }
+ anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
+ positiveNegativeFactor * translateDistance).setDuration(duration), LINEAR, sp);
if (LIVE_TILE.get() && taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
@@ -2206,18 +2278,6 @@
}
}
- // Additional offset for fake landscape, if the pinning happens to the right or
- // left, we need to scroll all the tasks away from the direction of the splaceholder
- // view
- if (isSplitSelectionActive()) {
- int splitPosition = getSplitPlaceholder().getSplitController()
- .getActiveSplitPositionOption().mStagePosition;
- int direction = mOrientationHandler
- .getSplitTranslationDirectionFactor(splitPosition);
- int splitOffset = mOrientationHandler.getSplitAnimationTranslation(
- mSplitPlaceholderView.getHeight(), mActivity.getDeviceProfile());
- offset += direction * splitOffset;
- }
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
FloatProperty translationProperty = child instanceof TaskView
@@ -2249,7 +2309,8 @@
mPendingAnimation.addEndListener(new Consumer<Boolean>() {
@Override
public void accept(Boolean success) {
- if (LIVE_TILE.get() && taskView.isRunningTask() && success) {
+ if (LIVE_TILE.get() && mEnableDrawingLiveTile && taskView.isRunningTask()
+ && success) {
finishRecentsAnimation(true /* toHome */, () -> onEnd(success));
} else {
onEnd(success);
@@ -2347,7 +2408,7 @@
return true;
}
- protected void runDismissAnimation(PendingAnimation pendingAnim) {
+ private void runDismissAnimation(PendingAnimation pendingAnim) {
AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
controller.dispatchOnStart();
controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
@@ -2596,13 +2657,15 @@
setTaskModalness(mTaskModalness);
mLastComputedTaskStartPushOutDistance = null;
mLastComputedTaskEndPushOutDistance = null;
+ mLastComputedTaskBottomPushOutDistance = null;
updatePageOffsets();
+ updateVerticalPageOffsets();
setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
private void updatePageOffsets() {
- float offset = mAdjacentPageOffset;
+ float offset = mAdjacentPageHorizontalOffset;
float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
int count = getChildCount();
@@ -2613,10 +2676,10 @@
float midpointOffsetSize = 0;
float leftOffsetSize = midpoint - 1 >= 0
- ? -getOffsetSize(midpoint - 1, midpoint, offset)
+ ? -getHorizontalOffsetSize(midpoint - 1, midpoint, offset)
: 0;
float rightOffsetSize = midpoint + 1 < count
- ? getOffsetSize(midpoint + 1, midpoint, offset)
+ ? getHorizontalOffsetSize(midpoint + 1, midpoint, offset)
: 0;
boolean showAsGrid = showAsGrid();
@@ -2630,14 +2693,14 @@
// calculation is the task directly next to the focus task in the grid.
int referenceIndex = modalMidpoint == 0 ? 1 : 0;
gridOffsetSize = referenceIndex < count
- ? getOffsetSize(referenceIndex, modalMidpoint, modalOffset)
+ ? getHorizontalOffsetSize(referenceIndex, modalMidpoint, modalOffset)
: 0;
} else {
modalLeftOffsetSize = modalMidpoint - 1 >= 0
- ? getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
+ ? getHorizontalOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
: 0;
modalRightOffsetSize = modalMidpoint + 1 < count
- ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
+ ? getHorizontalOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
: 0;
}
@@ -2688,7 +2751,7 @@
* translating away from the given midpoint.
* @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
*/
- private float getOffsetSize(int childIndex, int midpointIndex, float offsetProgress) {
+ private float getHorizontalOffsetSize(int childIndex, int midpointIndex, float offsetProgress) {
if (offsetProgress == 0) {
// Don't bother calculating everything below if we won't offset anyway.
return 0;
@@ -2750,6 +2813,64 @@
return distanceToOffscreen * offsetProgress;
}
+ private void updateVerticalPageOffsets() {
+ float offset = mAdjacentPageVerticalOffset;
+ int count = getTaskViewCount();
+
+ TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
+ ? null : getTaskView(mRunningTaskId);
+ int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
+
+ float offsetSize = getVerticalOffsetSize(offset);
+ float midpointOffsetSize = 0;
+
+ for (int i = 0; i < count; i++) {
+ float translation = i == midpoint
+ ? midpointOffsetSize
+ : offsetSize;
+ int directionFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor() * -1;
+ translation *= directionFactor;
+ TaskView child = getTaskViewAt(i);
+ FloatProperty translationProperty = child.getSecondaryTaskOffsetTranslationProperty();
+ translationProperty.set(child, translation);
+ if (LIVE_TILE.get() && mEnableDrawingLiveTile && i == getRunningTaskIndex()) {
+ mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = translation;
+ redrawLiveTile();
+ }
+ }
+ }
+
+ /**
+ * Computes the distance to offset the given child such that it is completely offscreen when
+ * translating away from its position in overview.
+ * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
+ */
+ private float getVerticalOffsetSize(float offsetProgress) {
+ if (offsetProgress == 0) {
+ // Don't bother calculating everything below if we won't offset anyway.
+ return 0;
+ }
+ // First, find the distance to offscreen from the normal (centered) task position.
+ mTempRectF.set(mLastComputedTaskSize);
+ RectF taskPosition = mTempRectF;
+ float desiredTop = getHeight();
+ float distanceToOffscreen = desiredTop - taskPosition.top;
+ // Next, we need to account for the resistance translation if any (e.g. long swipe up).
+ float translationY = mTaskViewsSecondaryTranslation;
+ distanceToOffscreen -= translationY;
+ // Finally, we need to account for RecentsView scale, because it moves tasks based on its
+ // pivot. To do this, we move the task position to where it would be offscreen at scale = 1
+ // (computed above), then we apply the scale via getMatrix() to determine how much that
+ // moves the task from its desired position, and adjust the computed distance accordingly.
+ if (mLastComputedTaskBottomPushOutDistance == null) {
+ taskPosition.offsetTo(0, desiredTop + translationY);
+ getMatrix().mapRect(taskPosition);
+ mLastComputedTaskBottomPushOutDistance = (taskPosition.top - desiredTop) / getScaleY();
+ }
+ distanceToOffscreen -= mLastComputedTaskBottomPushOutDistance;
+ return distanceToOffscreen * offsetProgress;
+ }
+
protected void setTaskViewsResistanceTranslation(float translation) {
mTaskViewsSecondaryTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
@@ -2759,20 +2880,20 @@
mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
}
- protected void setTaskViewsPrimaryTranslation(float translation) {
- mTaskViewsPrimaryTranslation = translation;
+ protected void setTaskViewsPrimarySplitTranslation(float translation) {
+ mTaskViewsPrimarySplitTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView task = getTaskViewAt(i);
- task.getPrimaryDismissTranslationProperty().set(task, translation / getScaleY());
+ task.getPrimarySplitTranslationProperty().set(task, translation);
}
- mLiveTileTaskViewSimulator.recentsViewPrimaryTranslation.value = translation;
}
- /**
- * TODO: Do not assume motion across X axis for adjacent page
- */
- public float getPageOffsetScale() {
- return Math.max(getWidth(), 1);
+ protected void setTaskViewsSecondarySplitTranslation(float translation) {
+ mTaskViewsSecondarySplitTranslation = translation;
+ for (int i = 0; i < getTaskViewCount(); i++) {
+ TaskView task = getTaskViewAt(i);
+ task.getSecondarySplitTranslationProperty().set(task, translation);
+ }
}
/**
@@ -2788,8 +2909,9 @@
public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
mSplitHiddenTaskView = taskView;
SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
- splitController.setInitialTaskSelect(taskView,
- splitPositionOption);
+ Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
+ taskView.getBottom());
+ splitController.setInitialTaskSelect(taskView, splitPositionOption, initialBounds);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
mSplitPlaceholderView.setLayoutParams(
splitController.getLayoutParamsForActivePosition(getResources(),
@@ -2809,7 +2931,10 @@
}
public PendingAnimation cancelSplitSelect(boolean animate) {
- mSplitPlaceholderView.getSplitController().resetState();
+ SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
+ SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
+ Rect initialBounds = splitController.getInitialBounds();
+ splitController.resetState();
int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
PendingAnimation pendingAnim = new PendingAnimation(duration);
if (!animate) {
@@ -2824,8 +2949,6 @@
getPageScrolls(oldScroll, false,
view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
- // x is correct, y is before tasks move up
- int[] locationOnScreen = mSplitHiddenTaskView.getLocationOnScreen();
int[] newScroll = new int[getChildCount()];
getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
@@ -2833,20 +2956,42 @@
for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
View child = getChildAt(i);
if (child == mSplitHiddenTaskView) {
+ TaskView taskView = (TaskView) child;
- int left = newScroll[i] + getPaddingStart();
- int topMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
- int top = -mSplitHiddenTaskView.getHeight() - locationOnScreen[1];
- mSplitHiddenTaskView.layout(left, top,
- left + mSplitHiddenTaskView.getWidth(),
- top + mSplitHiddenTaskView.getHeight());
- pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, TRANSLATION_Y,
- -top + mSplitPlaceholderView.getHeight() - topMargin));
+ int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitOption,
+ mActivity.getDeviceProfile());
+ FloatProperty<TaskView> dismissingTaskViewTranslate;
+ Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
+ taskView.getRight(), taskView.getBottom());
+ int distanceDelta = 0;
+ if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) {
+ dismissingTaskViewTranslate = taskView
+ .getSecondaryDissmissTranslationProperty();
+ distanceDelta = initialBounds.top - hiddenBounds.top;
+ taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right,
+ hiddenBounds.bottom);
+ } else {
+ dismissingTaskViewTranslate = taskView
+ .getPrimaryDismissTranslationProperty();
+ distanceDelta = initialBounds.left - hiddenBounds.left;
+ taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right,
+ initialBounds.bottom);
+ if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) {
+ distanceDelta *= -1;
+ }
+ }
+ pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView,
+ dismissingTaskViewTranslate,
+ distanceDelta));
pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
} else {
// If insertion is on last index (furthest from clear all), we directly add the view
// else we translate all views to the right of insertion index further right,
// ignore views to left
+ if (showAsGrid()) {
+ // TODO(b/186800707) handle more elegantly for grid
+ continue;
+ }
int scrollDiff = newScroll[i] - oldScroll[i];
if (scrollDiff != 0) {
FloatProperty translationProperty = child instanceof TaskView
@@ -2872,6 +3017,12 @@
pendingAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ // TODO(b/186800707) Figure out how to undo for grid view
+ // Need to handle cases where dismissed task is
+ // * Top Row
+ // * Bottom Row
+ // * Focused Task
+ updateGridProperties();
resetFromSplitSelectionState();
}
});
@@ -2881,13 +3032,16 @@
private void resetFromSplitSelectionState() {
mSplitHiddenTaskView.setTranslationY(0);
- int pageToSnapTo = mCurrentPage;
- if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
- pageToSnapTo += 1;
- } else {
- pageToSnapTo = mSplitHiddenTaskViewIndex;
+ if (!showAsGrid()) {
+ // TODO(b/186800707)
+ int pageToSnapTo = mCurrentPage;
+ if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
+ pageToSnapTo += 1;
+ } else {
+ pageToSnapTo = mSplitHiddenTaskViewIndex;
+ }
+ snapToPageImmediately(pageToSnapTo);
}
- snapToPageImmediately(pageToSnapTo);
onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
resetTaskVisuals();
mSplitHiddenTaskView = null;
@@ -3027,6 +3181,11 @@
return new PendingAnimation(duration);
}
+ // When swiping down from overview to tasks, ensures the snapped page's scroll maintain
+ // invariant between quick switch and overview, to ensure a smooth animation transition.
+ updateGridProperties();
+ updateScrollSynchronously();
+
int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
final boolean[] passedOverviewThreshold = new boolean[] {false};
ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
@@ -3349,8 +3508,16 @@
if (pageIndex == -1) {
return 0;
}
+
+ int overScrollShift = getOverScrollShift();
+ if (mAdjacentPageVerticalOffset > 0) {
+ // Don't dampen the scroll (due to overscroll) if the adjacent tasks are offscreen, so
+ // that the page can move freely given there's no visual indication why it shouldn't.
+ overScrollShift = (int) Utilities.mapRange(mAdjacentPageVerticalOffset, overScrollShift,
+ getUndampedOverScrollShift());
+ }
return getScrollForPage(pageIndex) - mOrientationHandler.getPrimaryScroll(this)
- + getOverScrollShift();
+ + overScrollShift;
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 08f5879..6f3aade 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -172,6 +172,32 @@
}
};
+ private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
+ new FloatProperty<TaskView>("splitSelectTranslationX") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setSplitSelectTranslationX(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mSplitSelectTranslationX;
+ }
+ };
+
+ private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
+ new FloatProperty<TaskView>("splitSelectTranslationY") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setSplitSelectTranslationY(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mSplitSelectTranslationY;
+ }
+ };
+
private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
new FloatProperty<TaskView>("dismissTranslationX") {
@Override
@@ -345,6 +371,9 @@
// The following grid translations scales with mGridProgress.
private float mGridTranslationX;
private float mGridTranslationY;
+ // Used when in SplitScreenSelectState
+ private float mSplitSelectTranslationY;
+ private float mSplitSelectTranslationX;
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
@@ -553,6 +582,7 @@
@Override
public void onAnimationEnd(Animator animator) {
+ recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
mIsClickableAsLiveTile = true;
}
});
@@ -825,8 +855,10 @@
protected void resetViewTransforms() {
// fullscreenTranslation and accumulatedTranslation should not be reset, as
// resetViewTransforms is called during Quickswitch scrolling.
- mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX = 0f;
- mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
+ mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX =
+ mSplitSelectTranslationX = 0f;
+ mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
+ mSplitSelectTranslationY = 0f;
applyTranslationX();
applyTranslationY();
setTranslationZ(0);
@@ -956,6 +988,15 @@
setScaleY(scale);
}
+ private void setSplitSelectTranslationX(float x) {
+ mSplitSelectTranslationX = x;
+ applyTranslationX();
+ }
+
+ private void setSplitSelectTranslationY(float y) {
+ mSplitSelectTranslationY = y;
+ applyTranslationY();
+ }
private void setDismissTranslationX(float x) {
mDismissTranslationX = x;
applyTranslationX();
@@ -1056,12 +1097,12 @@
private void applyTranslationX() {
setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
- + getPersistentTranslationX());
+ + mSplitSelectTranslationX + getPersistentTranslationX());
}
private void applyTranslationY() {
setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
- + getPersistentTranslationY());
+ + mSplitSelectTranslationY + getPersistentTranslationY());
}
/**
@@ -1085,6 +1126,16 @@
+ getGridTrans(mGridTranslationY);
}
+ public FloatProperty<TaskView> getPrimarySplitTranslationProperty() {
+ return getPagedOrientationHandler().getPrimaryValue(
+ SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+ }
+
+ public FloatProperty<TaskView> getSecondarySplitTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+ }
+
public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
return getPagedOrientationHandler().getPrimaryValue(
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
@@ -1100,6 +1151,11 @@
TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
}
+ public FloatProperty<TaskView> getSecondaryTaskOffsetTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
+ }
+
public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
return getPagedOrientationHandler().getSecondaryValue(
TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
index a9d523a..85c6488 100644
--- a/res/layout/widgets_bottom_sheet_content.xml
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -15,10 +15,12 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<View
+ android:id="@+id/collapse_handle"
android:layout_width="48dp"
android:layout_height="2dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="16dp"
+ android:visibility="gone"
android:background="?android:attr/textColorSecondary"/>
<TextView
style="@style/TextHeadline"
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
index bfce01d..ce7a682 100644
--- a/res/layout/widgets_full_sheet_search_and_recommendations.xml
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout
+<com.android.launcher3.widget.picker.SearchAndRecommendationsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
@@ -49,4 +49,4 @@
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
android:layout_marginTop="16dp"
android:visibility="gone"/>
-</LinearLayout>
+</com.android.launcher3.widget.picker.SearchAndRecommendationsView>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 20b4631..c1f3ac5 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -57,6 +57,22 @@
private final View[] mDragHandles = new View[HANDLE_COUNT];
private final List<Rect> mSystemGestureExclusionRects = new ArrayList<>(HANDLE_COUNT);
+ private final OnAttachStateChangeListener mWidgetViewAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ // Do nothing
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ // When the app widget view is detached, we should close the resize frame.
+ // An example is when the dragging starts, the widget view is detached from
+ // CellLayout and then reattached to DragLayout.
+ close(false);
+ }
+ };
+
private LauncherAppWidgetHostView mWidgetView;
private CellLayout mCellLayout;
@@ -177,7 +193,11 @@
private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
DragLayer dragLayer) {
mCellLayout = cellLayout;
+ if (mWidgetView != null) {
+ mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+ }
mWidgetView = widgetView;
+ mWidgetView.addOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
widgetView.getAppWidgetInfo();
mResizeMode = info.resizeMode;
@@ -628,6 +648,9 @@
@Override
protected void handleClose(boolean animate) {
mDragLayer.removeView(this);
+ if (mWidgetView != null) {
+ mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+ }
}
@Override
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index b263d38..7836fa3 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -17,20 +17,16 @@
package com.android.launcher3;
import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import android.annotation.TargetApi;
import android.appwidget.AppWidgetHostView;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -49,7 +45,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
@@ -83,11 +78,6 @@
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
- public static final int CHANGE_FLAG_GRID = 1 << 0;
- public static final int CHANGE_FLAG_ICON_PARAMS = 1 << 1;
-
- public static final String KEY_ICON_PATH_REF = "pref_icon_shape_path";
-
// Constants that affects the interpolation curve between statically defined device profile
// buckets.
private static final float KNEARESTNEIGHBOR = 3;
@@ -96,9 +86,6 @@
// used to offset float not being able to express extremely small weights in extreme cases.
private static final float WEIGHT_EFFICIENT = 100000f;
- private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier(
- "config_icon_mask", "string", "android");
-
/**
* Number of icons per row and column in the workspace.
*/
@@ -111,7 +98,6 @@
public int numFolderRows;
public int numFolderColumns;
public float iconSize;
- public String iconShapePath;
public float landscapeIconSize;
public float landscapeIconTextSize;
public int iconBitmapSize;
@@ -162,7 +148,6 @@
public Rect defaultWidgetPadding;
private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
- private OverlayMonitor mOverlayMonitor;
@VisibleForTesting
public InvariantDeviceProfile() {}
@@ -173,7 +158,6 @@
numFolderRows = p.numFolderRows;
numFolderColumns = p.numFolderColumns;
iconSize = p.iconSize;
- iconShapePath = p.iconShapePath;
landscapeIconSize = p.landscapeIconSize;
iconBitmapSize = p.iconBitmapSize;
iconTextSize = p.iconTextSize;
@@ -193,7 +177,6 @@
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
- mOverlayMonitor = p.mOverlayMonitor;
devicePaddings = p.devicePaddings;
}
@@ -215,7 +198,6 @@
onConfigChanged(displayContext);
}
});
- mOverlayMonitor = new OverlayMonitor(context);
}
/**
@@ -266,17 +248,6 @@
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
}
- /**
- * Retrieve system defined or RRO overriden icon shape.
- */
- private static String getIconShapePath(Context context) {
- if (CONFIG_ICON_MASK_RES_ID == 0) {
- Log.e(TAG, "Icon mask res identifier failed to retrieve.");
- return "";
- }
- return context.getResources().getString(CONFIG_ICON_MASK_RES_ID);
- }
-
private String initGrid(Context context, String gridName) {
Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
// Determine if we have split display
@@ -317,7 +288,6 @@
mExtraAttrs = closestProfile.extraAttrs;
iconSize = displayOption.iconSize;
- iconShapePath = getIconShapePath(context);
landscapeIconSize = displayOption.landscapeIconSize;
iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
iconTextSize = displayOption.iconTextSize;
@@ -391,18 +361,6 @@
mChangeListeners.remove(listener);
}
- public void verifyConfigChangedInBackground(final Context context) {
- String savedIconMaskPath = getDevicePrefs(context).getString(KEY_ICON_PATH_REF, "");
- // Good place to check if grid size changed in themepicker when launcher was dead.
- if (savedIconMaskPath.isEmpty()) {
- getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
- .apply();
- } else if (!savedIconMaskPath.equals(getIconShapePath(context))) {
- getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
- .apply();
- apply(CHANGE_FLAG_ICON_PARAMS);
- }
- }
public void setCurrentGrid(Context context, String gridName) {
Context appContext = context.getApplicationContext();
@@ -414,36 +372,13 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "IDP.onConfigChanged");
}
- // Config changes, what shall we do?
- InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
// Re-init grid
String gridName = getCurrentGridName(context);
initGrid(context, gridName);
- int changeFlags = 0;
- if (numRows != oldProfile.numRows ||
- numColumns != oldProfile.numColumns ||
- numFolderColumns != oldProfile.numFolderColumns ||
- numFolderRows != oldProfile.numFolderRows ||
- numDatabaseHotseatIcons != oldProfile.numDatabaseHotseatIcons) {
- changeFlags |= CHANGE_FLAG_GRID;
- }
-
- if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize ||
- !iconShapePath.equals(oldProfile.iconShapePath)) {
- changeFlags |= CHANGE_FLAG_ICON_PARAMS;
- }
- if (!iconShapePath.equals(oldProfile.iconShapePath)) {
- IconShape.init(context);
- }
-
- apply(changeFlags);
- }
-
- private void apply(int changeFlags) {
for (OnIDPChangeListener listener : mChangeListeners) {
- listener.onIdpChanged(changeFlags, this);
+ listener.onIdpChanged(this);
}
}
@@ -650,7 +585,10 @@
public interface OnIDPChangeListener {
- void onIdpChanged(int changeFlags, InvariantDeviceProfile profile);
+ /**
+ * Called when the device provide changes
+ */
+ void onIdpChanged(InvariantDeviceProfile profile);
}
@@ -809,18 +747,4 @@
return this;
}
}
-
- private class OverlayMonitor extends BroadcastReceiver {
-
- private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
-
- OverlayMonitor(Context context) {
- context.registerReceiver(this, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- onConfigChanged(context);
- }
- }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 09c7b7a..deb1147 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -565,11 +565,7 @@
}
@Override
- public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
- onIdpChanged(idp);
- }
-
- private void onIdpChanged(InvariantDeviceProfile idp) {
+ public void onIdpChanged(InvariantDeviceProfile idp) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "onIdpChanged");
}
@@ -2780,7 +2776,7 @@
* @see LauncherState#getOverviewScaleAndOffset(Launcher)
*/
public float[] getNormalOverviewScaleAndOffset() {
- return new float[] {NO_SCALE, NO_OFFSET};
+ return new float[] {NO_SCALE, NO_OFFSET, NO_OFFSET};
}
/**
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 11585f9..834b5a7 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,7 +16,8 @@
package com.android.launcher3;
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_THEMED_ICONS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
@@ -24,12 +25,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
-import android.os.Handler;
+import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
@@ -39,6 +41,7 @@
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -47,6 +50,7 @@
public class LauncherAppState {
public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
+ private static final String KEY_ICON_STATE = "pref_icon_shape_path";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
@@ -54,16 +58,11 @@
private final Context mContext;
private final LauncherModel mModel;
+ private final IconProvider mIconProvider;
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
- private SettingsCache.OnChangeListener mNotificationSettingsChangedListener;
-
- private SettingsCache mSettingsCache;
- private InstallSessionTracker mInstallSessionTracker;
- private SimpleBroadcastReceiver mModelChangeReceiver;
- private SafeCloseable mCalendarChangeTracker;
- private SafeCloseable mUserChangeListener;
+ private final RunnableList mOnTerminateCallback = new RunnableList();
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
@@ -80,40 +79,47 @@
public LauncherAppState(Context context) {
this(context, LauncherFiles.APP_ICONS_DB);
- mModelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
+ mInvariantDeviceProfile.addOnChangeListener(idp -> refreshAndReloadLauncher());
mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
- mModelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
+
+ SimpleBroadcastReceiver modelChangeReceiver =
+ new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
+ modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
if (FeatureFlags.IS_STUDIO_BUILD) {
- mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
+ modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
-
- mCalendarChangeTracker = IconProvider.registerIconChangeListener(mContext,
- mModel::onAppIconChanged, MODEL_EXECUTOR.getHandler());
+ mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
// TODO: remove listener on terminate
FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
CustomWidgetManager.INSTANCE.get(mContext)
.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
- mUserChangeListener = UserCache.INSTANCE.get(mContext)
+ SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext)
.addUserChangeListener(mModel::forceReload);
+ mOnTerminateCallback.add(userChangeListener::close);
- mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
- new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
+ IconObserver observer = new IconObserver();
+ SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
+ observer, MODEL_EXECUTOR.getHandler());
+ mOnTerminateCallback.add(iconChangeTracker::close);
+ MODEL_EXECUTOR.execute(observer::verifyIconChanged);
- mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context)
- .registerInstallTracker(mModel);
+ InstallSessionTracker installSessionTracker =
+ InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
+ mOnTerminateCallback.add(installSessionTracker::unregister);
// Register an observer to rebind the notification listener when dots are re-enabled.
- mSettingsCache = SettingsCache.INSTANCE.get(mContext);
- mNotificationSettingsChangedListener = this::onNotificationSettingsChanged;
- mSettingsCache.register(NOTIFICATION_BADGING_URI,
- mNotificationSettingsChangedListener);
- onNotificationSettingsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI));
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
+ SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged;
+ settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister);
+ onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
+ mOnTerminateCallback.add(() ->
+ settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationLister));
}
public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
@@ -122,30 +128,25 @@
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
-
- mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
+ mIconProvider = new IconProvider(context, ENABLE_THEMED_ICONS.get());
+ mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
+ iconCacheFileName, mIconProvider);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
}
- protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
+ private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
if (areNotificationDotsEnabled) {
NotificationListener.requestRebind(new ComponentName(
mContext, NotificationListener.class));
}
}
- private void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
- if (changeFlags == 0) {
- return;
- }
-
- if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) != 0) {
- LauncherIcons.clearPool();
- mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize);
- mWidgetCache.refresh();
- }
-
+ private void refreshAndReloadLauncher() {
+ LauncherIcons.clearPool();
+ mIconCache.updateIconParams(
+ mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
+ mWidgetCache.refresh();
mModel.forceReload();
}
@@ -154,25 +155,13 @@
*/
public void onTerminate() {
mModel.destroy();
- if (mModelChangeReceiver != null) {
- mContext.unregisterReceiver(mModelChangeReceiver);
- }
mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
- if (mInstallSessionTracker != null) {
- mInstallSessionTracker.unregister();
- }
- if (mCalendarChangeTracker != null) {
- mCalendarChangeTracker.close();
- }
- if (mUserChangeListener != null) {
- mUserChangeListener.close();
- }
CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
+ mOnTerminateCallback.executeAllAndDestroy();
+ }
- if (mSettingsCache != null) {
- mSettingsCache.unregister(NOTIFICATION_BADGING_URI,
- mNotificationSettingsChangedListener);
- }
+ public IconProvider getIconProvider() {
+ return mIconProvider;
}
public IconCache getIconCache() {
@@ -197,4 +186,26 @@
public static InvariantDeviceProfile getIDP(Context context) {
return InvariantDeviceProfile.INSTANCE.get(context);
}
+
+ private class IconObserver implements IconProvider.IconChangeListener {
+
+ @Override
+ public void onAppIconChanged(String packageName, UserHandle user) {
+ mModel.onAppIconChanged(packageName, user);
+ }
+
+ @Override
+ public void onSystemIconStateChanged(String iconState) {
+ IconShape.init(mContext);
+ refreshAndReloadLauncher();
+ getDevicePrefs(mContext).edit().putString(KEY_ICON_STATE, iconState).apply();
+ }
+
+ void verifyIconChanged() {
+ String iconState = mIconProvider.getSystemIconState();
+ if (!iconState.equals(getDevicePrefs(mContext).getString(KEY_ICON_STATE, ""))) {
+ onSystemIconStateChanged(iconState);
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4c11725..9d50edd 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -172,10 +172,12 @@
}
/**
- * Returns an array of two elements.
+ * Returns an array of three elements.
* The first specifies the scale for the overview
* The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
* should be shifted horizontally.
+ * The third is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
+ * should be shifted vertically.
*/
public float[] getOverviewScaleAndOffset(Launcher launcher) {
return launcher.getNormalOverviewScaleAndOffset();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 972a6e8..7c00362 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -661,7 +661,7 @@
.resolveActivity(info.getIntent(), info.user);
outObj[0] = activityInfo;
return activityInfo == null ? null : LauncherAppState.getInstance(launcher)
- .getIconCache().getIconProvider().getIcon(
+ .getIconProvider().getIcon(
activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
if (info instanceof PendingAddShortcutInfo) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index a5852ba..dbdfb2b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -32,6 +32,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.keyboard.KeyboardDragAndDropView;
import com.android.launcher3.model.data.AppInfo;
@@ -290,7 +291,12 @@
return actions;
}
- CellLayout layout = (CellLayout) host.getParent().getParent();
+ CellLayout layout;
+ if (host.getParent() instanceof DragView) {
+ layout = (CellLayout) ((DragView) host.getParent()).getContentViewParent().getParent();
+ } else {
+ layout = (CellLayout) host.getParent().getParent();
+ }
if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 119a91f..b11b63e 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -75,7 +75,7 @@
public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
- private static final float FLING_VELOCITY_MULTIPLIER = 1800f;
+ private static final float FLING_VELOCITY_MULTIPLIER = 1000f;
// Starts the springs after at least 25% of the animation has passed.
private static final float FLING_ANIMATION_THRESHOLD = 0.25f;
@@ -611,7 +611,7 @@
public void onAnimationUpdate(ValueAnimator valueAnimator) {
if (shouldSpring
&& valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
- absorbSwipeUpVelocity(-Math.abs(
+ absorbSwipeUpVelocity(Math.abs(
Math.round(velocity * FLING_VELOCITY_MULTIPLIER)));
shouldSpring = false;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c61c0d6..c4c7891 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,13 +15,15 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
@@ -154,9 +156,9 @@
return;
}
- Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
- ? config.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
- : FAST_OUT_SLOW_IN;
+ Interpolator interpolator = toState.equals(ALL_APPS)
+ ? (config.userControlled ? ACCEL_2 : ACCEL_0_75) :
+ (config.userControlled ? DEACCEL_2 : DEACCEL);
Animator anim = createSpringAnimation(mProgress, targetProgress);
anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index bc93a1e..297325a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -81,10 +81,11 @@
private int mPendingIconRequestCount = 0;
public IconCache(Context context, InvariantDeviceProfile idp) {
- this(context, idp, LauncherFiles.APP_ICONS_DB);
+ this(context, idp, LauncherFiles.APP_ICONS_DB, new IconProvider(context));
}
- public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName) {
+ public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName,
+ IconProvider iconProvider) {
super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */);
mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
@@ -93,7 +94,7 @@
mLauncherApps = mContext.getSystemService(LauncherApps.class);
mUserManager = UserCache.INSTANCE.get(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
- mIconProvider = new IconProvider(context, true /* supportsIconTheme */);
+ mIconProvider = iconProvider;
}
@Override
@@ -106,10 +107,6 @@
return mInstantAppResolver.isInstantApp(info);
}
- public IconProvider getIconProvider() {
- return mIconProvider;
- }
-
@Override
public BaseIconFactory getIconFactory() {
return LauncherIcons.obtain(mContext);
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 8fc3977..e820ac4 100644
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -58,7 +58,7 @@
public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) {
try (LauncherIcons li = LauncherIcons.obtain(context)) {
return li.createBadgedIconBitmap(LauncherAppState.getInstance(context)
- .getIconCache().getIconProvider().getIcon(object, li.mFillResIconDpi),
+ .getIconProvider().getIcon(object, li.mFillResIconDpi),
object.getUser(), object.getApplicationInfo().targetSdkVersion);
}
}
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 0754c29..6813b97 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,8 +16,6 @@
package com.android.launcher3.model.data;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_THEMED_ICONS;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -232,7 +230,7 @@
* Returns a FastBitmapDrawable with the icon and context theme applied
*/
public FastBitmapDrawable newIcon(Context context, boolean applyTheme) {
- FastBitmapDrawable drawable = applyTheme && ENABLE_THEMED_ICONS.get()
+ FastBitmapDrawable drawable = applyTheme
? bitmap.newThemedIcon(context) : bitmap.newIcon(context);
drawable.setIsDisabled(isDisabled());
return drawable;
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index b3057d5..0eea92c 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -94,6 +94,11 @@
"SearchActionItemInfo can only have either an Intent or a PendingIntent");
}
mIntent = intent;
+ // bandage fix for just one week
+ if (intent != null && "com.android.server.telecom".equals(intent.getPackage())) {
+ intent.setAction(Intent.ACTION_DIAL);
+ intent.setPackage(null);
+ }
}
public PendingIntent getPendingIntent() {
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 2fd5efc..44e55e1 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -160,6 +160,19 @@
}
@Override
+ public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+ DeviceProfile dp) {
+ // Don't use device profile here because we know we're in fake landscape, only split option
+ // available is top/left
+ if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ // Top (visually left) side
+ return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+ }
+ throw new IllegalStateException("Invalid split stage position: " +
+ splitPosition.mStagePosition);
+ }
+
+ @Override
public int getPrimaryScroll(View view) {
return view.getScrollY();
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 6c2f17e..d8e5a48 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -42,6 +42,10 @@
*/
public interface PagedOrientationHandler {
+ int SPLIT_TRANSLATE_PRIMARY_POSITIVE = 0;
+ int SPLIT_TRANSLATE_PRIMARY_NEGATIVE = 1;
+ int SPLIT_TRANSLATE_SECONDARY_NEGATIVE = 2;
+
PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
@@ -71,6 +75,13 @@
int getSecondaryDimension(View view);
FloatProperty<View> getPrimaryViewTranslate();
FloatProperty<View> getSecondaryViewTranslate();
+
+ /**
+ * @param splitPosition The position where the view to be split will go
+ * @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
+ * dismissal should happen
+ */
+ int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, DeviceProfile dp);
int getPrimaryScroll(View view);
float getPrimaryScale(View view);
int getChildStart(View view);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index f6a6448..d3d77fd 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -156,6 +156,25 @@
}
@Override
+ public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+ DeviceProfile dp) {
+ if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ if (dp.isLandscape) {
+ // Left side
+ return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+ } else {
+ // Top side
+ return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
+ }
+ } else if (splitPosition.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+ // We don't have a bottom option, so should be right
+ return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
+ }
+ throw new IllegalStateException("Invalid split stage position: " +
+ splitPosition.mStagePosition);
+ }
+
+ @Override
public int getPrimaryScroll(View view) {
return view.getScrollX();
}
@@ -277,7 +296,7 @@
} else {
// Phone Portrait, LargeScreen Landscape/Portrait
viewGroup.setOrientation(LinearLayout.HORIZONTAL);
- lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
+ lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
}
lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index be14e01..f5e1234 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -48,7 +48,13 @@
WindowInsets rootInsets = launcher.getRootView().getRootWindowInsets();
boolean isImeShown = rootInsets != null && rootInsets.isVisible(
WindowInsets.Type.ime());
- if (!isImeShown) return;
+ if (isImeShown) {
+ // this call is already asynchronous
+ launcher.getAppsView().getWindowInsetsController().hide(
+ WindowInsets.Type.ime()
+ );
+ }
+ return;
}
Message.obtain(HANDLER.get(launcher), MSG_HIDE_KEYBOARD, token).sendToTarget();
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index f18b63e..995ac47 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -118,6 +118,7 @@
ViewGroup.LayoutParams layoutParams = widgetsTableScrollView.getLayoutParams();
layoutParams.height = mMaxTableHeight;
widgetsTableScrollView.setLayoutParams(layoutParams);
+ findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
}
}
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
index 7f84077..d09cb8a 100644
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget.picker;
+import android.graphics.Point;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.RelativeLayout;
@@ -33,9 +35,11 @@
RecyclerViewFastScroller.OnFastScrollChangeListener {
private final boolean mHasWorkProfile;
private final SearchAndRecommendationViewHolder mViewHolder;
+ private final View mSearchAndRecommendationViewParent;
private final WidgetsRecyclerView mPrimaryRecyclerView;
private final WidgetsRecyclerView mSearchRecyclerView;
private final int mTabsHeight;
+ private final Point mTempOffset = new Point();
// The following are only non null if mHasWorkProfile is true.
@Nullable private final WidgetsRecyclerView mWorkRecyclerView;
@@ -62,6 +66,8 @@
*/
private int mCollapsibleHeightForTabs = 0;
+ private boolean mShouldForwardToRecyclerView = false;
+
SearchAndRecommendationsScrollController(
boolean hasWorkProfile,
int tabsHeight,
@@ -73,6 +79,8 @@
@Nullable PersonalWorkPagedView primaryWorkViewPager) {
mHasWorkProfile = hasWorkProfile;
mViewHolder = viewHolder;
+ mViewHolder.mContainer.setSearchAndRecommendationScrollController(this);
+ mSearchAndRecommendationViewParent = (View) mViewHolder.mContainer.getParent();
mPrimaryRecyclerView = primaryRecyclerView;
mCurrentRecyclerView = mPrimaryRecyclerView;
mWorkRecyclerView = workRecyclerView;
@@ -245,6 +253,43 @@
}
}
+ /**
+ * Returns {@code true} if a touch event should be intercepted by this controller.
+ */
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ calculateMotionEventOffset(mTempOffset);
+ event.offsetLocation(mTempOffset.x, mTempOffset.y);
+ try {
+ mShouldForwardToRecyclerView = mCurrentRecyclerView.onInterceptTouchEvent(event);
+ return mShouldForwardToRecyclerView;
+ } finally {
+ event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
+ }
+ }
+
+ /**
+ * Returns {@code true} if this controller has intercepted and consumed a touch event.
+ */
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mShouldForwardToRecyclerView) {
+ calculateMotionEventOffset(mTempOffset);
+ event.offsetLocation(mTempOffset.x, mTempOffset.y);
+ try {
+ return mCurrentRecyclerView.onTouchEvent(event);
+ } finally {
+ event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
+ }
+ }
+ return false;
+ }
+
+ private void calculateMotionEventOffset(Point p) {
+ p.x = mViewHolder.mContainer.getLeft() - mCurrentRecyclerView.getLeft()
+ - mSearchAndRecommendationViewParent.getLeft();
+ p.y = mViewHolder.mContainer.getTop() - mCurrentRecyclerView.getTop()
+ - mSearchAndRecommendationViewParent.getTop();
+ }
+
/** private the height, in pixel, + the vertical margins of a given view. */
private static int measureHeightWithVerticalMargins(View view) {
if (view.getVisibility() != View.VISIBLE) {
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
new file mode 100644
index 0000000..0d7d2b5
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
@@ -0,0 +1,62 @@
+/*
+ * 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.widget.picker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+/**
+ * A {@link LinearLayout} container for holding search and widgets recommendation.
+ *
+ * <p>This class intercepts touch events and dispatch them to the right view.
+ */
+public class SearchAndRecommendationsView extends LinearLayout {
+ private SearchAndRecommendationsScrollController mController;
+
+ public SearchAndRecommendationsView(Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public SearchAndRecommendationsView(Context context, AttributeSet attrs) {
+ this(context, attrs, /* defStyleAttr= */ 0);
+ }
+
+ public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+ }
+
+ public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public void setSearchAndRecommendationScrollController(
+ SearchAndRecommendationsScrollController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return mController.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mController.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index b4d4856..b1c5ffc 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -709,13 +709,14 @@
}
final class SearchAndRecommendationViewHolder {
- final ViewGroup mContainer;
+ final SearchAndRecommendationsView mContainer;
final View mCollapseHandle;
final WidgetsSearchBar mSearchBar;
final TextView mHeaderTitle;
final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
- SearchAndRecommendationViewHolder(ViewGroup searchAndRecommendationContainer) {
+ SearchAndRecommendationViewHolder(
+ SearchAndRecommendationsView searchAndRecommendationContainer) {
mContainer = searchAndRecommendationContainer;
mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);